/*
* S5P/EXYNOS4 SoC series camera host interface media device driver
*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 2 of the License,
* or (at your option) any later version.
*/
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <media/v4l2-ctrls.h>
#include <media/media-device.h>
#include <media/s5p_fimc.h>
#include "fimc-core.h"
#include "fimc-lite.h"
#include "fimc-mdevice.h"
#include "mipi-csis.h"
static int __fimc_md_set_camclk(struct fimc_md *fmd,
struct fimc_sensor_info *s_info,
bool on);
/**
* fimc_pipeline_prepare - update pipeline information with subdevice pointers
* @fimc: fimc device terminating the pipeline
*
* Caller holds the graph mutex.
*/
static void fimc_pipeline_prepare(struct fimc_pipeline *p,
struct media_entity *me)
{
struct media_pad *pad = &me->pads[0];
struct v4l2_subdev *sd;
int i;
for (i = 0; i < IDX_MAX; i++)
p->subdevs[i] = NULL;
while (1) {
if (!(pad->flags & MEDIA_PAD_FL_SINK))
break;
/* source pad */
pad = media_entity_remote_source(pad);
if (pad == NULL ||
media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
break;
sd = media_entity_to_v4l2_subdev(pad->entity);
switch (sd->grp_id) {
case SENSOR_GROUP_ID:
p->subdevs[IDX_SENSOR] = sd;
break;
case CSIS_GROUP_ID:
p->subdevs[IDX_CSIS] = sd;
break;
case FLITE_GROUP_ID:
p->subdevs[IDX_FLITE] = sd;
break;
case FIMC_GROUP_ID:
/* No need to control FIMC subdev through subdev ops */
break;
default:
pr_warn("%s: Unknown subdev grp_id: %#x\n",
__func__, sd->grp_id);
}
/* sink pad */
pad = &sd->entity.pads[0];
}
}
/**
* __subdev_set_power - change power state of a single subdev
* @sd: subdevice to change power state for
* @on: 1 to enable power or 0 to disable
*
* Return result of s_power subdev operation or -ENXIO if sd argument
* is NULL. Return 0 if the subdevice does not implement s_power.
*/
static int __subdev_set_power(struct v4l2_subdev *sd, int on)
{
int *use_count;
int ret;
if (sd == NULL)
return -ENXIO;
use_count = &sd->entity.use_count;
if (on && (*use_count)++ > 0)
return 0;
else if (!on && (*use_count == 0 || --(*use_count) > 0))
return 0;
ret = v4l2_subdev_call(sd, core, s_power, on);
return ret != -ENOIOCTLCMD ? ret : 0;
}
/**
* fimc_pipeline_s_power - change power state of all pipeline subdevs
* @fimc: fimc device terminating the pipeline
* @state: true to power on, false to power off
*
* Needs to be called with the graph mutex held.
*/
static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state)
{
unsigned int i;
int ret;
if (p->subdevs[IDX_SENSOR] == NULL)
return -ENXIO;
for (i = 0; i < IDX_MAX; i++) {
unsigned int idx = state ? (IDX_MAX - 1) - i : i;
ret = __subdev_set_power(p->subdevs[idx], state);
if (ret < 0 && ret != -ENXIO)
return ret;
}
return 0;
}
/**
* __fimc_pipeline_open - update the pipeline information, enable power
* of all pipeline subdevs and the sensor clock
* @me: media entity to start graph walk with
* @prep: true to acquire sensor (and csis) subdevs
*
* This function must be called with the graph mutex held.
*/
static int __fimc_pipeline_open(struct fimc_pipeline *p,
struct media_entity *me, bool prep)
{