summaryrefslogtreecommitdiff
path: root/drivers/staging/media
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-06-10 05:30:17 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-06-10 05:30:17 -0300
commitd635711daa98be86d4c7fd01499c34f566b54ccb (patch)
treeaa5cc3760a27c3d57146498cb82fa549547de06c /drivers/staging/media
parentc91265cd0efb83778f015b4d4b1129bd2cfd075e (diff)
Linux-libre 4.6.2-gnu
Diffstat (limited to 'drivers/staging/media')
-rw-r--r--drivers/staging/media/Kconfig8
-rw-r--r--drivers/staging/media/Makefile5
-rw-r--r--drivers/staging/media/bcm2048/radio-bcm2048.c7
-rw-r--r--drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h4
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipe.c11
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c14
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h7
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipeif.c7
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_isif.c24
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_resizer.c20
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c6
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_video.c46
-rw-r--r--drivers/staging/media/lirc/lirc_parallel.c24
-rw-r--r--drivers/staging/media/lirc/lirc_zilog.c4
-rw-r--r--drivers/staging/media/mn88472/mn88472.c12
-rw-r--r--drivers/staging/media/mn88473/Kconfig7
-rw-r--r--drivers/staging/media/mn88473/Makefile5
-rw-r--r--drivers/staging/media/mn88473/TODO21
-rw-r--r--drivers/staging/media/mn88473/mn88473.c522
-rw-r--r--drivers/staging/media/mn88473/mn88473_priv.h37
-rw-r--r--drivers/staging/media/mx2/Kconfig15
-rw-r--r--drivers/staging/media/mx2/Makefile3
-rw-r--r--drivers/staging/media/mx2/TODO10
-rw-r--r--drivers/staging/media/mx2/mx2_camera.c1636
-rw-r--r--drivers/staging/media/mx3/Kconfig15
-rw-r--r--drivers/staging/media/mx3/Makefile3
-rw-r--r--drivers/staging/media/mx3/TODO10
-rw-r--r--drivers/staging/media/mx3/mx3_camera.c1264
-rw-r--r--drivers/staging/media/omap1/Kconfig13
-rw-r--r--drivers/staging/media/omap1/Makefile3
-rw-r--r--drivers/staging/media/omap1/TODO8
-rw-r--r--drivers/staging/media/omap1/omap1_camera.c1738
-rw-r--r--drivers/staging/media/omap4iss/iss.c213
-rw-r--r--drivers/staging/media/omap4iss/iss.h6
-rw-r--r--drivers/staging/media/omap4iss/iss_video.c15
-rw-r--r--drivers/staging/media/omap4iss/iss_video.h1
-rw-r--r--drivers/staging/media/timb/Kconfig11
-rw-r--r--drivers/staging/media/timb/Makefile1
-rw-r--r--drivers/staging/media/timb/timblogiw.c870
39 files changed, 5712 insertions, 914 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index 14697686e..0078b6a92 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -27,10 +27,16 @@ source "drivers/staging/media/davinci_vpfe/Kconfig"
source "drivers/staging/media/mn88472/Kconfig"
-source "drivers/staging/media/mn88473/Kconfig"
+source "drivers/staging/media/mx2/Kconfig"
+
+source "drivers/staging/media/mx3/Kconfig"
+
+source "drivers/staging/media/omap1/Kconfig"
source "drivers/staging/media/omap4iss/Kconfig"
+source "drivers/staging/media/timb/Kconfig"
+
# Keep LIRC at the end, as it has sub-menus
source "drivers/staging/media/lirc/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index 34c557b4c..91495882a 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -2,6 +2,9 @@ obj-$(CONFIG_I2C_BCM2048) += bcm2048/
obj-$(CONFIG_DVB_CXD2099) += cxd2099/
obj-$(CONFIG_LIRC_STAGING) += lirc/
obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/
+obj-$(CONFIG_VIDEO_MX2) += mx2/
+obj-$(CONFIG_VIDEO_MX3) += mx3/
+obj-$(CONFIG_VIDEO_OMAP1) += omap1/
obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
obj-$(CONFIG_DVB_MN88472) += mn88472/
-obj-$(CONFIG_DVB_MN88473) += mn88473/
+obj-$(CONFIG_VIDEO_TIMBERDALE) += timb/
diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c
index 8fdf0ac4f..abf330f92 100644
--- a/drivers/staging/media/bcm2048/radio-bcm2048.c
+++ b/drivers/staging/media/bcm2048/radio-bcm2048.c
@@ -1828,17 +1828,14 @@ static int bcm2048_deinit(struct bcm2048_device *bdev)
err = bcm2048_set_audio_route(bdev, 0);
if (err < 0)
- goto exit;
+ return err;
err = bcm2048_set_dac_output(bdev, 0);
if (err < 0)
- goto exit;
+ return err;
err = bcm2048_set_power_state(bdev, BCM2048_POWER_OFF);
- if (err < 0)
- goto exit;
-exit:
return err;
}
diff --git a/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h
index 7b7e7b26c..f4f35c9ad 100644
--- a/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h
+++ b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h
@@ -538,8 +538,8 @@ struct vpfe_isif_raw_config {
};
/**********************************************************************
- IPIPE API Structures
-**********************************************************************/
+ * IPIPE API Structures
+ **********************************************************************/
/* IPIPE module configurations */
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
index ac78ed2f8..ff47a8f36 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
@@ -1350,21 +1350,16 @@ error:
*/
static long ipipe_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
- int ret = 0;
-
switch (cmd) {
case VIDIOC_VPFE_IPIPE_S_CONFIG:
- ret = ipipe_s_config(sd, arg);
- break;
+ return ipipe_s_config(sd, arg);
case VIDIOC_VPFE_IPIPE_G_CONFIG:
- ret = ipipe_g_config(sd, arg);
- break;
+ return ipipe_g_config(sd, arg);
default:
- ret = -ENOIOCTLCMD;
+ return -ENOIOCTLCMD;
}
- return ret;
}
void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en)
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
index b1d5e23ae..958ef71ee 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
@@ -242,7 +242,7 @@ static int get_ipipe_mode(struct vpfe_ipipe_device *ipipe)
if (ipipeif_sink == IPIPEIF_INPUT_MEMORY)
return IPIPE_MODE_SINGLE_SHOT;
- else if (ipipeif_sink == IPIPEIF_INPUT_ISIF)
+ if (ipipeif_sink == IPIPEIF_INPUT_ISIF)
return IPIPE_MODE_CONTINUOUS;
return -EINVAL;
@@ -682,8 +682,10 @@ ipipe_set_rgb2rgb_regs(void __iomem *base_addr, unsigned int id,
ipipe_clock_enable(base_addr);
if (id == IPIPE_RGB2RGB_2) {
- /* For second RGB module, gain integer is 3 bits instead
- of 4, offset has 11 bits insread of 13 */
+ /*
+ * For second RGB module, gain integer is 3 bits instead
+ * of 4, offset has 11 bits insread of 13
+ */
offset = RGB2_MUL_BASE;
integ_mask = 0x7;
offset_mask = RGB2RGB_2_OFST_MASK;
@@ -792,8 +794,10 @@ ipipe_set_3d_lut_regs(void __iomem *base_addr, void __iomem *isp5_base_addr,
/* valied table */
tbl = lut_3d->table;
for (i = 0; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) {
- /* Each entry has 0-9 (B), 10-19 (G) and
- 20-29 R values */
+ /*
+ * Each entry has 0-9 (B), 10-19 (G) and
+ * 20-29 R values
+ */
val = tbl[i].b & D3_LUT_ENTRY_MASK;
val |= (tbl[i].g & D3_LUT_ENTRY_MASK) <<
D3_LUT_ENTRY_G_SHIFT;
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h
index 2bf2f7a69..7ee157233 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h
@@ -278,9 +278,10 @@
/* Resizer Rescale Parameters */
#define RSZ_EN_A 0x58
#define RSZ_EN_B 0xe8
-/* offset of the registers to be added with base register of
- either RSZ0 or RSZ1
-*/
+/*
+ * offset of the registers to be added with base register of
+ * either RSZ0 or RSZ1
+ */
#define RSZ_MODE 0x4
#define RSZ_420 0x8
#define RSZ_I_VPS 0xc
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
index 633d6456f..46fd2c7f6 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
@@ -94,7 +94,7 @@ static int get_oneshot_mode(enum ipipeif_input_entity input)
{
if (input == IPIPEIF_INPUT_MEMORY)
return IPIPEIF_MODE_ONE_SHOT;
- else if (input == IPIPEIF_INPUT_ISIF)
+ if (input == IPIPEIF_INPUT_ISIF)
return IPIPEIF_MODE_CONTINUOUS;
return -EINVAL;
@@ -641,8 +641,9 @@ ipipeif_try_format(struct vpfe_ipipeif_device *ipipeif,
}
static int
-ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_size_enum *fse)
+ipipeif_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
{
struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt format;
diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c
index 99057892d..ae9202ded 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_isif.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c
@@ -282,7 +282,8 @@ isif_config_format(struct vpfe_device *vpfe_dev, unsigned int pad)
* @fmt: pointer to v4l2 subdev format structure
*/
static void
-isif_try_format(struct vpfe_isif_device *isif, struct v4l2_subdev_pad_config *cfg,
+isif_try_format(struct vpfe_isif_device *isif,
+ struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
unsigned int width = fmt->format.width;
@@ -625,21 +626,16 @@ static int isif_set_params(struct v4l2_subdev *sd, void *params)
*/
static long isif_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
- int ret;
-
switch (cmd) {
case VIDIOC_VPFE_ISIF_S_RAW_PARAMS:
- ret = isif_set_params(sd, arg);
- break;
+ return isif_set_params(sd, arg);
case VIDIOC_VPFE_ISIF_G_RAW_PARAMS:
- ret = isif_get_params(sd, arg);
- break;
+ return isif_get_params(sd, arg);
default:
- ret = -ENOIOCTLCMD;
+ return -ENOIOCTLCMD;
}
- return ret;
}
static void isif_config_gain_offset(struct vpfe_isif_device *isif)
@@ -1239,7 +1235,8 @@ static int isif_config_ycbcr(struct v4l2_subdev *sd, int mode)
* a lot of registers that we didn't touch
*/
/* start with all bits zero */
- ccdcfg = modeset = 0;
+ ccdcfg = 0;
+ modeset = 0;
pix_fmt = isif_get_pix_fmt(format->code);
if (pix_fmt < 0) {
pr_debug("Invalid pix_fmt(input mode)\n");
@@ -1398,8 +1395,9 @@ static int isif_set_stream(struct v4l2_subdev *sd, int enable)
* @which: wanted subdev format.
*/
static struct v4l2_mbus_framefmt *
-__isif_get_format(struct vpfe_isif_device *isif, struct v4l2_subdev_pad_config *cfg,
- unsigned int pad, enum v4l2_subdev_format_whence which)
+__isif_get_format(struct vpfe_isif_device *isif,
+ struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+ enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY) {
struct v4l2_subdev_format fmt;
@@ -1570,7 +1568,7 @@ isif_pad_set_selection(struct v4l2_subdev *sd,
sel->r.height = format->height;
}
/* adjust the width to 16 pixel boundary */
- sel->r.width = ((sel->r.width + 15) & ~0xf);
+ sel->r.width = (sel->r.width + 15) & ~0xf;
vpfe_isif->crop = sel->r;
if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
isif_set_image_window(vpfe_isif);
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
index a91395ce9..3cd56cc13 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
@@ -404,7 +404,7 @@ resizer_calculate_down_scale_f_div_param(struct device *dev,
param->f_div.pass[0].src_hsz = upper_h1 + o;
param->f_div.pass[1].o_hsz = h2 - 1;
param->f_div.pass[1].i_hps = 10 + (val1 * two_power);
- param->f_div.pass[1].h_phs = (val - (val1 << 8));
+ param->f_div.pass[1].h_phs = val - (val1 << 8);
param->f_div.pass[1].src_hps = upper_h1 - o;
param->f_div.pass[1].src_hsz = upper_h2 + o;
@@ -425,8 +425,8 @@ resizer_configure_common_in_params(struct vpfe_resizer_device *resizer)
param->rsz_common.hps = param->user_config.hst;
if (vpfe_ipipeif_decimation_enabled(vpfe_dev))
- param->rsz_common.hsz = (((informat->width - 1) *
- IPIPEIF_RSZ_CONST) / vpfe_ipipeif_get_rsz(vpfe_dev));
+ param->rsz_common.hsz = ((informat->width - 1) *
+ IPIPEIF_RSZ_CONST) / vpfe_ipipeif_get_rsz(vpfe_dev);
else
param->rsz_common.hsz = informat->width - 1;
@@ -650,7 +650,7 @@ resizer_calculate_normal_f_div_param(struct device *dev, int input_width,
param->f_div.pass[0].src_hsz = (input_width >> 2) + o;
param->f_div.pass[1].o_hsz = h2 - 1;
param->f_div.pass[1].i_hps = val1;
- param->f_div.pass[1].h_phs = (val - (val1 << 8));
+ param->f_div.pass[1].h_phs = val - (val1 << 8);
param->f_div.pass[1].src_hps = (input_width >> 2) - o;
param->f_div.pass[1].src_hsz = (input_width >> 2) + o;
@@ -1387,8 +1387,9 @@ resizer_try_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
* @fmt: pointer to v4l2 subdev format structure
* return -EINVAL or zero on success
*/
-static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
+static int resizer_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
{
struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
@@ -1447,8 +1448,9 @@ static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_con
* @fmt: pointer to v4l2 subdev format structure
* return -EINVAL or zero on success
*/
-static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
+static int resizer_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
{
struct v4l2_mbus_framefmt *format;
@@ -1670,7 +1672,7 @@ static int resizer_link_setup(struct media_entity *entity,
resizer->crop_resizer.input =
RESIZER_CROP_INPUT_IPIPEIF;
else if (ipipe_source == IPIPE_OUTPUT_RESIZER)
- resizer->crop_resizer.input =
+ resizer->crop_resizer.input =
RESIZER_CROP_INPUT_IPIPE;
else
return -EINVAL;
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
index ec46f366d..bf077f834 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
@@ -442,8 +442,10 @@ static int vpfe_register_entities(struct vpfe_device *vpfe_dev)
/* create links now, starting with external(i2c) entities */
for (i = 0; i < vpfe_dev->num_ext_subdevs; i++)
- /* if entity has no pads (ex: amplifier),
- cant establish link */
+ /*
+ * if entity has no pads (ex: amplifier),
+ * cant establish link
+ */
if (vpfe_dev->sd[i]->entity.num_pads) {
ret = media_create_pad_link(&vpfe_dev->sd[i]->entity,
0, &vpfe_dev->vpfe_isif.subdev.entity,
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
index db49af902..be72a8e5f 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -178,9 +178,10 @@ static int vpfe_update_pipe_state(struct vpfe_video_device *video)
if (ret)
return ret;
- /* Find out if there is any input video
- if yes, it is single shot.
- */
+ /*
+ * Find out if there is any input video
+ * if yes, it is single shot.
+ */
if (pipe->input_num == 0) {
pipe->state = VPFE_PIPELINE_STREAM_CONTINUOUS;
ret = vpfe_update_current_ext_subdev(video);
@@ -460,7 +461,7 @@ void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video)
video->next_frm = list_entry(video->dma_queue.next,
struct vpfe_cap_buffer, list);
- if (VPFE_PIPELINE_STREAM_SINGLESHOT == video->pipe.state)
+ if (video->pipe.state == VPFE_PIPELINE_STREAM_SINGLESHOT)
video->cur_frm = video->next_frm;
list_del(&video->next_frm->list);
@@ -529,10 +530,11 @@ static int vpfe_release(struct file *file)
if (fh->io_allowed) {
if (video->started) {
vpfe_stop_capture(video);
- /* mark pipe state as stopped in vpfe_release(),
- as app might call streamon() after streamoff()
- in which case driver has to start streaming.
- */
+ /*
+ * mark pipe state as stopped in vpfe_release(),
+ * as app might call streamon() after streamoff()
+ * in which case driver has to start streaming.
+ */
video->pipe.state = VPFE_PIPELINE_STREAM_STOPPED;
vb2_streamoff(&video->buffer_queue,
video->buffer_queue.type);
@@ -672,8 +674,10 @@ static int vpfe_enum_fmt(struct file *file, void *priv,
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt\n");
- /* since already subdev pad format is set,
- only one pixel format is available */
+ /*
+ * since already subdev pad format is set,
+ * only one pixel format is available
+ */
if (fmt->index > 0) {
v4l2_err(&vpfe_dev->v4l2_dev, "Invalid index\n");
return -EINVAL;
@@ -1328,8 +1332,8 @@ static int vpfe_reqbufs(struct file *file, void *priv,
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n");
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type &&
- V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) {
+ if (req_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ req_buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT){
v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n");
return -EINVAL;
}
@@ -1390,8 +1394,8 @@ static int vpfe_querybuf(struct file *file, void *priv,
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n");
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type &&
- V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
return -EINVAL;
}
@@ -1417,8 +1421,8 @@ static int vpfe_qbuf(struct file *file, void *priv,
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n");
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type &&
- V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) {
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ p->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
return -EINVAL;
}
@@ -1445,8 +1449,8 @@ static int vpfe_dqbuf(struct file *file, void *priv,
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n");
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type &&
- V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
return -EINVAL;
}
@@ -1478,8 +1482,8 @@ static int vpfe_streamon(struct file *file, void *priv,
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n");
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type &&
- V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
+ if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ buf_type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
return ret;
}
@@ -1495,7 +1499,7 @@ static int vpfe_streamon(struct file *file, void *priv,
return -EIO;
}
/* Validate the pipeline */
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE == buf_type) {
+ if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
ret = vpfe_video_validate_pipeline(pipe);
if (ret < 0)
return ret;
diff --git a/drivers/staging/media/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c
index d009bcb43..68ede6c56 100644
--- a/drivers/staging/media/lirc/lirc_parallel.c
+++ b/drivers/staging/media/lirc/lirc_parallel.c
@@ -193,7 +193,7 @@ static int lirc_claim(void)
return 0;
}
}
- out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+ out(LIRC_LP_CONTROL, LP_PSELECP | LP_PINITP);
is_claimed = 1;
return 1;
}
@@ -264,7 +264,7 @@ static void lirc_lirc_irq_handler(void *blah)
init = 1;
}
- timeout = timer/10; /* timeout after 1/10 sec. */
+ timeout = timer / 10; /* timeout after 1/10 sec. */
signal = 1;
level = lirc_get_timer();
do {
@@ -286,15 +286,15 @@ static void lirc_lirc_irq_handler(void *blah)
/* adjust value to usecs */
__u64 helper;
- helper = ((__u64) signal)*1000000;
+ helper = ((__u64)signal) * 1000000;
do_div(helper, timer);
- signal = (long) helper;
+ signal = (long)helper;
if (signal > LIRC_SFH506_DELAY)
data = signal - LIRC_SFH506_DELAY;
else
data = 1;
- rbuf_write(PULSE_BIT|data); /* pulse */
+ rbuf_write(PULSE_BIT | data); /* pulse */
}
lastkt = ktime_get();
#else
@@ -331,7 +331,7 @@ static ssize_t lirc_read(struct file *filep, char __user *buf, size_t n,
set_current_state(TASK_INTERRUPTIBLE);
while (count < n) {
if (rptr != wptr) {
- if (copy_to_user(buf+count, &rbuf[rptr],
+ if (copy_to_user(buf + count, &rbuf[rptr],
sizeof(int))) {
result = -EFAULT;
break;
@@ -393,9 +393,9 @@ static ssize_t lirc_write(struct file *filep, const char __user *buf, size_t n,
for (i = 0; i < count; i++) {
__u64 helper;
- helper = ((__u64) wbuf[i])*timer;
+ helper = ((__u64)wbuf[i]) * timer;
do_div(helper, 1000000);
- wbuf[i] = (int) helper;
+ wbuf[i] = (int)helper;
}
local_irq_save(flags);
@@ -647,7 +647,7 @@ static int __init lirc_parallel_init(void)
goto exit_device_put;
pport = parport_find_base(io);
- if (pport == NULL) {
+ if (!pport) {
pr_notice("no port at %x found\n", io);
result = -ENXIO;
goto exit_device_put;
@@ -656,7 +656,7 @@ static int __init lirc_parallel_init(void)
pf, kf, lirc_lirc_irq_handler, 0,
NULL);
parport_put_port(pport);
- if (ppdevice == NULL) {
+ if (!ppdevice) {
pr_notice("parport_register_device() failed\n");
result = -ENXIO;
goto exit_device_put;
@@ -664,7 +664,7 @@ static int __init lirc_parallel_init(void)
if (parport_claim(ppdevice) != 0)
goto skip_init;
is_claimed = 1;
- out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+ out(LIRC_LP_CONTROL, LP_PSELECP | LP_PINITP);
#ifdef LIRC_TIMER
if (debug)
@@ -730,7 +730,7 @@ module_param(irq, int, S_IRUGO);
MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
module_param(tx_mask, int, S_IRUGO);
-MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
+MODULE_PARM_DESC(tx_mask, "Transmitter mask (default: 0x01)");
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Enable debugging messages");
diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c
index e0d72062f..b03c089b7 100644
--- a/drivers/staging/media/lirc/lirc_zilog.c
+++ b/drivers/staging/media/lirc/lirc_zilog.c
@@ -1680,9 +1680,7 @@ module_init(zilog_init);
module_exit(zilog_exit);
MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
-MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
- "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, "
- "Andy Walls");
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, Andy Walls");
MODULE_LICENSE("GPL");
/* for compat with old name, which isn't all that accurate anymore */
MODULE_ALIAS("lirc_pvr150");
diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/staging/media/mn88472/mn88472.c
index 0295f5639..c7a76f0fc 100644
--- a/drivers/staging/media/mn88472/mn88472.c
+++ b/drivers/staging/media/mn88472/mn88472.c
@@ -96,9 +96,9 @@ static int mn88472_set_frontend(struct dvb_frontend *fe)
/* Calculate IF registers ( (1<<24)*IF / Xtal ) */
tmp = div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2),
dev->xtal);
- if_val[0] = ((tmp >> 16) & 0xff);
- if_val[1] = ((tmp >> 8) & 0xff);
- if_val[2] = ((tmp >> 0) & 0xff);
+ if_val[0] = (tmp >> 16) & 0xff;
+ if_val[1] = (tmp >> 8) & 0xff;
+ if_val[2] = (tmp >> 0) & 0xff;
ret = regmap_write(dev->regmap[2], 0xfb, 0x13);
ret = regmap_write(dev->regmap[2], 0xef, 0x13);
@@ -456,7 +456,7 @@ static int mn88472_probe(struct i2c_client *client,
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
+ if (!dev) {
ret = -ENOMEM;
goto err;
}
@@ -483,7 +483,7 @@ static int mn88472_probe(struct i2c_client *client,
* 0x1a and 0x1c, in order to get own I2C client for each register page.
*/
dev->client[1] = i2c_new_dummy(client->adapter, 0x1a);
- if (dev->client[1] == NULL) {
+ if (!dev->client[1]) {
ret = -ENODEV;
dev_err(&client->dev, "I2C registration failed\n");
if (ret)
@@ -497,7 +497,7 @@ static int mn88472_probe(struct i2c_client *client,
i2c_set_clientdata(dev->client[1], dev);
dev->client[2] = i2c_new_dummy(client->adapter, 0x1c);
- if (dev->client[2] == NULL) {
+ if (!dev->client[2]) {
ret = -ENODEV;
dev_err(&client->dev, "2nd I2C registration failed\n");
if (ret)
diff --git a/drivers/staging/media/mn88473/Kconfig b/drivers/staging/media/mn88473/Kconfig
deleted file mode 100644
index 6c9ebf51c..000000000
--- a/drivers/staging/media/mn88473/Kconfig
+++ /dev/null
@@ -1,7 +0,0 @@
-config DVB_MN88473
- tristate "Panasonic MN88473"
- depends on DVB_CORE && I2C
- select REGMAP_I2C
- default m if !MEDIA_SUBDRV_AUTOSELECT
- help
- Say Y when you want to support this frontend.
diff --git a/drivers/staging/media/mn88473/Makefile b/drivers/staging/media/mn88473/Makefile
deleted file mode 100644
index fac55410c..000000000
--- a/drivers/staging/media/mn88473/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-obj-$(CONFIG_DVB_MN88473) += mn88473.o
-
-ccflags-y += -Idrivers/media/dvb-core/
-ccflags-y += -Idrivers/media/dvb-frontends/
-ccflags-y += -Idrivers/media/tuners/
diff --git a/drivers/staging/media/mn88473/TODO b/drivers/staging/media/mn88473/TODO
deleted file mode 100644
index b90a14be3..000000000
--- a/drivers/staging/media/mn88473/TODO
+++ /dev/null
@@ -1,21 +0,0 @@
-Driver general quality is not good enough for mainline. Also, other
-device drivers (USB-bridge, tuner) needed for Astrometa receiver in
-question could need some changes. However, if that driver is mainlined
-due to some other device than Astrometa, unrelated TODOs could be
-skipped. In that case rtl28xxu driver needs module parameter to prevent
-driver loading.
-
-Required TODOs:
-* missing lock flags
-* I2C errors
-* tuner sensitivity
-
-*Do not* send any patch fixing checkpatch.pl issues. Currently it passes
-checkpatch.pl tests. I don't want waste my time to review this kind of
-trivial stuff. *Do not* add missing register I/O error checks. Those are
-missing for the reason it is much easier to compare I2C data sniffs when
-there is less lines. Those error checks are about the last thing to be added.
-
-Patches should be submitted to:
-linux-media@vger.kernel.org and Antti Palosaari <crope@iki.fi>
-
diff --git a/drivers/staging/media/mn88473/mn88473.c b/drivers/staging/media/mn88473/mn88473.c
deleted file mode 100644
index 18439db24..000000000
--- a/drivers/staging/media/mn88473/mn88473.c
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * Panasonic MN88473 DVB-T/T2/C demodulator driver
- *
- * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include "mn88473_priv.h"
-
-static int mn88473_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s)
-{
- s->min_delay_ms = 1000;
- return 0;
-}
-
-static int mn88473_set_frontend(struct dvb_frontend *fe)
-{
- struct i2c_client *client = fe->demodulator_priv;
- struct mn88473_dev *dev = i2c_get_clientdata(client);
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
- u32 if_frequency;
- u64 tmp;
- u8 delivery_system_val, if_val[3], bw_val[7];
-
- dev_dbg(&client->dev,
- "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n",
- c->delivery_system,
- c->modulation,
- c->frequency,
- c->bandwidth_hz,
- c->symbol_rate,
- c->inversion,
- c->stream_id);
-
- if (!dev->warm) {
- ret = -EAGAIN;
- goto err;
- }
-
- switch (c->delivery_system) {
- case SYS_DVBT:
- delivery_system_val = 0x02;
- break;
- case SYS_DVBT2:
- delivery_system_val = 0x03;
- break;
- case SYS_DVBC_ANNEX_A:
- delivery_system_val = 0x04;
- break;
- default:
- ret = -EINVAL;
- goto err;
- }
-
- if (c->bandwidth_hz <= 6000000) {
- memcpy(bw_val, "\xe9\x55\x55\x1c\x29\x1c\x29", 7);
- } else if (c->bandwidth_hz <= 7000000) {
- memcpy(bw_val, "\xc8\x00\x00\x17\x0a\x17\x0a", 7);
- } else if (c->bandwidth_hz <= 8000000) {
- memcpy(bw_val, "\xaf\x00\x00\x11\xec\x11\xec", 7);
- } else {
- ret = -EINVAL;
- goto err;
- }
-
- /* program tuner */
- if (fe->ops.tuner_ops.set_params) {
- ret = fe->ops.tuner_ops.set_params(fe);
- if (ret)
- goto err;
- }
-
- if (fe->ops.tuner_ops.get_if_frequency) {
- ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
- if (ret)
- goto err;
-
- dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency);
- } else {
- if_frequency = 0;
- }
-
- /* Calculate IF registers ( (1<<24)*IF / Xtal ) */
- tmp = div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2),
- dev->xtal);
- if_val[0] = ((tmp >> 16) & 0xff);
- if_val[1] = ((tmp >> 8) & 0xff);
- if_val[2] = ((tmp >> 0) & 0xff);
-
- ret = regmap_write(dev->regmap[2], 0x05, 0x00);
- ret = regmap_write(dev->regmap[2], 0xfb, 0x13);
- ret = regmap_write(dev->regmap[2], 0xef, 0x13);
- ret = regmap_write(dev->regmap[2], 0xf9, 0x13);
- ret = regmap_write(dev->regmap[2], 0x00, 0x18);
- ret = regmap_write(dev->regmap[2], 0x01, 0x01);
- ret = regmap_write(dev->regmap[2], 0x02, 0x21);
- ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val);
- ret = regmap_write(dev->regmap[2], 0x0b, 0x00);
-
- for (i = 0; i < sizeof(if_val); i++) {
- ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]);
- if (ret)
- goto err;
- }
-
- for (i = 0; i < sizeof(bw_val); i++) {
- ret = regmap_write(dev->regmap[2], 0x13 + i, bw_val[i]);
- if (ret)
- goto err;
- }
-
- ret = regmap_write(dev->regmap[2], 0x2d, 0x3b);
- ret = regmap_write(dev->regmap[2], 0x2e, 0x00);
- ret = regmap_write(dev->regmap[2], 0x56, 0x0d);
- ret = regmap_write(dev->regmap[0], 0x01, 0xba);
- ret = regmap_write(dev->regmap[0], 0x02, 0x13);
- ret = regmap_write(dev->regmap[0], 0x03, 0x80);
- ret = regmap_write(dev->regmap[0], 0x04, 0xba);
- ret = regmap_write(dev->regmap[0], 0x05, 0x91);
- ret = regmap_write(dev->regmap[0], 0x07, 0xe7);
- ret = regmap_write(dev->regmap[0], 0x08, 0x28);
- ret = regmap_write(dev->regmap[0], 0x0a, 0x1a);
- ret = regmap_write(dev->regmap[0], 0x13, 0x1f);
- ret = regmap_write(dev->regmap[0], 0x19, 0x03);
- ret = regmap_write(dev->regmap[0], 0x1d, 0xb0);
- ret = regmap_write(dev->regmap[0], 0x2a, 0x72);
- ret = regmap_write(dev->regmap[0], 0x2d, 0x00);
- ret = regmap_write(dev->regmap[0], 0x3c, 0x00);
- ret = regmap_write(dev->regmap[0], 0x3f, 0xf8);
- ret = regmap_write(dev->regmap[0], 0x40, 0xf4);
- ret = regmap_write(dev->regmap[0], 0x41, 0x08);
- ret = regmap_write(dev->regmap[0], 0xd2, 0x29);
- ret = regmap_write(dev->regmap[0], 0xd4, 0x55);
- ret = regmap_write(dev->regmap[1], 0x10, 0x10);
- ret = regmap_write(dev->regmap[1], 0x11, 0xab);
- ret = regmap_write(dev->regmap[1], 0x12, 0x0d);
- ret = regmap_write(dev->regmap[1], 0x13, 0xae);
- ret = regmap_write(dev->regmap[1], 0x14, 0x1d);
- ret = regmap_write(dev->regmap[1], 0x15, 0x9d);
- ret = regmap_write(dev->regmap[1], 0xbe, 0x08);
- ret = regmap_write(dev->regmap[2], 0x09, 0x08);
- ret = regmap_write(dev->regmap[2], 0x08, 0x1d);
- ret = regmap_write(dev->regmap[0], 0xb2, 0x37);
- ret = regmap_write(dev->regmap[0], 0xd7, 0x04);
- ret = regmap_write(dev->regmap[2], 0x32, 0x80);
- ret = regmap_write(dev->regmap[2], 0x36, 0x00);
- ret = regmap_write(dev->regmap[2], 0xf8, 0x9f);
- if (ret)
- goto err;
-
- dev->delivery_system = c->delivery_system;
-
- return 0;
-err:
- dev_dbg(&client->dev, "failed=%d\n", ret);
- return ret;
-}
-
-static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status)
-{
- struct i2c_client *client = fe->demodulator_priv;
- struct mn88473_dev *dev = i2c_get_clientdata(client);
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret;
- unsigned int utmp;
- int lock = 0;
-
- *status = 0;
-
- if (!dev->warm) {
- ret = -EAGAIN;
- goto err;
- }
-
- switch (c->delivery_system) {
- case SYS_DVBT:
- ret = regmap_read(dev->regmap[0], 0x62, &utmp);
- if (ret)
- goto err;
- if (!(utmp & 0xA0)) {
- if ((utmp & 0xF) >= 0x03)
- *status |= FE_HAS_SIGNAL;
- if ((utmp & 0xF) >= 0x09)
- lock = 1;
- }
- break;
- case SYS_DVBT2:
- ret = regmap_read(dev->regmap[2], 0x8B, &utmp);
- if (ret)
- goto err;
- if (!(utmp & 0x40)) {
- if ((utmp & 0xF) >= 0x07)
- *status |= FE_HAS_SIGNAL;
- if ((utmp & 0xF) >= 0x0a)
- *status |= FE_HAS_CARRIER;
- if ((utmp & 0xF) >= 0x0d)
- *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- }
- break;
- case SYS_DVBC_ANNEX_A:
- ret = regmap_read(dev->regmap[1], 0x85, &utmp);
- if (ret)
- goto err;
- if (!(utmp & 0x40)) {
- ret = regmap_read(dev->regmap[1], 0x89, &utmp);
- if (ret)
- goto err;
- if (utmp & 0x01)
- lock = 1;
- }
- break;
- default:
- ret = -EINVAL;
- goto err;
- }
-
- if (lock)
- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
- FE_HAS_SYNC | FE_HAS_LOCK;
-
- return 0;
-err:
- dev_dbg(&client->dev, "failed=%d\n", ret);
- return ret;
-}
-
-static int mn88473_init(struct dvb_frontend *fe)
-{
- struct i2c_client *client = fe->demodulator_priv;
- struct mn88473_dev *dev = i2c_get_clientdata(client);
- int ret, len, remaining;
- const struct firmware *fw = NULL;
- u8 *fw_file = MN88473_FIRMWARE;
- unsigned int tmp;
-
- dev_dbg(&client->dev, "\n");
-
- /* set cold state by default */
- dev->warm = false;
-
- /* check if firmware is already running */
- ret = regmap_read(dev->regmap[0], 0xf5, &tmp);
- if (ret)
- goto err;
-
- if (!(tmp & 0x1)) {
- dev_info(&client->dev, "firmware already running\n");
- dev->warm = true;
- return 0;
- }
-
- /* request the firmware, this will block and timeout */
- ret = reject_firmware(&fw, fw_file, &client->dev);
- if (ret) {
- dev_err(&client->dev, "firmare file '%s' not found\n", fw_file);
- goto err_request_firmware;
- }
-
- dev_info(&client->dev, "downloading firmware from file '%s'\n",
- fw_file);
-
- ret = regmap_write(dev->regmap[0], 0xf5, 0x03);
- if (ret)
- goto err;
-
- for (remaining = fw->size; remaining > 0;
- remaining -= (dev->i2c_wr_max - 1)) {
- len = remaining;
- if (len > (dev->i2c_wr_max - 1))
- len = dev->i2c_wr_max - 1;
-
- ret = regmap_bulk_write(dev->regmap[0], 0xf6,
- &fw->data[fw->size - remaining], len);
- if (ret) {
- dev_err(&client->dev, "firmware download failed=%d\n",
- ret);
- goto err;
- }
- }
-
- /* parity check of firmware */
- ret = regmap_read(dev->regmap[0], 0xf8, &tmp);
- if (ret) {
- dev_err(&client->dev,
- "parity reg read failed=%d\n", ret);
- goto err;
- }
- if (tmp & 0x10) {
- dev_err(&client->dev,
- "firmware parity check failed=0x%x\n", tmp);
- goto err;
- }
- dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp);
-
- ret = regmap_write(dev->regmap[0], 0xf5, 0x00);
- if (ret)
- goto err;
-
- release_firmware(fw);
- fw = NULL;
-
- /* warm state */
- dev->warm = true;
-
- return 0;
-
-err:
- release_firmware(fw);
-err_request_firmware:
- dev_dbg(&client->dev, "failed=%d\n", ret);
- return ret;
-}
-
-static int mn88473_sleep(struct dvb_frontend *fe)
-{
- struct i2c_client *client = fe->demodulator_priv;
- struct mn88473_dev *dev = i2c_get_clientdata(client);
- int ret;
-
- dev_dbg(&client->dev, "\n");
-
- ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
- if (ret)
- goto err;
-
- dev->delivery_system = SYS_UNDEFINED;
-
- return 0;
-err:
- dev_dbg(&client->dev, "failed=%d\n", ret);
- return ret;
-}
-
-static struct dvb_frontend_ops mn88473_ops = {
- .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_AC},
- .info = {
- .name = "Panasonic MN88473",
- .symbol_rate_min = 1000000,
- .symbol_rate_max = 7200000,
- .caps = FE_CAN_FEC_1_2 |
- FE_CAN_FEC_2_3 |
- FE_CAN_FEC_3_4 |
- FE_CAN_FEC_5_6 |
- FE_CAN_FEC_7_8 |
- FE_CAN_FEC_AUTO |
- FE_CAN_QPSK |
- FE_CAN_QAM_16 |
- FE_CAN_QAM_32 |
- FE_CAN_QAM_64 |
- FE_CAN_QAM_128 |
- FE_CAN_QAM_256 |
- FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO |
- FE_CAN_MUTE_TS |
- FE_CAN_2G_MODULATION |
- FE_CAN_MULTISTREAM
- },
-
- .get_tune_settings = mn88473_get_tune_settings,
-
- .init = mn88473_init,
- .sleep = mn88473_sleep,
-
- .set_frontend = mn88473_set_frontend,
-
- .read_status = mn88473_read_status,
-};
-
-static int mn88473_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct mn88473_config *config = client->dev.platform_data;
- struct mn88473_dev *dev;
- int ret;
- unsigned int utmp;
- static const struct regmap_config regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- };
-
- dev_dbg(&client->dev, "\n");
-
- /* Caller really need to provide pointer for frontend we create. */
- if (config->fe == NULL) {
- dev_err(&client->dev, "frontend pointer not defined\n");
- ret = -EINVAL;
- goto err;
- }
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- ret = -ENOMEM;
- goto err;
- }
-
- dev->i2c_wr_max = config->i2c_wr_max;
- if (!config->xtal)
- dev->xtal = 25000000;
- else
- dev->xtal = config->xtal;
- dev->client[0] = client;
- dev->regmap[0] = regmap_init_i2c(dev->client[0], &regmap_config);
- if (IS_ERR(dev->regmap[0])) {
- ret = PTR_ERR(dev->regmap[0]);
- goto err_kfree;
- }
-
- /* check demod answers to I2C */
- ret = regmap_read(dev->regmap[0], 0x00, &utmp);
- if (ret)
- goto err_regmap_0_regmap_exit;
-
- /*
- * Chip has three I2C addresses for different register pages. Used
- * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients,
- * 0x1a and 0x1c, in order to get own I2C client for each register page.
- */
- dev->client[1] = i2c_new_dummy(client->adapter, 0x1a);
- if (dev->client[1] == NULL) {
- ret = -ENODEV;
- dev_err(&client->dev, "I2C registration failed\n");
- if (ret)
- goto err_regmap_0_regmap_exit;
- }
- dev->regmap[1] = regmap_init_i2c(dev->client[1], &regmap_config);
- if (IS_ERR(dev->regmap[1])) {
- ret = PTR_ERR(dev->regmap[1]);
- goto err_client_1_i2c_unregister_device;
- }
- i2c_set_clientdata(dev->client[1], dev);
-
- dev->client[2] = i2c_new_dummy(client->adapter, 0x1c);
- if (dev->client[2] == NULL) {
- ret = -ENODEV;
- dev_err(&client->dev, "2nd I2C registration failed\n");
- if (ret)
- goto err_regmap_1_regmap_exit;
- }
- dev->regmap[2] = regmap_init_i2c(dev->client[2], &regmap_config);
- if (IS_ERR(dev->regmap[2])) {
- ret = PTR_ERR(dev->regmap[2]);
- goto err_client_2_i2c_unregister_device;
- }
- i2c_set_clientdata(dev->client[2], dev);
-
- /* create dvb_frontend */
- memcpy(&dev->fe.ops, &mn88473_ops, sizeof(struct dvb_frontend_ops));
- dev->fe.demodulator_priv = client;
- *config->fe = &dev->fe;
- i2c_set_clientdata(client, dev);
-
- dev_info(&dev->client[0]->dev, "Panasonic MN88473 successfully attached\n");
- return 0;
-
-err_client_2_i2c_unregister_device:
- i2c_unregister_device(dev->client[2]);
-err_regmap_1_regmap_exit:
- regmap_exit(dev->regmap[1]);
-err_client_1_i2c_unregister_device:
- i2c_unregister_device(dev->client[1]);
-err_regmap_0_regmap_exit:
- regmap_exit(dev->regmap[0]);
-err_kfree:
- kfree(dev);
-err:
- dev_dbg(&client->dev, "failed=%d\n", ret);
- return ret;
-}
-
-static int mn88473_remove(struct i2c_client *client)
-{
- struct mn88473_dev *dev = i2c_get_clientdata(client);
-
- dev_dbg(&client->dev, "\n");
-
- regmap_exit(dev->regmap[2]);
- i2c_unregister_device(dev->client[2]);
-
- regmap_exit(dev->regmap[1]);
- i2c_unregister_device(dev->client[1]);
-
- regmap_exit(dev->regmap[0]);
-
- kfree(dev);
-
- return 0;
-}
-
-static const struct i2c_device_id mn88473_id_table[] = {
- {"mn88473", 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, mn88473_id_table);
-
-static struct i2c_driver mn88473_driver = {
- .driver = {
- .name = "mn88473",
- },
- .probe = mn88473_probe,
- .remove = mn88473_remove,
- .id_table = mn88473_id_table,
-};
-
-module_i2c_driver(mn88473_driver);
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Panasonic MN88473 DVB-T/T2/C demodulator driver");
-MODULE_LICENSE("GPL");
-/*(DEBLOBBED)*/
diff --git a/drivers/staging/media/mn88473/mn88473_priv.h b/drivers/staging/media/mn88473/mn88473_priv.h
deleted file mode 100644
index 0706d7e60..000000000
--- a/drivers/staging/media/mn88473/mn88473_priv.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Panasonic MN88473 DVB-T/T2/C demodulator driver
- *
- * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef MN88473_PRIV_H
-#define MN88473_PRIV_H
-
-#include "dvb_frontend.h"
-#include "mn88473.h"
-#include <linux/firmware.h>
-#include <linux/regmap.h>
-
-#define MN88473_FIRMWARE "/*(DEBLOBBED)*/"
-
-struct mn88473_dev {
- struct i2c_client *client[3];
- struct regmap *regmap[3];
- struct dvb_frontend fe;
- u16 i2c_wr_max;
- enum fe_delivery_system delivery_system;
- bool warm; /* FW running */
- u32 xtal;
-};
-
-#endif
diff --git a/drivers/staging/media/mx2/Kconfig b/drivers/staging/media/mx2/Kconfig
new file mode 100644
index 000000000..beaa885cf
--- /dev/null
+++ b/drivers/staging/media/mx2/Kconfig
@@ -0,0 +1,15 @@
+config VIDEO_MX2
+ tristate "i.MX27 Camera Sensor Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA
+ depends on SOC_IMX27 || COMPILE_TEST
+ depends on HAS_DMA
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This is a v4l2 driver for the i.MX27 Camera Sensor Interface
+
+ This driver is deprecated: it should become a stand-alone driver
+ instead of using the soc-camera framework.
+
+ Unless someone is willing to take this on (unlikely with such
+ ancient hardware) it is going to be removed from the kernel
+ soon.
diff --git a/drivers/staging/media/mx2/Makefile b/drivers/staging/media/mx2/Makefile
new file mode 100644
index 000000000..fc5b2826a
--- /dev/null
+++ b/drivers/staging/media/mx2/Makefile
@@ -0,0 +1,3 @@
+# Makefile for i.MX27 Camera Sensor driver
+
+obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o
diff --git a/drivers/staging/media/mx2/TODO b/drivers/staging/media/mx2/TODO
new file mode 100644
index 000000000..bc68fa443
--- /dev/null
+++ b/drivers/staging/media/mx2/TODO
@@ -0,0 +1,10 @@
+This driver is deprecated: it should become a stand-alone driver instead of
+using the soc-camera framework.
+
+Unless someone is willing to take this on (unlikely with such ancient
+hardware) it is going to be removed from the kernel soon.
+
+Note that trivial patches will not be accepted anymore, only a full conversion.
+
+If you want to convert this driver, please contact the linux-media mailinglist
+(see http://linuxtv.org/lists.php).
diff --git a/drivers/staging/media/mx2/mx2_camera.c b/drivers/staging/media/mx2/mx2_camera.c
new file mode 100644
index 000000000..48dd5b785
--- /dev/null
+++ b/drivers/staging/media/mx2/mx2_camera.c
@@ -0,0 +1,1636 @@
+/*
+ * V4L2 Driver for i.MX27 camera host
+ *
+ * Copyright (C) 2008, Sascha Hauer, Pengutronix
+ * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography
+ * Copyright (C) 2012, Javier Martin, Vista Silicon S.L.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/gcd.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/soc_camera.h>
+#include <media/drv-intf/soc_mediabus.h>
+
+#include <linux/videodev2.h>
+
+#include <linux/platform_data/media/camera-mx2.h>
+
+#include <asm/dma.h>
+
+#define MX2_CAM_DRV_NAME "mx2-camera"
+#define MX2_CAM_VERSION "0.0.6"
+#define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera"
+
+/* reset values */
+#define CSICR1_RESET_VAL 0x40000800
+#define CSICR2_RESET_VAL 0x0
+#define CSICR3_RESET_VAL 0x0
+
+/* csi control reg 1 */
+#define CSICR1_SWAP16_EN (1 << 31)
+#define CSICR1_EXT_VSYNC (1 << 30)
+#define CSICR1_EOF_INTEN (1 << 29)
+#define CSICR1_PRP_IF_EN (1 << 28)
+#define CSICR1_CCIR_MODE (1 << 27)
+#define CSICR1_COF_INTEN (1 << 26)
+#define CSICR1_SF_OR_INTEN (1 << 25)
+#define CSICR1_RF_OR_INTEN (1 << 24)
+#define CSICR1_STATFF_LEVEL (3 << 22)
+#define CSICR1_STATFF_INTEN (1 << 21)
+#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19)
+#define CSICR1_RXFF_INTEN (1 << 18)
+#define CSICR1_SOF_POL (1 << 17)
+#define CSICR1_SOF_INTEN (1 << 16)
+#define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12)
+#define CSICR1_HSYNC_POL (1 << 11)
+#define CSICR1_CCIR_EN (1 << 10)
+#define CSICR1_MCLKEN (1 << 9)
+#define CSICR1_FCC (1 << 8)
+#define CSICR1_PACK_DIR (1 << 7)
+#define CSICR1_CLR_STATFIFO (1 << 6)
+#define CSICR1_CLR_RXFIFO (1 << 5)
+#define CSICR1_GCLK_MODE (1 << 4)
+#define CSICR1_INV_DATA (1 << 3)
+#define CSICR1_INV_PCLK (1 << 2)
+#define CSICR1_REDGE (1 << 1)
+#define CSICR1_FMT_MASK (CSICR1_PACK_DIR | CSICR1_SWAP16_EN)
+
+#define SHIFT_STATFF_LEVEL 22
+#define SHIFT_RXFF_LEVEL 19
+#define SHIFT_MCLKDIV 12
+
+#define SHIFT_FRMCNT 16
+
+#define CSICR1 0x00
+#define CSICR2 0x04
+#define CSISR 0x08
+#define CSISTATFIFO 0x0c
+#define CSIRFIFO 0x10
+#define CSIRXCNT 0x14
+#define CSICR3 0x1c
+#define CSIDMASA_STATFIFO 0x20
+#define CSIDMATA_STATFIFO 0x24
+#define CSIDMASA_FB1 0x28
+#define CSIDMASA_FB2 0x2c
+#define CSIFBUF_PARA 0x30
+#define CSIIMAG_PARA 0x34
+
+/* EMMA PrP */
+#define PRP_CNTL 0x00
+#define PRP_INTR_CNTL 0x04
+#define PRP_INTRSTATUS 0x08
+#define PRP_SOURCE_Y_PTR 0x0c
+#define PRP_SOURCE_CB_PTR 0x10
+#define PRP_SOURCE_CR_PTR 0x14
+#define PRP_DEST_RGB1_PTR 0x18
+#define PRP_DEST_RGB2_PTR 0x1c
+#define PRP_DEST_Y_PTR 0x20
+#define PRP_DEST_CB_PTR 0x24
+#define PRP_DEST_CR_PTR 0x28
+#define PRP_SRC_FRAME_SIZE 0x2c
+#define PRP_DEST_CH1_LINE_STRIDE 0x30
+#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
+#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
+#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
+#define PRP_CH2_OUT_IMAGE_SIZE 0x40
+#define PRP_SRC_LINE_STRIDE 0x44
+#define PRP_CSC_COEF_012 0x48
+#define PRP_CSC_COEF_345 0x4c
+#define PRP_CSC_COEF_678 0x50
+#define PRP_CH1_RZ_HORI_COEF1 0x54
+#define PRP_CH1_RZ_HORI_COEF2 0x58
+#define PRP_CH1_RZ_HORI_VALID 0x5c
+#define PRP_CH1_RZ_VERT_COEF1 0x60
+#define PRP_CH1_RZ_VERT_COEF2 0x64
+#define PRP_CH1_RZ_VERT_VALID 0x68
+#define PRP_CH2_RZ_HORI_COEF1 0x6c
+#define PRP_CH2_RZ_HORI_COEF2 0x70
+#define PRP_CH2_RZ_HORI_VALID 0x74
+#define PRP_CH2_RZ_VERT_COEF1 0x78
+#define PRP_CH2_RZ_VERT_COEF2 0x7c
+#define PRP_CH2_RZ_VERT_VALID 0x80
+
+#define PRP_CNTL_CH1EN (1 << 0)
+#define PRP_CNTL_CH2EN (1 << 1)
+#define PRP_CNTL_CSIEN (1 << 2)
+#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
+#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
+#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
+#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
+#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
+#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
+#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
+#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
+#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
+#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
+#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
+#define PRP_CNTL_CH1_LEN (1 << 9)
+#define PRP_CNTL_CH2_LEN (1 << 10)
+#define PRP_CNTL_SKIP_FRAME (1 << 11)
+#define PRP_CNTL_SWRST (1 << 12)
+#define PRP_CNTL_CLKEN (1 << 13)
+#define PRP_CNTL_WEN (1 << 14)
+#define PRP_CNTL_CH1BYP (1 << 15)
+#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
+#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
+#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
+#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
+#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
+#define PRP_CNTL_CH2B1EN (1 << 29)
+#define PRP_CNTL_CH2B2EN (1 << 30)
+#define PRP_CNTL_CH2FEN (1 << 31)
+
+/* IRQ Enable and status register */
+#define PRP_INTR_RDERR (1 << 0)
+#define PRP_INTR_CH1WERR (1 << 1)
+#define PRP_INTR_CH2WERR (1 << 2)
+#define PRP_INTR_CH1FC (1 << 3)
+#define PRP_INTR_CH2FC (1 << 5)
+#define PRP_INTR_LBOVF (1 << 7)
+#define PRP_INTR_CH2OVF (1 << 8)
+
+/* Resizing registers */
+#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24)
+#define PRP_RZ_VALID_BILINEAR (1 << 31)
+
+#define MAX_VIDEO_MEM 16
+
+#define RESIZE_NUM_MIN 1
+#define RESIZE_NUM_MAX 20
+#define BC_COEF 3
+#define SZ_COEF (1 << BC_COEF)
+
+#define RESIZE_DIR_H 0
+#define RESIZE_DIR_V 1
+
+#define RESIZE_ALGO_BILINEAR 0
+#define RESIZE_ALGO_AVERAGING 1
+
+struct mx2_prp_cfg {
+ int channel;
+ u32 in_fmt;
+ u32 out_fmt;
+ u32 src_pixel;
+ u32 ch1_pixel;
+ u32 irq_flags;
+ u32 csicr1;
+};
+
+/* prp resizing parameters */
+struct emma_prp_resize {
+ int algo; /* type of algorithm used */
+ int len; /* number of coefficients */
+ unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */
+};
+
+/* prp configuration for a client-host fmt pair */
+struct mx2_fmt_cfg {
+ u32 in_fmt;
+ u32 out_fmt;
+ struct mx2_prp_cfg cfg;
+};
+
+struct mx2_buf_internal {
+ struct list_head queue;
+ int bufnum;
+ bool discard;
+};
+
+/* buffer for one video frame */
+struct mx2_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_v4l2_buffer vb;
+ struct mx2_buf_internal internal;
+};
+
+enum mx2_camera_type {
+ IMX27_CAMERA,
+};
+
+struct mx2_camera_dev {
+ struct device *dev;
+ struct soc_camera_host soc_host;
+ struct clk *clk_emma_ahb, *clk_emma_ipg;
+ struct clk *clk_csi_ahb, *clk_csi_per;
+
+ void __iomem *base_csi, *base_emma;
+
+ struct mx2_camera_platform_data *pdata;
+ unsigned long platform_flags;
+
+ struct list_head capture;
+ struct list_head active_bufs;
+ struct list_head discard;
+
+ spinlock_t lock;
+
+ int dma;
+ struct mx2_buffer *active;
+ struct mx2_buffer *fb1_active;
+ struct mx2_buffer *fb2_active;
+
+ u32 csicr1;
+ enum mx2_camera_type devtype;
+
+ struct mx2_buf_internal buf_discard[2];
+ void *discard_buffer;
+ dma_addr_t discard_buffer_dma;
+ size_t discard_size;
+ struct mx2_fmt_cfg *emma_prp;
+ struct emma_prp_resize resizing[2];
+ unsigned int s_width, s_height;
+ u32 frame_count;
+ struct vb2_alloc_ctx *alloc_ctx;
+};
+
+static struct platform_device_id mx2_camera_devtype[] = {
+ {
+ .name = "imx27-camera",
+ .driver_data = IMX27_CAMERA,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, mx2_camera_devtype);
+
+static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf)
+{
+ return container_of(int_buf, struct mx2_buffer, internal);
+}
+
+static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
+ /*
+ * This is a generic configuration which is valid for most
+ * prp input-output format combinations.
+ * We set the incoming and outgoing pixelformat to a
+ * 16 Bit wide format and adjust the bytesperline
+ * accordingly. With this configuration the inputdata
+ * will not be changed by the emma and could be any type
+ * of 16 Bit Pixelformat.
+ */
+ {
+ .in_fmt = 0,
+ .out_fmt = 0,
+ .cfg = {
+ .channel = 1,
+ .in_fmt = PRP_CNTL_DATA_IN_RGB16,
+ .out_fmt = PRP_CNTL_CH1_OUT_RGB16,
+ .src_pixel = 0x2ca00565, /* RGB565 */
+ .ch1_pixel = 0x2ca00565, /* RGB565 */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+ PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+ .csicr1 = 0,
+ }
+ },
+ {
+ .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUYV,
+ .cfg = {
+ .channel = 1,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH1_OUT_YUV422,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+ PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+ .csicr1 = CSICR1_SWAP16_EN,
+ }
+ },
+ {
+ .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUYV,
+ .cfg = {
+ .channel = 1,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH1_OUT_YUV422,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+ PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+ .csicr1 = CSICR1_PACK_DIR,
+ }
+ },
+ {
+ .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUV420,
+ .cfg = {
+ .channel = 2,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+ PRP_INTR_CH2OVF,
+ .csicr1 = CSICR1_PACK_DIR,
+ }
+ },
+ {
+ .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUV420,
+ .cfg = {
+ .channel = 2,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+ PRP_INTR_CH2OVF,
+ .csicr1 = CSICR1_SWAP16_EN,
+ }
+ },
+};
+
+static struct mx2_fmt_cfg *mx27_emma_prp_get_format(u32 in_fmt, u32 out_fmt)
+{
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(mx27_emma_prp_table); i++)
+ if ((mx27_emma_prp_table[i].in_fmt == in_fmt) &&
+ (mx27_emma_prp_table[i].out_fmt == out_fmt)) {
+ return &mx27_emma_prp_table[i];
+ }
+ /* If no match return the most generic configuration */
+ return &mx27_emma_prp_table[0];
+};
+
+static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
+ unsigned long phys, int bufnum)
+{
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+
+ if (prp->cfg.channel == 1) {
+ writel(phys, pcdev->base_emma +
+ PRP_DEST_RGB1_PTR + 4 * bufnum);
+ } else {
+ writel(phys, pcdev->base_emma +
+ PRP_DEST_Y_PTR - 0x14 * bufnum);
+ if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
+ u32 imgsize = pcdev->soc_host.icd->user_height *
+ pcdev->soc_host.icd->user_width;
+
+ writel(phys + imgsize, pcdev->base_emma +
+ PRP_DEST_CB_PTR - 0x14 * bufnum);
+ writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
+ PRP_DEST_CR_PTR - 0x14 * bufnum);
+ }
+ }
+}
+
+static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
+{
+ clk_disable_unprepare(pcdev->clk_csi_ahb);
+ clk_disable_unprepare(pcdev->clk_csi_per);
+ writel(0, pcdev->base_csi + CSICR1);
+ writel(0, pcdev->base_emma + PRP_CNTL);
+}
+
+static int mx2_camera_add_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void mx2_camera_remove_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on mx2 camera sensor interface
+ */
+static int mx2_camera_clock_start(struct soc_camera_host *ici)
+{
+ struct mx2_camera_dev *pcdev = ici->priv;
+ int ret;
+ u32 csicr1;
+
+ ret = clk_prepare_enable(pcdev->clk_csi_ahb);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(pcdev->clk_csi_per);
+ if (ret < 0)
+ goto exit_csi_ahb;
+
+ csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC |
+ CSICR1_RXFF_LEVEL(0);
+
+ pcdev->csicr1 = csicr1;
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+
+ pcdev->frame_count = 0;
+
+ return 0;
+
+exit_csi_ahb:
+ clk_disable_unprepare(pcdev->clk_csi_ahb);
+
+ return ret;
+}
+
+static void mx2_camera_clock_stop(struct soc_camera_host *ici)
+{
+ struct mx2_camera_dev *pcdev = ici->priv;
+
+ mx2_camera_deactivate(pcdev);
+}
+
+/*
+ * Videobuf operations
+ */
+static int mx2_videobuf_setup(struct vb2_queue *vq,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+
+ dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
+
+ alloc_ctxs[0] = pcdev->alloc_ctx;
+
+ sizes[0] = icd->sizeimage;
+
+ if (0 == *count)
+ *count = 32;
+ if (!*num_planes &&
+ sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
+
+ *num_planes = 1;
+
+ return 0;
+}
+
+static int mx2_videobuf_prepare(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ int ret = 0;
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+#ifdef DEBUG
+ /*
+ * This can be useful if you want to see if we actually fill
+ * the buffer with something
+ */
+ memset((void *)vb2_plane_vaddr(vb, 0),
+ 0xaa, vb2_get_plane_payload(vb, 0));
+#endif
+
+ vb2_set_plane_payload(vb, 0, icd->sizeimage);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static void mx2_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_buffer *buf = container_of(vbuf, struct mx2_buffer, vb);
+ unsigned long flags;
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ list_add_tail(&buf->internal.queue, &pcdev->capture);
+
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
+ int bytesperline)
+{
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+
+ writel((pcdev->s_width << 16) | pcdev->s_height,
+ pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+ writel(prp->cfg.src_pixel,
+ pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
+ if (prp->cfg.channel == 1) {
+ writel((icd->user_width << 16) | icd->user_height,
+ pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
+ writel(bytesperline,
+ pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
+ writel(prp->cfg.ch1_pixel,
+ pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
+ } else { /* channel 2 */
+ writel((icd->user_width << 16) | icd->user_height,
+ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+ }
+
+ /* Enable interrupts */
+ writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
+}
+
+static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev)
+{
+ int dir;
+
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+ unsigned char *s = pcdev->resizing[dir].s;
+ int len = pcdev->resizing[dir].len;
+ unsigned int coeff[2] = {0, 0};
+ unsigned int valid = 0;
+ int i;
+
+ if (len == 0)
+ continue;
+
+ for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) {
+ int j;
+
+ j = i > 9 ? 1 : 0;
+ coeff[j] = (coeff[j] << BC_COEF) |
+ (s[i] & (SZ_COEF - 1));
+
+ if (i == 5 || i == 15)
+ coeff[j] <<= 1;
+
+ valid = (valid << 1) | (s[i] >> BC_COEF);
+ }
+
+ valid |= PRP_RZ_VALID_TBL_LEN(len);
+
+ if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR)
+ valid |= PRP_RZ_VALID_BILINEAR;
+
+ if (pcdev->emma_prp->cfg.channel == 1) {
+ if (dir == RESIZE_DIR_H) {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH1_RZ_HORI_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH1_RZ_HORI_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH1_RZ_HORI_VALID);
+ } else {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH1_RZ_VERT_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH1_RZ_VERT_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH1_RZ_VERT_VALID);
+ }
+ } else {
+ if (dir == RESIZE_DIR_H) {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH2_RZ_HORI_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH2_RZ_HORI_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH2_RZ_HORI_VALID);
+ } else {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH2_RZ_VERT_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH2_RZ_VERT_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH2_RZ_VERT_VALID);
+ }
+ }
+ }
+}
+
+static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ struct vb2_buffer *vb;
+ struct mx2_buffer *buf;
+ unsigned long phys;
+ int bytesperline;
+ unsigned long flags;
+
+ if (count < 2)
+ return -ENOBUFS;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 0;
+ vb = &buf->vb.vb2_buf;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 1;
+ vb = &buf->vb.vb2_buf;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ if (bytesperline < 0) {
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ return bytesperline;
+ }
+
+ /*
+ * I didn't manage to properly enable/disable the prp
+ * on a per frame basis during running transfers,
+ * thus we allocate a buffer here and use it to
+ * discard frames when no buffer is available.
+ * Feel free to work on this ;)
+ */
+ pcdev->discard_size = icd->user_height * bytesperline;
+ pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size,
+ &pcdev->discard_buffer_dma, GFP_ATOMIC);
+ if (!pcdev->discard_buffer) {
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ return -ENOMEM;
+ }
+
+ pcdev->buf_discard[0].discard = true;
+ list_add_tail(&pcdev->buf_discard[0].queue,
+ &pcdev->discard);
+
+ pcdev->buf_discard[1].discard = true;
+ list_add_tail(&pcdev->buf_discard[1].queue,
+ &pcdev->discard);
+
+ mx2_prp_resize_commit(pcdev);
+
+ mx27_camera_emma_buf_init(icd, bytesperline);
+
+ if (prp->cfg.channel == 1) {
+ writel(PRP_CNTL_CH1EN |
+ PRP_CNTL_CSIEN |
+ prp->cfg.in_fmt |
+ prp->cfg.out_fmt |
+ PRP_CNTL_CH1_LEN |
+ PRP_CNTL_CH1BYP |
+ PRP_CNTL_CH1_TSKIP(0) |
+ PRP_CNTL_IN_TSKIP(0),
+ pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(PRP_CNTL_CH2EN |
+ PRP_CNTL_CSIEN |
+ prp->cfg.in_fmt |
+ prp->cfg.out_fmt |
+ PRP_CNTL_CH2_LEN |
+ PRP_CNTL_CH2_TSKIP(0) |
+ PRP_CNTL_IN_TSKIP(0),
+ pcdev->base_emma + PRP_CNTL);
+ }
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+
+ return 0;
+}
+
+static void mx2_stop_streaming(struct vb2_queue *q)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ unsigned long flags;
+ void *b;
+ u32 cntl;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ cntl = readl(pcdev->base_emma + PRP_CNTL);
+ if (prp->cfg.channel == 1) {
+ writel(cntl & ~PRP_CNTL_CH1EN,
+ pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(cntl & ~PRP_CNTL_CH2EN,
+ pcdev->base_emma + PRP_CNTL);
+ }
+ INIT_LIST_HEAD(&pcdev->capture);
+ INIT_LIST_HEAD(&pcdev->active_bufs);
+ INIT_LIST_HEAD(&pcdev->discard);
+
+ b = pcdev->discard_buffer;
+ pcdev->discard_buffer = NULL;
+
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+
+ dma_free_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size, b, pcdev->discard_buffer_dma);
+}
+
+static struct vb2_ops mx2_videobuf_ops = {
+ .queue_setup = mx2_videobuf_setup,
+ .buf_prepare = mx2_videobuf_prepare,
+ .buf_queue = mx2_videobuf_queue,
+ .start_streaming = mx2_start_streaming,
+ .stop_streaming = mx2_stop_streaming,
+};
+
+static int mx2_camera_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = icd;
+ q->ops = &mx2_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct mx2_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+ return vb2_queue_init(q);
+}
+
+#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH | \
+ V4L2_MBUS_DATA_ACTIVE_LOW)
+
+static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
+{
+ int count = 0;
+
+ readl(pcdev->base_emma + PRP_CNTL);
+ writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
+ while (count++ < 100) {
+ if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
+ return 0;
+ barrier();
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ int ret;
+ int bytesperline;
+ u32 csicr1 = pcdev->csicr1;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, MX2_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = MX2_BUS_FLAGS;
+ }
+
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ csicr1 = (csicr1 & ~CSICR1_FMT_MASK) | pcdev->emma_prp->cfg.csicr1;
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ csicr1 |= CSICR1_REDGE;
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ csicr1 |= CSICR1_SOF_POL;
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ csicr1 |= CSICR1_HSYNC_POL;
+ if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC)
+ csicr1 |= CSICR1_EXT_VSYNC;
+ if (pcdev->platform_flags & MX2_CAMERA_CCIR)
+ csicr1 |= CSICR1_CCIR_EN;
+ if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE)
+ csicr1 |= CSICR1_CCIR_MODE;
+ if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK)
+ csicr1 |= CSICR1_GCLK_MODE;
+ if (pcdev->platform_flags & MX2_CAMERA_INV_DATA)
+ csicr1 |= CSICR1_INV_DATA;
+
+ pcdev->csicr1 = csicr1;
+
+ bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ if (bytesperline < 0)
+ return bytesperline;
+
+ ret = mx27_camera_emma_prp_reset(pcdev);
+ if (ret)
+ return ret;
+
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+
+ return 0;
+}
+
+static int mx2_camera_set_crop(struct soc_camera_device *icd,
+ const struct v4l2_crop *a)
+{
+ struct v4l2_crop a_writable = *a;
+ struct v4l2_rect *rect = &a_writable.c;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &fmt.format;
+ int ret;
+
+ soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+ soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+
+ ret = v4l2_subdev_call(sd, video, s_crop, a);
+ if (ret < 0)
+ return ret;
+
+ /* The capture device might have changed its output */
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+ mf->width, mf->height);
+
+ icd->user_width = mf->width;
+ icd->user_height = mf->height;
+
+ return ret;
+}
+
+static int mx2_camera_get_formats(struct soc_camera_device *icd,
+ unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_mbus_pixelfmt *fmt;
+ struct device *dev = icd->parent;
+ struct v4l2_subdev_mbus_code_enum code = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .index = idx,
+ };
+ int ret, formats = 0;
+
+ ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
+ if (ret < 0)
+ /* no more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code.code);
+ if (!fmt) {
+ dev_err(dev, "Invalid format code #%u: %d\n", idx, code.code);
+ return 0;
+ }
+
+ if (code.code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ code.code == MEDIA_BUS_FMT_UYVY8_2X8) {
+ formats++;
+ if (xlate) {
+ /*
+ * CH2 can output YUV420 which is a standard format in
+ * soc_mediabus.c
+ */
+ xlate->host_fmt =
+ soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_1_5X8);
+ xlate->code = code.code;
+ dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+ xlate->host_fmt->name, code.code);
+ xlate++;
+ }
+ }
+
+ if (code.code == MEDIA_BUS_FMT_UYVY8_2X8) {
+ formats++;
+ if (xlate) {
+ xlate->host_fmt =
+ soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_2X8);
+ xlate->code = code.code;
+ dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+ xlate->host_fmt->name, code.code);
+ xlate++;
+ }
+ }
+
+ /* Generic pass-trough */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code.code;
+ xlate++;
+ }
+ return formats;
+}
+
+static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
+ struct v4l2_mbus_framefmt *mf_in,
+ struct v4l2_pix_format *pix_out, bool apply)
+{
+ unsigned int num, den;
+ unsigned long m;
+ int i, dir;
+
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+ struct emma_prp_resize tmprsz;
+ unsigned char *s = tmprsz.s;
+ int len = 0;
+ int in, out;
+
+ if (dir == RESIZE_DIR_H) {
+ in = mf_in->width;
+ out = pix_out->width;
+ } else {
+ in = mf_in->height;
+ out = pix_out->height;
+ }
+
+ if (in < out)
+ return -EINVAL;
+ else if (in == out)
+ continue;
+
+ /* Calculate ratio */
+ m = gcd(in, out);
+ num = in / m;
+ den = out / m;
+ if (num > RESIZE_NUM_MAX)
+ return -EINVAL;
+
+ if ((num >= 2 * den) && (den == 1) &&
+ (num < 9) && (!(num & 0x01))) {
+ int sum = 0;
+ int j;
+
+ /* Average scaling for >= 2:1 ratios */
+ /* Support can be added for num >=9 and odd values */
+
+ tmprsz.algo = RESIZE_ALGO_AVERAGING;
+ len = num;
+
+ for (i = 0; i < (len / 2); i++)
+ s[i] = 8;
+
+ do {
+ for (i = 0; i < (len / 2); i++) {
+ s[i] = s[i] >> 1;
+ sum = 0;
+ for (j = 0; j < (len / 2); j++)
+ sum += s[j];
+ if (sum == 4)
+ break;
+ }
+ } while (sum != 4);
+
+ for (i = (len / 2); i < len; i++)
+ s[i] = s[len - i - 1];
+
+ s[len - 1] |= SZ_COEF;
+ } else {
+ /* bilinear scaling for < 2:1 ratios */
+ int v; /* overflow counter */
+ int coeff, nxt; /* table output */
+ int in_pos_inc = 2 * den;
+ int out_pos = num;
+ int out_pos_inc = 2 * num;
+ int init_carry = num - den;
+ int carry = init_carry;
+
+ tmprsz.algo = RESIZE_ALGO_BILINEAR;
+ v = den + in_pos_inc;
+ do {
+ coeff = v - out_pos;
+ out_pos += out_pos_inc;
+ carry += out_pos_inc;
+ for (nxt = 0; v < out_pos; nxt++) {
+ v += in_pos_inc;
+ carry -= in_pos_inc;
+ }
+
+ if (len > RESIZE_NUM_MAX)
+ return -EINVAL;
+
+ coeff = ((coeff << BC_COEF) +
+ (in_pos_inc >> 1)) / in_pos_inc;
+
+ if (coeff >= (SZ_COEF - 1))
+ coeff--;
+
+ coeff |= SZ_COEF;
+ s[len] = (unsigned char)coeff;
+ len++;
+
+ for (i = 1; i < nxt; i++) {
+ if (len >= RESIZE_NUM_MAX)
+ return -EINVAL;
+ s[len] = 0;
+ len++;
+ }
+ } while (carry != init_carry);
+ }
+ tmprsz.len = len;
+ if (dir == RESIZE_DIR_H)
+ mf_in->width = pix_out->width;
+ else
+ mf_in->height = pix_out->height;
+
+ if (apply)
+ memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz));
+ }
+ return 0;
+}
+
+static int mx2_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ int ret;
+
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ /* Store width and height returned by the sensor for resizing */
+ pcdev->s_width = mf->width;
+ pcdev->s_height = mf->height;
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+ __func__, pcdev->s_width, pcdev->s_height);
+
+ pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+ xlate->host_fmt->fourcc);
+
+ memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+ if ((mf->width != pix->width || mf->height != pix->height) &&
+ pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+ if (mx2_emmaprp_resize(pcdev, mf, pix, true) < 0)
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+ }
+
+ if (mf->code != xlate->code)
+ return -EINVAL;
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->field = mf->field;
+ pix->colorspace = mf->colorspace;
+ icd->current_fmt = xlate;
+
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ return 0;
+}
+
+static int mx2_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ __u32 pixfmt = pix->pixelformat;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *emma_prp;
+ int ret;
+
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (pixfmt && !xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /*
+ * limit to MX27 hardware capabilities: width must be a multiple of 8 as
+ * requested by the CSI. (Table 39-2 in the i.MX27 Reference Manual).
+ */
+ pix->width &= ~0x7;
+
+ /* limit to sensor capabilities */
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+ __func__, pcdev->s_width, pcdev->s_height);
+
+ /* If the sensor does not support image size try PrP resizing */
+ emma_prp = mx27_emma_prp_get_format(xlate->code,
+ xlate->host_fmt->fourcc);
+
+ if ((mf->width != pix->width || mf->height != pix->height) &&
+ emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+ if (mx2_emmaprp_resize(pcdev, mf, pix, false) < 0)
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+ }
+
+ if (mf->field == V4L2_FIELD_ANY)
+ mf->field = V4L2_FIELD_NONE;
+ /*
+ * Driver supports interlaced images provided they have
+ * both fields so that they can be processed as if they
+ * were progressive.
+ */
+ if (mf->field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf->field)) {
+ dev_err(icd->parent, "Field type %d unsupported.\n",
+ mf->field);
+ return -EINVAL;
+ }
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->field = mf->field;
+ pix->colorspace = mf->colorspace;
+
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ return 0;
+}
+
+static int mx2_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the friendly caller:-> */
+ strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static unsigned int mx2_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = mx2_camera_add_device,
+ .remove = mx2_camera_remove_device,
+ .clock_start = mx2_camera_clock_start,
+ .clock_stop = mx2_camera_clock_stop,
+ .set_fmt = mx2_camera_set_fmt,
+ .set_crop = mx2_camera_set_crop,
+ .get_formats = mx2_camera_get_formats,
+ .try_fmt = mx2_camera_try_fmt,
+ .init_videobuf2 = mx2_camera_init_videobuf,
+ .poll = mx2_camera_poll,
+ .querycap = mx2_camera_querycap,
+ .set_bus_param = mx2_camera_set_bus_param,
+};
+
+static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
+ int bufnum, bool err)
+{
+#ifdef DEBUG
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+#endif
+ struct mx2_buf_internal *ibuf;
+ struct mx2_buffer *buf;
+ struct vb2_buffer *vb;
+ struct vb2_v4l2_buffer *vbuf;
+ unsigned long phys;
+
+ ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal,
+ queue);
+
+ BUG_ON(ibuf->bufnum != bufnum);
+
+ if (ibuf->discard) {
+ /*
+ * Discard buffer must not be returned to user space.
+ * Just return it to the discard queue.
+ */
+ list_move_tail(pcdev->active_bufs.next, &pcdev->discard);
+ } else {
+ buf = mx2_ibuf_to_buf(ibuf);
+
+ vb = &buf->vb.vb2_buf;
+ vbuf = to_vb2_v4l2_buffer(vb);
+#ifdef DEBUG
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (prp->cfg.channel == 1) {
+ if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR +
+ 4 * bufnum) != phys) {
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
+ readl(pcdev->base_emma +
+ PRP_DEST_RGB1_PTR + 4 * bufnum));
+ }
+ } else {
+ if (readl(pcdev->base_emma + PRP_DEST_Y_PTR -
+ 0x14 * bufnum) != phys) {
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
+ readl(pcdev->base_emma +
+ PRP_DEST_Y_PTR - 0x14 * bufnum));
+ }
+ }
+#endif
+ dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
+ vb2_plane_vaddr(vb, 0),
+ vb2_get_plane_payload(vb, 0));
+
+ list_del_init(&buf->internal.queue);
+ vb->timestamp = ktime_get_ns();
+ vbuf->sequence = pcdev->frame_count;
+ if (err)
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ else
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+
+ pcdev->frame_count++;
+
+ if (list_empty(&pcdev->capture)) {
+ if (list_empty(&pcdev->discard)) {
+ dev_warn(pcdev->dev, "%s: trying to access empty discard list\n",
+ __func__);
+ return;
+ }
+
+ ibuf = list_first_entry(&pcdev->discard,
+ struct mx2_buf_internal, queue);
+ ibuf->bufnum = bufnum;
+
+ list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
+ mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
+ return;
+ }
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+
+ buf->internal.bufnum = bufnum;
+
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ vb = &buf->vb.vb2_buf;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, bufnum);
+}
+
+static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data)
+{
+ struct mx2_camera_dev *pcdev = data;
+ unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS);
+ struct mx2_buf_internal *ibuf;
+
+ spin_lock(&pcdev->lock);
+
+ if (list_empty(&pcdev->active_bufs)) {
+ dev_warn(pcdev->dev, "%s: called while active list is empty\n",
+ __func__);
+
+ if (!status) {
+ spin_unlock(&pcdev->lock);
+ return IRQ_NONE;
+ }
+ }
+
+ if (status & (1 << 7)) { /* overflow */
+ u32 cntl = readl(pcdev->base_emma + PRP_CNTL);
+ writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN),
+ pcdev->base_emma + PRP_CNTL);
+ writel(cntl, pcdev->base_emma + PRP_CNTL);
+
+ ibuf = list_first_entry(&pcdev->active_bufs,
+ struct mx2_buf_internal, queue);
+ mx27_camera_frame_done_emma(pcdev,
+ ibuf->bufnum, true);
+
+ status &= ~(1 << 7);
+ } else if (((status & (3 << 5)) == (3 << 5)) ||
+ ((status & (3 << 3)) == (3 << 3))) {
+ /*
+ * Both buffers have triggered, process the one we're expecting
+ * to first
+ */
+ ibuf = list_first_entry(&pcdev->active_bufs,
+ struct mx2_buf_internal, queue);
+ mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false);
+ status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */
+ } else if ((status & (1 << 6)) || (status & (1 << 4))) {
+ mx27_camera_frame_done_emma(pcdev, 0, false);
+ } else if ((status & (1 << 5)) || (status & (1 << 3))) {
+ mx27_camera_frame_done_emma(pcdev, 1, false);
+ }
+
+ spin_unlock(&pcdev->lock);
+ writel(status, pcdev->base_emma + PRP_INTRSTATUS);
+
+ return IRQ_HANDLED;
+}
+
+static int mx27_camera_emma_init(struct platform_device *pdev)
+{
+ struct mx2_camera_dev *pcdev = platform_get_drvdata(pdev);
+ struct resource *res_emma;
+ int irq_emma;
+ int err = 0;
+
+ res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irq_emma = platform_get_irq(pdev, 1);
+ if (!res_emma || !irq_emma) {
+ dev_err(pcdev->dev, "no EMMA resources\n");
+ err = -ENODEV;
+ goto out;
+ }
+
+ pcdev->base_emma = devm_ioremap_resource(pcdev->dev, res_emma);
+ if (IS_ERR(pcdev->base_emma)) {
+ err = PTR_ERR(pcdev->base_emma);
+ goto out;
+ }
+
+ err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0,
+ MX2_CAM_DRV_NAME, pcdev);
+ if (err) {
+ dev_err(pcdev->dev, "Camera EMMA interrupt register failed\n");
+ goto out;
+ }
+
+ pcdev->clk_emma_ipg = devm_clk_get(pcdev->dev, "emma-ipg");
+ if (IS_ERR(pcdev->clk_emma_ipg)) {
+ err = PTR_ERR(pcdev->clk_emma_ipg);
+ goto out;
+ }
+
+ clk_prepare_enable(pcdev->clk_emma_ipg);
+
+ pcdev->clk_emma_ahb = devm_clk_get(pcdev->dev, "emma-ahb");
+ if (IS_ERR(pcdev->clk_emma_ahb)) {
+ err = PTR_ERR(pcdev->clk_emma_ahb);
+ goto exit_clk_emma_ipg;
+ }
+
+ clk_prepare_enable(pcdev->clk_emma_ahb);
+
+ err = mx27_camera_emma_prp_reset(pcdev);
+ if (err)
+ goto exit_clk_emma_ahb;
+
+ return err;
+
+exit_clk_emma_ahb:
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+exit_clk_emma_ipg:
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+out:
+ return err;
+}
+
+static int mx2_camera_probe(struct platform_device *pdev)
+{
+ struct mx2_camera_dev *pcdev;
+ struct resource *res_csi;
+ int irq_csi;
+ int err = 0;
+
+ dev_dbg(&pdev->dev, "initialising\n");
+
+ res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq_csi = platform_get_irq(pdev, 0);
+ if (res_csi == NULL || irq_csi < 0) {
+ dev_err(&pdev->dev, "Missing platform resources data\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ pcdev->clk_csi_ahb = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(pcdev->clk_csi_ahb)) {
+ dev_err(&pdev->dev, "Could not get csi ahb clock\n");
+ err = PTR_ERR(pcdev->clk_csi_ahb);
+ goto exit;
+ }
+
+ pcdev->clk_csi_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(pcdev->clk_csi_per)) {
+ dev_err(&pdev->dev, "Could not get csi per clock\n");
+ err = PTR_ERR(pcdev->clk_csi_per);
+ goto exit;
+ }
+
+ pcdev->pdata = pdev->dev.platform_data;
+ if (pcdev->pdata) {
+ long rate;
+
+ pcdev->platform_flags = pcdev->pdata->flags;
+
+ rate = clk_round_rate(pcdev->clk_csi_per,
+ pcdev->pdata->clk * 2);
+ if (rate <= 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+ err = clk_set_rate(pcdev->clk_csi_per, rate);
+ if (err < 0)
+ goto exit;
+ }
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ INIT_LIST_HEAD(&pcdev->active_bufs);
+ INIT_LIST_HEAD(&pcdev->discard);
+ spin_lock_init(&pcdev->lock);
+
+ pcdev->base_csi = devm_ioremap_resource(&pdev->dev, res_csi);
+ if (IS_ERR(pcdev->base_csi)) {
+ err = PTR_ERR(pcdev->base_csi);
+ goto exit;
+ }
+
+ pcdev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, pcdev);
+
+ err = mx27_camera_emma_init(pdev);
+ if (err)
+ goto exit;
+
+ /*
+ * We're done with drvdata here. Clear the pointer so that
+ * v4l2 core can start using drvdata on its purpose.
+ */
+ platform_set_drvdata(pdev, NULL);
+
+ pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME,
+ pcdev->soc_host.ops = &mx2_soc_camera_host_ops,
+ pcdev->soc_host.priv = pcdev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
+ pcdev->soc_host.nr = pdev->id;
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ err = PTR_ERR(pcdev->alloc_ctx);
+ goto eallocctx;
+ }
+ err = soc_camera_host_register(&pcdev->soc_host);
+ if (err)
+ goto exit_free_emma;
+
+ dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n",
+ clk_get_rate(pcdev->clk_csi_per));
+
+ return 0;
+
+exit_free_emma:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+eallocctx:
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+exit:
+ return err;
+}
+
+static int mx2_camera_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct mx2_camera_dev *pcdev = container_of(soc_host,
+ struct mx2_camera_dev, soc_host);
+
+ soc_camera_host_unregister(&pcdev->soc_host);
+
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+
+ dev_info(&pdev->dev, "MX2 Camera driver unloaded\n");
+
+ return 0;
+}
+
+static struct platform_driver mx2_camera_driver = {
+ .driver = {
+ .name = MX2_CAM_DRV_NAME,
+ },
+ .id_table = mx2_camera_devtype,
+ .remove = mx2_camera_remove,
+};
+
+module_platform_driver_probe(mx2_camera_driver, mx2_camera_probe);
+
+MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver");
+MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MX2_CAM_VERSION);
diff --git a/drivers/staging/media/mx3/Kconfig b/drivers/staging/media/mx3/Kconfig
new file mode 100644
index 000000000..595d5fe7c
--- /dev/null
+++ b/drivers/staging/media/mx3/Kconfig
@@ -0,0 +1,15 @@
+config VIDEO_MX3
+ tristate "i.MX3x Camera Sensor Interface driver"
+ depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
+ depends on MX3_IPU || COMPILE_TEST
+ depends on HAS_DMA
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This is a v4l2 driver for the i.MX3x Camera Sensor Interface
+
+ This driver is deprecated: it should become a stand-alone driver
+ instead of using the soc-camera framework.
+
+ Unless someone is willing to take this on (unlikely with such
+ ancient hardware) it is going to be removed from the kernel
+ soon.
diff --git a/drivers/staging/media/mx3/Makefile b/drivers/staging/media/mx3/Makefile
new file mode 100644
index 000000000..6d91dcd80
--- /dev/null
+++ b/drivers/staging/media/mx3/Makefile
@@ -0,0 +1,3 @@
+# Makefile for i.MX3x Camera Sensor driver
+
+obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
diff --git a/drivers/staging/media/mx3/TODO b/drivers/staging/media/mx3/TODO
new file mode 100644
index 000000000..bc68fa443
--- /dev/null
+++ b/drivers/staging/media/mx3/TODO
@@ -0,0 +1,10 @@
+This driver is deprecated: it should become a stand-alone driver instead of
+using the soc-camera framework.
+
+Unless someone is willing to take this on (unlikely with such ancient
+hardware) it is going to be removed from the kernel soon.
+
+Note that trivial patches will not be accepted anymore, only a full conversion.
+
+If you want to convert this driver, please contact the linux-media mailinglist
+(see http://linuxtv.org/lists.php).
diff --git a/drivers/staging/media/mx3/mx3_camera.c b/drivers/staging/media/mx3/mx3_camera.c
new file mode 100644
index 000000000..aa39e9569
--- /dev/null
+++ b/drivers/staging/media/mx3/mx3_camera.c
@@ -0,0 +1,1264 @@
+/*
+ * V4L2 Driver for i.MX3x camera host
+ *
+ * Copyright (C) 2008
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/dma/ipu-dma.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/soc_camera.h>
+#include <media/drv-intf/soc_mediabus.h>
+
+#include <linux/platform_data/media/camera-mx3.h>
+#include <linux/platform_data/dma-imx.h>
+
+#define MX3_CAM_DRV_NAME "mx3-camera"
+
+/* CMOS Sensor Interface Registers */
+#define CSI_REG_START 0x60
+
+#define CSI_SENS_CONF (0x60 - CSI_REG_START)
+#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START)
+#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START)
+#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START)
+#define CSI_TST_CTRL (0x70 - CSI_REG_START)
+#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START)
+#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START)
+#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START)
+#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START)
+#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START)
+
+#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0
+#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1
+#define CSI_SENS_CONF_DATA_POL_SHIFT 2
+#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3
+#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4
+#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7
+#define CSI_SENS_CONF_DATA_FMT_SHIFT 8
+#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10
+#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15
+#define CSI_SENS_CONF_DIVRATIO_SHIFT 16
+
+#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+
+#define MAX_VIDEO_MEM 16
+
+struct mx3_camera_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+
+ /* One descriptot per scatterlist (per frame) */
+ struct dma_async_tx_descriptor *txd;
+
+ /* We have to "build" a scatterlist ourselves - one element per frame */
+ struct scatterlist sg;
+};
+
+/**
+ * struct mx3_camera_dev - i.MX3x camera (CSI) object
+ * @dev: camera device, to which the coherent buffer is attached
+ * @icd: currently attached camera sensor
+ * @clk: pointer to clock
+ * @base: remapped register base address
+ * @pdata: platform data
+ * @platform_flags: platform flags
+ * @mclk: master clock frequency in Hz
+ * @capture: list of capture videobuffers
+ * @lock: protects video buffer lists
+ * @active: active video buffer
+ * @idmac_channel: array of pointers to IPU DMAC DMA channels
+ * @soc_host: embedded soc_host object
+ */
+struct mx3_camera_dev {
+ /*
+ * i.MX3x is only supposed to handle one camera on its Camera Sensor
+ * Interface. If anyone ever builds hardware to enable more than one
+ * camera _simultaneously_, they will have to modify this driver too
+ */
+ struct clk *clk;
+
+ void __iomem *base;
+
+ struct mx3_camera_pdata *pdata;
+
+ unsigned long platform_flags;
+ unsigned long mclk;
+ u16 width_flags; /* max 15 bits */
+
+ struct list_head capture;
+ spinlock_t lock; /* Protects video buffer lists */
+ struct mx3_camera_buffer *active;
+ size_t buf_total;
+ struct vb2_alloc_ctx *alloc_ctx;
+ enum v4l2_field field;
+ int sequence;
+
+ /* IDMAC / dmaengine interface */
+ struct idmac_channel *idmac_channel[1]; /* We need one channel */
+
+ struct soc_camera_host soc_host;
+};
+
+struct dma_chan_request {
+ struct mx3_camera_dev *mx3_cam;
+ enum ipu_channel id;
+};
+
+static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg)
+{
+ return __raw_readl(mx3->base + reg);
+}
+
+static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg)
+{
+ __raw_writel(value, mx3->base + reg);
+}
+
+static struct mx3_camera_buffer *to_mx3_vb(struct vb2_v4l2_buffer *vb)
+{
+ return container_of(vb, struct mx3_camera_buffer, vb);
+}
+
+/* Called from the IPU IDMAC ISR */
+static void mx3_cam_dma_done(void *arg)
+{
+ struct idmac_tx_desc *desc = to_tx_desc(arg);
+ struct dma_chan *chan = desc->txd.chan;
+ struct idmac_channel *ichannel = to_idmac_chan(chan);
+ struct mx3_camera_dev *mx3_cam = ichannel->client;
+
+ dev_dbg(chan->device->dev, "callback cookie %d, active DMA %pad\n",
+ desc->txd.cookie, mx3_cam->active ? &sg_dma_address(&mx3_cam->active->sg) : NULL);
+
+ spin_lock(&mx3_cam->lock);
+ if (mx3_cam->active) {
+ struct vb2_v4l2_buffer *vb = &mx3_cam->active->vb;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+
+ list_del_init(&buf->queue);
+ vb->vb2_buf.timestamp = ktime_get_ns();
+ vb->field = mx3_cam->field;
+ vb->sequence = mx3_cam->sequence++;
+ vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
+ }
+
+ if (list_empty(&mx3_cam->capture)) {
+ mx3_cam->active = NULL;
+ spin_unlock(&mx3_cam->lock);
+
+ /*
+ * stop capture - without further buffers IPU_CHA_BUF0_RDY will
+ * not get updated
+ */
+ return;
+ }
+
+ mx3_cam->active = list_entry(mx3_cam->capture.next,
+ struct mx3_camera_buffer, queue);
+ spin_unlock(&mx3_cam->lock);
+}
+
+/*
+ * Videobuf operations
+ */
+
+/*
+ * Calculate the __buffer__ (not data) size and number of buffers.
+ */
+static int mx3_videobuf_setup(struct vb2_queue *vq,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+
+ if (!mx3_cam->idmac_channel[0])
+ return -EINVAL;
+
+ alloc_ctxs[0] = mx3_cam->alloc_ctx;
+
+ if (!vq->num_buffers)
+ mx3_cam->sequence = 0;
+
+ if (!*count)
+ *count = 2;
+
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ if (!*num_planes)
+ sizes[0] = icd->sizeimage;
+ else if (sizes[0] < icd->sizeimage)
+ return -EINVAL;
+
+ /* If *num_planes != 0, we have already verified *count. */
+ if (sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
+ sizes[0];
+
+ *num_planes = 1;
+
+ return 0;
+}
+
+static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
+{
+ /* Add more formats as need arises and test possibilities appear... */
+ switch (fourcc) {
+ case V4L2_PIX_FMT_RGB24:
+ return IPU_PIX_FMT_RGB24;
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_RGB565:
+ default:
+ return IPU_PIX_FMT_GENERIC;
+ }
+}
+
+static void mx3_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vbuf);
+ struct scatterlist *sg = &buf->sg;
+ struct dma_async_tx_descriptor *txd;
+ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+ struct idmac_video_param *video = &ichan->params.video;
+ const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
+ dma_cookie_t cookie;
+ size_t new_size;
+
+ new_size = icd->sizeimage;
+
+ if (vb2_plane_size(vb, 0) < new_size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
+ vbuf->vb2_buf.index, vb2_plane_size(vb, 0), new_size);
+ goto error;
+ }
+
+ if (!buf->txd) {
+ sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
+ sg_dma_len(sg) = new_size;
+
+ txd = dmaengine_prep_slave_sg(
+ &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!txd)
+ goto error;
+
+ txd->callback_param = txd;
+ txd->callback = mx3_cam_dma_done;
+
+ buf->txd = txd;
+ } else {
+ txd = buf->txd;
+ }
+
+ vb2_set_plane_payload(vb, 0, new_size);
+
+ /* This is the configuration of one sg-element */
+ video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
+
+ if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
+ /*
+ * If the IPU DMA channel is configured to transfer generic
+ * 8-bit data, we have to set up the geometry parameters
+ * correctly, according to the current pixel format. The DMA
+ * horizontal parameters in this case are expressed in bytes,
+ * not in pixels.
+ */
+ video->out_width = icd->bytesperline;
+ video->out_height = icd->user_height;
+ video->out_stride = icd->bytesperline;
+ } else {
+ /*
+ * For IPU known formats the pixel unit will be managed
+ * successfully by the IPU code
+ */
+ video->out_width = icd->user_width;
+ video->out_height = icd->user_height;
+ video->out_stride = icd->user_width;
+ }
+
+#ifdef DEBUG
+ /* helps to see what DMA actually has written */
+ if (vb2_plane_vaddr(vb, 0))
+ memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
+#endif
+
+ spin_lock_irq(&mx3_cam->lock);
+ list_add_tail(&buf->queue, &mx3_cam->capture);
+
+ if (!mx3_cam->active)
+ mx3_cam->active = buf;
+
+ spin_unlock_irq(&mx3_cam->lock);
+
+ cookie = txd->tx_submit(txd);
+ dev_dbg(icd->parent, "Submitted cookie %d DMA %pad\n",
+ cookie, &sg_dma_address(&buf->sg));
+
+ if (cookie >= 0)
+ return;
+
+ spin_lock_irq(&mx3_cam->lock);
+
+ /* Submit error */
+ list_del_init(&buf->queue);
+
+ if (mx3_cam->active == buf)
+ mx3_cam->active = NULL;
+
+ spin_unlock_irq(&mx3_cam->lock);
+error:
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+static void mx3_videobuf_release(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vbuf);
+ struct dma_async_tx_descriptor *txd = buf->txd;
+ unsigned long flags;
+
+ dev_dbg(icd->parent,
+ "Release%s DMA %pad, queue %sempty\n",
+ mx3_cam->active == buf ? " active" : "", &sg_dma_address(&buf->sg),
+ list_empty(&buf->queue) ? "" : "not ");
+
+ spin_lock_irqsave(&mx3_cam->lock, flags);
+
+ if (mx3_cam->active == buf)
+ mx3_cam->active = NULL;
+
+ /* Doesn't hurt also if the list is empty */
+ list_del_init(&buf->queue);
+
+ if (txd) {
+ buf->txd = NULL;
+ if (mx3_cam->idmac_channel[0])
+ async_tx_ack(txd);
+ }
+
+ spin_unlock_irqrestore(&mx3_cam->lock, flags);
+
+ mx3_cam->buf_total -= vb2_plane_size(vb, 0);
+}
+
+static int mx3_videobuf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vbuf);
+
+ if (!buf->txd) {
+ /* This is for locking debugging only */
+ INIT_LIST_HEAD(&buf->queue);
+ sg_init_table(&buf->sg, 1);
+
+ mx3_cam->buf_total += vb2_plane_size(vb, 0);
+ }
+
+ return 0;
+}
+
+static void mx3_stop_streaming(struct vb2_queue *q)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+ struct mx3_camera_buffer *buf, *tmp;
+ unsigned long flags;
+
+ if (ichan)
+ dmaengine_pause(&ichan->dma_chan);
+
+ spin_lock_irqsave(&mx3_cam->lock, flags);
+
+ mx3_cam->active = NULL;
+
+ list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
+ list_del_init(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irqrestore(&mx3_cam->lock, flags);
+}
+
+static struct vb2_ops mx3_videobuf_ops = {
+ .queue_setup = mx3_videobuf_setup,
+ .buf_queue = mx3_videobuf_queue,
+ .buf_cleanup = mx3_videobuf_release,
+ .buf_init = mx3_videobuf_init,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .stop_streaming = mx3_stop_streaming,
+};
+
+static int mx3_camera_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = icd;
+ q->ops = &mx3_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct mx3_camera_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &ici->host_lock;
+
+ return vb2_queue_init(q);
+}
+
+/* First part of ipu_csi_init_interface() */
+static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam)
+{
+ u32 conf;
+ long rate;
+
+ /* Set default size: ipu_csi_set_window_size() */
+ csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE);
+ /* ...and position to 0:0: ipu_csi_set_window_pos() */
+ conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+ csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL);
+
+ /* We use only gated clock synchronisation mode so far */
+ conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT;
+
+ /* Set generic data, platform-biggest bus-width */
+ conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+ conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+ conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+ conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/
+ conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+
+ if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC)
+ conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC)
+ conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+ conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+ conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+ conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+ conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+
+ /* ipu_csi_init_interface() */
+ csi_reg_write(mx3_cam, conf, CSI_SENS_CONF);
+
+ clk_prepare_enable(mx3_cam->clk);
+ rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
+ dev_dbg(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+ if (rate)
+ clk_set_rate(mx3_cam->clk, rate);
+}
+
+static int mx3_camera_add_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void mx3_camera_remove_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+/* Called with .host_lock held */
+static int mx3_camera_clock_start(struct soc_camera_host *ici)
+{
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+
+ mx3_camera_activate(mx3_cam);
+
+ mx3_cam->buf_total = 0;
+
+ return 0;
+}
+
+/* Called with .host_lock held */
+static void mx3_camera_clock_stop(struct soc_camera_host *ici)
+{
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+
+ if (*ichan) {
+ dma_release_channel(&(*ichan)->dma_chan);
+ *ichan = NULL;
+ }
+
+ clk_disable_unprepare(mx3_cam->clk);
+}
+
+static int test_platform_param(struct mx3_camera_dev *mx3_cam,
+ unsigned char buswidth, unsigned long *flags)
+{
+ /*
+ * If requested data width is supported by the platform, use it or any
+ * possible lower value - i.MX31 is smart enough to shift bits
+ */
+ if (buswidth > fls(mx3_cam->width_flags))
+ return -EINVAL;
+
+ /*
+ * Platform specified synchronization and pixel clock polarities are
+ * only a recommendation and are only used during probing. MX3x
+ * camera interface only works in master mode, i.e., uses HSYNC and
+ * VSYNC signals from the sensor
+ */
+ *flags = V4L2_MBUS_MASTER |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_LOW;
+
+ return 0;
+}
+
+static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
+ const unsigned int depth)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
+ int ret = test_platform_param(mx3_cam, depth, &bus_flags);
+
+ dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
+
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool chan_filter(struct dma_chan *chan, void *arg)
+{
+ struct dma_chan_request *rq = arg;
+ struct mx3_camera_pdata *pdata;
+
+ if (!imx_dma_is_ipu(chan))
+ return false;
+
+ if (!rq)
+ return false;
+
+ pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data;
+
+ return rq->id == chan->chan_id &&
+ pdata->dma_dev == chan->device->dev;
+}
+
+static const struct soc_mbus_pixelfmt mx3_camera_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .name = "Bayer BGGR (sRGB) 8 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ }, {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .name = "Monochrome 8 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+};
+
+/* This will be corrected as we get more formats */
+static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ int formats = 0, ret;
+ struct v4l2_subdev_mbus_code_enum code = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .index = idx,
+ };
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code.code);
+ if (!fmt) {
+ dev_warn(icd->parent,
+ "Unsupported format code #%u: 0x%x\n", idx, code.code);
+ return 0;
+ }
+
+ /* This also checks support for the requested bits-per-sample */
+ ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample);
+ if (ret < 0)
+ return 0;
+
+ switch (code.code) {
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &mx3_camera_formats[0];
+ xlate->code = code.code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s using code 0x%x\n",
+ mx3_camera_formats[0].name, code.code);
+ }
+ break;
+ case MEDIA_BUS_FMT_Y10_1X10:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &mx3_camera_formats[1];
+ xlate->code = code.code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s using code 0x%x\n",
+ mx3_camera_formats[1].name, code.code);
+ }
+ break;
+ default:
+ if (!mx3_camera_packing_supported(fmt))
+ return 0;
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code.code;
+ dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n",
+ (fmt->fourcc >> (0*8)) & 0xFF,
+ (fmt->fourcc >> (1*8)) & 0xFF,
+ (fmt->fourcc >> (2*8)) & 0xFF,
+ (fmt->fourcc >> (3*8)) & 0xFF);
+ xlate++;
+ }
+
+ return formats;
+}
+
+static void configure_geometry(struct mx3_camera_dev *mx3_cam,
+ unsigned int width, unsigned int height,
+ const struct soc_mbus_pixelfmt *fmt)
+{
+ u32 ctrl, width_field, height_field;
+
+ if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) {
+ /*
+ * As the CSI will be configured to output BAYER, here
+ * the width parameter count the number of samples to
+ * capture to complete the whole image width.
+ */
+ unsigned int num, den;
+ int ret = soc_mbus_samples_per_pixel(fmt, &num, &den);
+ BUG_ON(ret < 0);
+ width = width * num / den;
+ }
+
+ /* Setup frame size - this cannot be changed on-the-fly... */
+ width_field = width - 1;
+ height_field = height - 1;
+ csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
+
+ csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
+ csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2);
+
+ csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE);
+
+ /* ...and position */
+ ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+ /* Sensor does the cropping */
+ csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
+}
+
+static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+ struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+ /* We have to use IDMAC_IC_7 for Bayer / generic data */
+ struct dma_chan_request rq = {.mx3_cam = mx3_cam,
+ .id = IDMAC_IC_7};
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+ chan = dma_request_channel(mask, chan_filter, &rq);
+ if (!chan)
+ return -EBUSY;
+
+ *ichan = to_idmac_chan(chan);
+ (*ichan)->client = mx3_cam;
+
+ return 0;
+}
+
+/*
+ * FIXME: learn to use stride != width, then we can keep stride properly aligned
+ * and support arbitrary (even) widths.
+ */
+static inline void stride_align(__u32 *width)
+{
+ if (ALIGN(*width, 8) < 4096)
+ *width = ALIGN(*width, 8);
+ else
+ *width = *width & ~7;
+}
+
+/*
+ * As long as we don't implement host-side cropping and scaling, we can use
+ * default g_crop and cropcap from soc_camera.c
+ */
+static int mx3_camera_set_crop(struct soc_camera_device *icd,
+ const struct v4l2_crop *a)
+{
+ struct v4l2_crop a_writable = *a;
+ struct v4l2_rect *rect = &a_writable.c;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &fmt.format;
+ int ret;
+
+ soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+ soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+
+ ret = v4l2_subdev_call(sd, video, s_crop, a);
+ if (ret < 0)
+ return ret;
+
+ /* The capture device might have changed its output sizes */
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret;
+
+ if (mf->code != icd->current_fmt->code)
+ return -EINVAL;
+
+ if (mf->width & 7) {
+ /* Ouch! We can only handle 8-byte aligned width... */
+ stride_align(&mf->width);
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (mf->width != icd->user_width || mf->height != icd->user_height)
+ configure_geometry(mx3_cam, mf->width, mf->height,
+ icd->current_fmt->host_fmt);
+
+ dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+ mf->width, mf->height);
+
+ icd->user_width = mf->width;
+ icd->user_height = mf->height;
+
+ return ret;
+}
+
+static int mx3_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ stride_align(&pix->width);
+ dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height);
+
+ /*
+ * Might have to perform a complete interface initialisation like in
+ * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider
+ * mxc_v4l2_s_fmt()
+ */
+
+ configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt);
+
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format);
+ if (ret < 0)
+ return ret;
+
+ if (mf->code != xlate->code)
+ return -EINVAL;
+
+ if (!mx3_cam->idmac_channel[0]) {
+ ret = acquire_dma_channel(mx3_cam);
+ if (ret < 0)
+ return ret;
+ }
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->field = mf->field;
+ mx3_cam->field = mf->field;
+ pix->colorspace = mf->colorspace;
+ icd->current_fmt = xlate;
+
+ dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height);
+
+ return ret;
+}
+
+static int mx3_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ __u32 pixfmt = pix->pixelformat;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (pixfmt && !xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* limit to MX3 hardware capabilities */
+ if (pix->height > 4096)
+ pix->height = 4096;
+ if (pix->width > 4096)
+ pix->width = 4096;
+
+ /* limit to sensor capabilities */
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->colorspace = mf->colorspace;
+
+ switch (mf->field) {
+ case V4L2_FIELD_ANY:
+ pix->field = V4L2_FIELD_NONE;
+ break;
+ case V4L2_FIELD_NONE:
+ break;
+ default:
+ dev_err(icd->parent, "Field type %d unsupported.\n",
+ mf->field);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mx3_camera_reqbufs(struct soc_camera_device *icd,
+ struct v4l2_requestbuffers *p)
+{
+ return 0;
+}
+
+static unsigned int mx3_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static int mx3_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the firendly caller:-> */
+ strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int mx3_camera_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+ unsigned long bus_flags, common_flags;
+ u32 dw, sens_conf;
+ const struct soc_mbus_pixelfmt *fmt;
+ int buswidth;
+ int ret;
+ const struct soc_camera_format_xlate *xlate;
+ struct device *dev = icd->parent;
+
+ fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
+ if (!fmt)
+ return -EINVAL;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ buswidth = fmt->bits_per_sample;
+ ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
+
+ dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
+
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = bus_flags;
+ }
+
+ dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
+ cfg.flags, bus_flags, common_flags);
+
+ /* Make choices, based on platform preferences */
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ /*
+ * So far only gated clock mode is supported. Add a line
+ * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) |
+ * below and select the required mode when supporting other
+ * synchronisation protocols.
+ */
+ sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) &
+ ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_DATA_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) |
+ (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) |
+ (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT));
+
+ /* TODO: Support RGB and YUV formats */
+
+ /* This has been set in mx3_camera_activate(), but we clear it above */
+ sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+
+ /* Just do what we're asked to do */
+ switch (xlate->host_fmt->bits_per_sample) {
+ case 4:
+ dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ case 8:
+ dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ case 10:
+ dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ default:
+ /*
+ * Actually it can only be 15 now, default is just to silence
+ * compiler warnings
+ */
+ case 15:
+ dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ }
+
+ csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF);
+
+ dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw);
+
+ return 0;
+}
+
+static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = mx3_camera_add_device,
+ .remove = mx3_camera_remove_device,
+ .clock_start = mx3_camera_clock_start,
+ .clock_stop = mx3_camera_clock_stop,
+ .set_crop = mx3_camera_set_crop,
+ .set_fmt = mx3_camera_set_fmt,
+ .try_fmt = mx3_camera_try_fmt,
+ .get_formats = mx3_camera_get_formats,
+ .init_videobuf2 = mx3_camera_init_videobuf,
+ .reqbufs = mx3_camera_reqbufs,
+ .poll = mx3_camera_poll,
+ .querycap = mx3_camera_querycap,
+ .set_bus_param = mx3_camera_set_bus_param,
+};
+
+static int mx3_camera_probe(struct platform_device *pdev)
+{
+ struct mx3_camera_pdata *pdata = pdev->dev.platform_data;
+ struct mx3_camera_dev *mx3_cam;
+ struct resource *res;
+ void __iomem *base;
+ int err = 0;
+ struct soc_camera_host *soc_host;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ if (!pdata)
+ return -EINVAL;
+
+ mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL);
+ if (!mx3_cam) {
+ dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
+ return -ENOMEM;
+ }
+
+ mx3_cam->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mx3_cam->clk))
+ return PTR_ERR(mx3_cam->clk);
+
+ mx3_cam->pdata = pdata;
+ mx3_cam->platform_flags = pdata->flags;
+ if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) {
+ /*
+ * Platform hasn't set available data widths. This is bad.
+ * Warn and use a default.
+ */
+ dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
+ "data widths, using default 8 bit\n");
+ mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
+ }
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
+ mx3_cam->width_flags = 1 << 3;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+ mx3_cam->width_flags |= 1 << 7;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+ mx3_cam->width_flags |= 1 << 9;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+ mx3_cam->width_flags |= 1 << 14;
+
+ mx3_cam->mclk = pdata->mclk_10khz * 10000;
+ if (!mx3_cam->mclk) {
+ dev_warn(&pdev->dev,
+ "mclk_10khz == 0! Please, fix your platform data. "
+ "Using default 20MHz\n");
+ mx3_cam->mclk = 20000000;
+ }
+
+ /* list of video-buffers */
+ INIT_LIST_HEAD(&mx3_cam->capture);
+ spin_lock_init(&mx3_cam->lock);
+
+ mx3_cam->base = base;
+
+ soc_host = &mx3_cam->soc_host;
+ soc_host->drv_name = MX3_CAM_DRV_NAME;
+ soc_host->ops = &mx3_soc_camera_host_ops;
+ soc_host->priv = mx3_cam;
+ soc_host->v4l2_dev.dev = &pdev->dev;
+ soc_host->nr = pdev->id;
+
+ mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(mx3_cam->alloc_ctx))
+ return PTR_ERR(mx3_cam->alloc_ctx);
+
+ if (pdata->asd_sizes) {
+ soc_host->asd = pdata->asd;
+ soc_host->asd_sizes = pdata->asd_sizes;
+ }
+
+ err = soc_camera_host_register(soc_host);
+ if (err)
+ goto ecamhostreg;
+
+ /* IDMAC interface */
+ dmaengine_get();
+
+ return 0;
+
+ecamhostreg:
+ vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+ return err;
+}
+
+static int mx3_camera_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct mx3_camera_dev *mx3_cam = container_of(soc_host,
+ struct mx3_camera_dev, soc_host);
+
+ soc_camera_host_unregister(soc_host);
+
+ /*
+ * The channel has either not been allocated,
+ * or should have been released
+ */
+ if (WARN_ON(mx3_cam->idmac_channel[0]))
+ dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan);
+
+ vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+
+ dmaengine_put();
+
+ return 0;
+}
+
+static struct platform_driver mx3_camera_driver = {
+ .driver = {
+ .name = MX3_CAM_DRV_NAME,
+ },
+ .probe = mx3_camera_probe,
+ .remove = mx3_camera_remove,
+};
+
+module_platform_driver(mx3_camera_driver);
+
+MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.2.3");
+MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME);
diff --git a/drivers/staging/media/omap1/Kconfig b/drivers/staging/media/omap1/Kconfig
new file mode 100644
index 000000000..6cfab3a04
--- /dev/null
+++ b/drivers/staging/media/omap1/Kconfig
@@ -0,0 +1,13 @@
+config VIDEO_OMAP1
+ tristate "OMAP1 Camera Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA
+ depends on ARCH_OMAP1
+ depends on HAS_DMA
+ select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the TI OMAP1 camera interface
+
+ This driver is deprecated and will be removed soon unless someone
+ will start the work to convert this driver to the vb2 framework
+ and remove the soc-camera dependency.
diff --git a/drivers/staging/media/omap1/Makefile b/drivers/staging/media/omap1/Makefile
new file mode 100644
index 000000000..288562260
--- /dev/null
+++ b/drivers/staging/media/omap1/Makefile
@@ -0,0 +1,3 @@
+# Makefile for OMAP1 driver
+
+obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
diff --git a/drivers/staging/media/omap1/TODO b/drivers/staging/media/omap1/TODO
new file mode 100644
index 000000000..1025f9f60
--- /dev/null
+++ b/drivers/staging/media/omap1/TODO
@@ -0,0 +1,8 @@
+This driver is deprecated and will be removed soon unless someone will start
+the work to convert this driver to the vb2 framework and remove the
+soc-camera dependency.
+
+Note that trivial patches will not be accepted anymore, only a full conversion.
+
+If you want to convert this driver, please contact the linux-media mailinglist
+(see http://linuxtv.org/lists.php).
diff --git a/drivers/staging/media/omap1/omap1_camera.c b/drivers/staging/media/omap1/omap1_camera.c
new file mode 100644
index 000000000..bd721e354
--- /dev/null
+++ b/drivers/staging/media/omap1/omap1_camera.c
@@ -0,0 +1,1738 @@
+/*
+ * V4L2 SoC Camera driver for OMAP1 Camera Interface
+ *
+ * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host
+ * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
+ *
+ * Based on PXA SoC camera driver
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * Hardware specific bits initialy based on former work by Matt Callow
+ * drivers/media/platform/omap/omap1510cam.c
+ * Copyright (C) 2006 Matt Callow
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/platform_data/media/omap1_camera.h>
+#include <media/soc_camera.h>
+#include <media/drv-intf/soc_mediabus.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/videobuf-dma-sg.h>
+
+#include <linux/omap-dma.h>
+
+
+#define DRIVER_NAME "omap1-camera"
+#define DRIVER_VERSION "0.0.2"
+
+#define OMAP_DMA_CAMERA_IF_RX 20
+
+/*
+ * ---------------------------------------------------------------------------
+ * OMAP1 Camera Interface registers
+ * ---------------------------------------------------------------------------
+ */
+
+#define REG_CTRLCLOCK 0x00
+#define REG_IT_STATUS 0x04
+#define REG_MODE 0x08
+#define REG_STATUS 0x0C
+#define REG_CAMDATA 0x10
+#define REG_GPIO 0x14
+#define REG_PEAK_COUNTER 0x18
+
+/* CTRLCLOCK bit shifts */
+#define LCLK_EN BIT(7)
+#define DPLL_EN BIT(6)
+#define MCLK_EN BIT(5)
+#define CAMEXCLK_EN BIT(4)
+#define POLCLK BIT(3)
+#define FOSCMOD_SHIFT 0
+#define FOSCMOD_MASK (0x7 << FOSCMOD_SHIFT)
+#define FOSCMOD_12MHz 0x0
+#define FOSCMOD_6MHz 0x2
+#define FOSCMOD_9_6MHz 0x4
+#define FOSCMOD_24MHz 0x5
+#define FOSCMOD_8MHz 0x6
+
+/* IT_STATUS bit shifts */
+#define DATA_TRANSFER BIT(5)
+#define FIFO_FULL BIT(4)
+#define H_DOWN BIT(3)
+#define H_UP BIT(2)
+#define V_DOWN BIT(1)
+#define V_UP BIT(0)
+
+/* MODE bit shifts */
+#define RAZ_FIFO BIT(18)
+#define EN_FIFO_FULL BIT(17)
+#define EN_NIRQ BIT(16)
+#define THRESHOLD_SHIFT 9
+#define THRESHOLD_MASK (0x7f << THRESHOLD_SHIFT)
+#define DMA BIT(8)
+#define EN_H_DOWN BIT(7)
+#define EN_H_UP BIT(6)
+#define EN_V_DOWN BIT(5)
+#define EN_V_UP BIT(4)
+#define ORDERCAMD BIT(3)
+
+#define IRQ_MASK (EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \
+ EN_NIRQ | EN_FIFO_FULL)
+
+/* STATUS bit shifts */
+#define HSTATUS BIT(1)
+#define VSTATUS BIT(0)
+
+/* GPIO bit shifts */
+#define CAM_RST BIT(0)
+
+/* end of OMAP1 Camera Interface registers */
+
+
+#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
+
+#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
+#define FIFO_SHIFT __fls(FIFO_SIZE)
+
+#define DMA_BURST_SHIFT (1 + OMAP_DMA_DATA_BURST_4)
+#define DMA_BURST_SIZE (1 << DMA_BURST_SHIFT)
+
+#define DMA_ELEMENT_SHIFT OMAP_DMA_DATA_TYPE_S32
+#define DMA_ELEMENT_SIZE (1 << DMA_ELEMENT_SHIFT)
+
+#define DMA_FRAME_SHIFT_CONTIG (FIFO_SHIFT - 1)
+#define DMA_FRAME_SHIFT_SG DMA_BURST_SHIFT
+
+#define DMA_FRAME_SHIFT(x) ((x) == OMAP1_CAM_DMA_CONTIG ? \
+ DMA_FRAME_SHIFT_CONTIG : \
+ DMA_FRAME_SHIFT_SG)
+#define DMA_FRAME_SIZE(x) (1 << DMA_FRAME_SHIFT(x))
+#define DMA_SYNC OMAP_DMA_SYNC_FRAME
+#define THRESHOLD_LEVEL DMA_FRAME_SIZE
+
+
+#define MAX_VIDEO_MEM 4 /* arbitrary video memory limit in MB */
+
+
+/*
+ * Structures
+ */
+
+/* buffer for one video frame */
+struct omap1_cam_buf {
+ struct videobuf_buffer vb;
+ u32 code;
+ int inwork;
+ struct scatterlist *sgbuf;
+ int sgcount;
+ int bytes_left;
+ enum videobuf_state result;
+};
+
+struct omap1_cam_dev {
+ struct soc_camera_host soc_host;
+ struct clk *clk;
+
+ unsigned int irq;
+ void __iomem *base;
+
+ int dma_ch;
+
+ struct omap1_cam_platform_data *pdata;
+ struct resource *res;
+ unsigned long pflags;
+ unsigned long camexclk;
+
+ struct list_head capture;
+
+ /* lock used to protect videobuf */
+ spinlock_t lock;
+
+ /* Pointers to DMA buffers */
+ struct omap1_cam_buf *active;
+ struct omap1_cam_buf *ready;
+
+ enum omap1_cam_vb_mode vb_mode;
+ int (*mmap_mapper)(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma);
+
+ u32 reg_cache[0];
+};
+
+
+static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val)
+{
+ pcdev->reg_cache[reg / sizeof(u32)] = val;
+ __raw_writel(val, pcdev->base + reg);
+}
+
+static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache)
+{
+ return !from_cache ? __raw_readl(pcdev->base + reg) :
+ pcdev->reg_cache[reg / sizeof(u32)];
+}
+
+#define CAM_READ(pcdev, reg) \
+ cam_read(pcdev, REG_##reg, false)
+#define CAM_WRITE(pcdev, reg, val) \
+ cam_write(pcdev, REG_##reg, val)
+#define CAM_READ_CACHE(pcdev, reg) \
+ cam_read(pcdev, REG_##reg, true)
+
+/*
+ * Videobuf operations
+ */
+static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ *size = icd->sizeimage;
+
+ if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode))
+ *count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode);
+
+ if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
+
+ dev_dbg(icd->parent,
+ "%s: count=%d, size=%d\n", __func__, *count, *size);
+
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ struct videobuf_buffer *vb = &buf->vb;
+
+ BUG_ON(in_interrupt());
+
+ videobuf_waiton(vq, vb, 0, 0);
+
+ if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ videobuf_dma_contig_free(vq, vb);
+ } else {
+ struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->parent;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ videobuf_dma_unmap(dev, dma);
+ videobuf_dma_free(dma);
+ }
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int omap1_videobuf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ int ret;
+
+ WARN_ON(!list_empty(&vb->queue));
+
+ BUG_ON(NULL == icd->current_fmt);
+
+ buf->inwork = 1;
+
+ if (buf->code != icd->current_fmt->code || vb->field != field ||
+ vb->width != icd->user_width ||
+ vb->height != icd->user_height) {
+ buf->code = icd->current_fmt->code;
+ vb->width = icd->user_width;
+ vb->height = icd->user_height;
+ vb->field = field;
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ }
+
+ vb->size = icd->sizeimage;
+
+ if (vb->baddr && vb->bsize < vb->size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret)
+ goto fail;
+
+ vb->state = VIDEOBUF_PREPARED;
+ }
+ buf->inwork = 0;
+
+ return 0;
+fail:
+ free_buffer(vq, buf, pcdev->vb_mode);
+out:
+ buf->inwork = 0;
+ return ret;
+}
+
+static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ dma_addr_t dma_addr;
+ unsigned int block_size;
+
+ if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ dma_addr = videobuf_to_dma_contig(&buf->vb);
+ block_size = buf->vb.size;
+ } else {
+ if (WARN_ON(!buf->sgbuf)) {
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ dma_addr = sg_dma_address(buf->sgbuf);
+ if (WARN_ON(!dma_addr)) {
+ buf->sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ block_size = sg_dma_len(buf->sgbuf);
+ if (WARN_ON(!block_size)) {
+ buf->sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ if (unlikely(buf->bytes_left < block_size))
+ block_size = buf->bytes_left;
+ if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE - 1))) {
+ dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE);
+ block_size &= ~(DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE - 1);
+ }
+ buf->bytes_left -= block_size;
+ buf->sgcount++;
+ }
+
+ omap_set_dma_dest_params(dma_ch,
+ OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0);
+ omap_set_dma_transfer_params(dma_ch,
+ OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode),
+ block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT),
+ DMA_SYNC, 0, 0);
+}
+
+static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev)
+{
+ struct omap1_cam_buf *buf;
+
+ /*
+ * If there is already a buffer pointed out by the pcdev->ready,
+ * (re)use it, otherwise try to fetch and configure a new one.
+ */
+ buf = pcdev->ready;
+ if (!buf) {
+ if (list_empty(&pcdev->capture))
+ return buf;
+ buf = list_entry(pcdev->capture.next,
+ struct omap1_cam_buf, vb.queue);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ pcdev->ready = buf;
+ list_del_init(&buf->vb.queue);
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, we can safely enter next buffer parameters
+ * into the DMA programming register set after the DMA
+ * has already been activated on the previous buffer
+ */
+ set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode);
+ } else {
+ /*
+ * In SG mode, the above is not safe since there are probably
+ * a bunch of sgbufs from previous sglist still pending.
+ * Instead, mark the sglist fresh for the upcoming
+ * try_next_sgbuf().
+ */
+ buf->sgbuf = NULL;
+ }
+
+ return buf;
+}
+
+static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf)
+{
+ struct scatterlist *sgbuf;
+
+ if (likely(buf->sgbuf)) {
+ /* current sglist is active */
+ if (unlikely(!buf->bytes_left)) {
+ /* indicate sglist complete */
+ sgbuf = NULL;
+ } else {
+ /* process next sgbuf */
+ sgbuf = sg_next(buf->sgbuf);
+ if (WARN_ON(!sgbuf)) {
+ buf->result = VIDEOBUF_ERROR;
+ } else if (WARN_ON(!sg_dma_len(sgbuf))) {
+ sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ }
+ }
+ buf->sgbuf = sgbuf;
+ } else {
+ /* sglist is fresh, initialize it before using */
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+ sgbuf = dma->sglist;
+ if (!(WARN_ON(!sgbuf))) {
+ buf->sgbuf = sgbuf;
+ buf->sgcount = 0;
+ buf->bytes_left = buf->vb.size;
+ buf->result = VIDEOBUF_DONE;
+ }
+ }
+ if (sgbuf)
+ /*
+ * Put our next sgbuf parameters (address, size)
+ * into the DMA programming register set.
+ */
+ set_dma_dest_params(dma_ch, buf, OMAP1_CAM_DMA_SG);
+
+ return sgbuf;
+}
+
+static void start_capture(struct omap1_cam_dev *pcdev)
+{
+ struct omap1_cam_buf *buf = pcdev->active;
+ u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN;
+
+ if (WARN_ON(!buf))
+ return;
+
+ /*
+ * Enable start of frame interrupt, which we will use for activating
+ * our end of frame watchdog when capture actually starts.
+ */
+ mode |= EN_V_UP;
+
+ if (unlikely(ctrlclock & LCLK_EN))
+ /* stop pixel clock before FIFO reset */
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+ /* reset FIFO */
+ CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO);
+
+ omap_start_dma(pcdev->dma_ch);
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, it's a good moment for fetching next sgbuf
+ * from the current sglist and, if available, already putting
+ * its parameters into the DMA programming register set.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ /* (re)enable pixel clock */
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN);
+ /* release FIFO reset */
+ CAM_WRITE(pcdev, MODE, mode);
+}
+
+static void suspend_capture(struct omap1_cam_dev *pcdev)
+{
+ u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+ omap_stop_dma(pcdev->dma_ch);
+}
+
+static void disable_capture(struct omap1_cam_dev *pcdev)
+{
+ u32 mode = CAM_READ_CACHE(pcdev, MODE);
+
+ CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA));
+}
+
+static void omap1_videobuf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct omap1_cam_buf *buf;
+ u32 mode;
+
+ list_add_tail(&vb->queue, &pcdev->capture);
+ vb->state = VIDEOBUF_QUEUED;
+
+ if (pcdev->active) {
+ /*
+ * Capture in progress, so don't touch pcdev->ready even if
+ * empty. Since the transfer of the DMA programming register set
+ * content to the DMA working register set is done automatically
+ * by the DMA hardware, this can pretty well happen while we
+ * are keeping the lock here. Leave fetching it from the queue
+ * to be done when a next DMA interrupt occures instead.
+ */
+ return;
+ }
+
+ WARN_ON(pcdev->ready);
+
+ buf = prepare_next_vb(pcdev);
+ if (WARN_ON(!buf))
+ return;
+
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+
+ dev_dbg(icd->parent,
+ "%s: capture not active, setup FIFO, start DMA\n", __func__);
+ mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
+ mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT;
+ CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA);
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, the above prepare_next_vb() didn't actually
+ * put anything into the DMA programming register set,
+ * so we have to do it now, before activating DMA.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ start_capture(pcdev);
+}
+
+static void omap1_videobuf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct omap1_cam_buf *buf =
+ container_of(vb, struct omap1_cam_buf, vb);
+ struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ switch (vb->state) {
+ case VIDEOBUF_DONE:
+ dev_dbg(dev, "%s (done)\n", __func__);
+ break;
+ case VIDEOBUF_ACTIVE:
+ dev_dbg(dev, "%s (active)\n", __func__);
+ break;
+ case VIDEOBUF_QUEUED:
+ dev_dbg(dev, "%s (queued)\n", __func__);
+ break;
+ case VIDEOBUF_PREPARED:
+ dev_dbg(dev, "%s (prepared)\n", __func__);
+ break;
+ default:
+ dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state);
+ break;
+ }
+
+ free_buffer(vq, buf, pcdev->vb_mode);
+}
+
+static void videobuf_done(struct omap1_cam_dev *pcdev,
+ enum videobuf_state result)
+{
+ struct omap1_cam_buf *buf = pcdev->active;
+ struct videobuf_buffer *vb;
+ struct device *dev = pcdev->soc_host.icd->parent;
+
+ if (WARN_ON(!buf)) {
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ return;
+ }
+
+ if (result == VIDEOBUF_ERROR)
+ suspend_capture(pcdev);
+
+ vb = &buf->vb;
+ if (waitqueue_active(&vb->done)) {
+ if (!pcdev->ready && result != VIDEOBUF_ERROR) {
+ /*
+ * No next buffer has been entered into the DMA
+ * programming register set on time (could be done only
+ * while the previous DMA interurpt was processed, not
+ * later), so the last DMA block, be it a whole buffer
+ * if in CONTIG or its last sgbuf if in SG mode, is
+ * about to be reused by the just autoreinitialized DMA
+ * engine, and overwritten with next frame data. Best we
+ * can do is stopping the capture as soon as possible,
+ * hopefully before the next frame start.
+ */
+ suspend_capture(pcdev);
+ }
+ vb->state = result;
+ v4l2_get_timestamp(&vb->ts);
+ if (result != VIDEOBUF_ERROR)
+ vb->field_count++;
+ wake_up(&vb->done);
+
+ /* shift in next buffer */
+ buf = pcdev->ready;
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+
+ if (!buf) {
+ /*
+ * No next buffer was ready on time (see above), so
+ * indicate error condition to force capture restart or
+ * stop, depending on next buffer already queued or not.
+ */
+ result = VIDEOBUF_ERROR;
+ prepare_next_vb(pcdev);
+
+ buf = pcdev->ready;
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+ }
+ } else if (pcdev->ready) {
+ /*
+ * In both CONTIG and SG mode, the DMA engine has possibly
+ * been already autoreinitialized with the preprogrammed
+ * pcdev->ready buffer. We can either accept this fact
+ * and just swap the buffers, or provoke an error condition
+ * and restart capture. The former seems less intrusive.
+ */
+ dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
+ __func__);
+ pcdev->active = pcdev->ready;
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, we have to make sure that the buffer we
+ * are putting back into the pcdev->ready is marked
+ * fresh.
+ */
+ buf->sgbuf = NULL;
+ }
+ pcdev->ready = buf;
+
+ buf = pcdev->active;
+ } else {
+ /*
+ * No next buffer has been entered into
+ * the DMA programming register set on time.
+ */
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the DMA engine has already been
+ * reinitialized with the current buffer. Best we can do
+ * is not touching it.
+ */
+ dev_dbg(dev,
+ "%s: nobody waiting on videobuf, reuse it\n",
+ __func__);
+ } else {
+ /*
+ * In SG mode, the DMA engine has just been
+ * autoreinitialized with the last sgbuf from the
+ * current list. Restart capture in order to transfer
+ * next frame start into the first sgbuf, not the last
+ * one.
+ */
+ if (result != VIDEOBUF_ERROR) {
+ suspend_capture(pcdev);
+ result = VIDEOBUF_ERROR;
+ }
+ }
+ }
+
+ if (!buf) {
+ dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
+ disable_capture(pcdev);
+ return;
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the current buffer parameters had already
+ * been entered into the DMA programming register set while the
+ * buffer was fetched with prepare_next_vb(), they may have also
+ * been transferred into the runtime set and already active if
+ * the DMA still running.
+ */
+ } else {
+ /* In SG mode, extra steps are required */
+ if (result == VIDEOBUF_ERROR)
+ /* make sure we (re)use sglist from start on error */
+ buf->sgbuf = NULL;
+
+ /*
+ * In any case, enter the next sgbuf parameters into the DMA
+ * programming register set. They will be used either during
+ * nearest DMA autoreinitialization or, in case of an error,
+ * on DMA startup below.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ if (result == VIDEOBUF_ERROR) {
+ dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
+ __func__);
+ start_capture(pcdev);
+ /*
+ * In SG mode, the above also resulted in the next sgbuf
+ * parameters being entered into the DMA programming register
+ * set, making them ready for next DMA autoreinitialization.
+ */
+ }
+
+ /*
+ * Finally, try fetching next buffer.
+ * In CONTIG mode, it will also enter it into the DMA programming
+ * register set, making it ready for next DMA autoreinitialization.
+ */
+ prepare_next_vb(pcdev);
+}
+
+static void dma_isr(int channel, unsigned short status, void *data)
+{
+ struct omap1_cam_dev *pcdev = data;
+ struct omap1_cam_buf *buf = pcdev->active;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ if (WARN_ON(!buf)) {
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ goto out;
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, assume we have just managed to collect the
+ * whole frame, hopefully before our end of frame watchdog is
+ * triggered. Then, all we have to do is disabling the watchdog
+ * for this frame, and calling videobuf_done() with success
+ * indicated.
+ */
+ CAM_WRITE(pcdev, MODE,
+ CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
+ videobuf_done(pcdev, VIDEOBUF_DONE);
+ } else {
+ /*
+ * In SG mode, we have to process every sgbuf from the current
+ * sglist, one after another.
+ */
+ if (buf->sgbuf) {
+ /*
+ * Current sglist not completed yet, try fetching next
+ * sgbuf, hopefully putting it into the DMA programming
+ * register set, making it ready for next DMA
+ * autoreinitialization.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ if (buf->sgbuf)
+ goto out;
+
+ /*
+ * No more sgbufs left in the current sglist. This
+ * doesn't mean that the whole videobuffer is already
+ * complete, but only that the last sgbuf from the
+ * current sglist is about to be filled. It will be
+ * ready on next DMA interrupt, signalled with the
+ * buf->sgbuf set back to NULL.
+ */
+ if (buf->result != VIDEOBUF_ERROR) {
+ /*
+ * Video frame collected without errors so far,
+ * we can prepare for collecting a next one
+ * as soon as DMA gets autoreinitialized
+ * after the current (last) sgbuf is completed.
+ */
+ buf = prepare_next_vb(pcdev);
+ if (!buf)
+ goto out;
+
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ goto out;
+ }
+ }
+ /* end of videobuf */
+ videobuf_done(pcdev, buf->result);
+ }
+
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static irqreturn_t cam_isr(int irq, void *data)
+{
+ struct omap1_cam_dev *pcdev = data;
+ struct device *dev = pcdev->soc_host.icd->parent;
+ struct omap1_cam_buf *buf = pcdev->active;
+ u32 it_status;
+ unsigned long flags;
+
+ it_status = CAM_READ(pcdev, IT_STATUS);
+ if (!it_status)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ if (WARN_ON(!buf)) {
+ dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
+ __func__, it_status);
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ goto out;
+ }
+
+ if (unlikely(it_status & FIFO_FULL)) {
+ dev_warn(dev, "%s: FIFO overflow\n", __func__);
+
+ } else if (it_status & V_DOWN) {
+ /* end of video frame watchdog */
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the watchdog is disabled with
+ * successful DMA end of block interrupt, and reenabled
+ * on next frame start. If we get here, there is nothing
+ * to check, we must be out of sync.
+ */
+ } else {
+ if (buf->sgcount == 2) {
+ /*
+ * If exactly 2 sgbufs from the next sglist have
+ * been programmed into the DMA engine (the
+ * first one already transferred into the DMA
+ * runtime register set, the second one still
+ * in the programming set), then we are in sync.
+ */
+ goto out;
+ }
+ }
+ dev_notice(dev, "%s: unexpected end of video frame\n",
+ __func__);
+
+ } else if (it_status & V_UP) {
+ u32 mode;
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, we need this interrupt every frame
+ * in oredr to reenable our end of frame watchdog.
+ */
+ mode = CAM_READ_CACHE(pcdev, MODE);
+ } else {
+ /*
+ * In SG mode, the below enabled end of frame watchdog
+ * is kept on permanently, so we can turn this one shot
+ * setup off.
+ */
+ mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP;
+ }
+
+ if (!(mode & EN_V_DOWN)) {
+ /* (re)enable end of frame watchdog interrupt */
+ mode |= EN_V_DOWN;
+ }
+ CAM_WRITE(pcdev, MODE, mode);
+ goto out;
+
+ } else {
+ dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
+ __func__, it_status);
+ goto out;
+ }
+
+ videobuf_done(pcdev, VIDEOBUF_ERROR);
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct videobuf_queue_ops omap1_videobuf_ops = {
+ .buf_setup = omap1_videobuf_setup,
+ .buf_prepare = omap1_videobuf_prepare,
+ .buf_queue = omap1_videobuf_queue,
+ .buf_release = omap1_videobuf_release,
+};
+
+
+/*
+ * SOC Camera host operations
+ */
+
+static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
+{
+ /* apply/release camera sensor reset if requested by platform data */
+ if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH)
+ CAM_WRITE(pcdev, GPIO, reset);
+ else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW)
+ CAM_WRITE(pcdev, GPIO, !reset);
+}
+
+static int omap1_cam_add_device(struct soc_camera_device *icd)
+{
+ dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void omap1_cam_remove_device(struct soc_camera_device *icd)
+{
+ dev_dbg(icd->parent,
+ "OMAP1 Camera driver detached from camera %d\n", icd->devnum);
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on OMAP1 camera sensor interface
+ */
+static int omap1_cam_clock_start(struct soc_camera_host *ici)
+{
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 ctrlclock;
+
+ clk_enable(pcdev->clk);
+
+ /* setup sensor clock */
+ ctrlclock = CAM_READ(pcdev, CTRLCLOCK);
+ ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ ctrlclock &= ~FOSCMOD_MASK;
+ switch (pcdev->camexclk) {
+ case 6000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
+ break;
+ case 8000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
+ break;
+ case 9600000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
+ break;
+ case 12000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
+ break;
+ case 24000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
+ default:
+ break;
+ }
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
+
+ /* enable internal clock */
+ ctrlclock |= MCLK_EN;
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ sensor_reset(pcdev, false);
+
+ return 0;
+}
+
+static void omap1_cam_clock_stop(struct soc_camera_host *ici)
+{
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 ctrlclock;
+
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+
+ sensor_reset(pcdev, true);
+
+ /* disable and release system clocks */
+ ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz;
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN);
+
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
+
+ clk_disable(pcdev->clk);
+}
+
+/* Duplicate standard formats based on host capability of byte swapping */
+static const struct soc_mbus_lookup omap1_cam_formats[] = {
+{
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .name = "YUYV",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .name = "YVYU",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .name = "UYVY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .name = "VYUY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .name = "RGB555",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .name = "RGB555X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .name = "RGB565",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .name = "RGB565X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+},
+};
+
+static int omap1_cam_get_formats(struct soc_camera_device *icd,
+ unsigned int idx, struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ int formats = 0, ret;
+ struct v4l2_subdev_mbus_code_enum code = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .index = idx,
+ };
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code.code);
+ if (!fmt) {
+ dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__,
+ idx, code.code);
+ return 0;
+ }
+
+ /* Check support for the requested bits-per-sample */
+ if (fmt->bits_per_sample != 8)
+ return 0;
+
+ switch (code.code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_BE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = soc_mbus_find_fmtdesc(code.code,
+ omap1_cam_formats,
+ ARRAY_SIZE(omap1_cam_formats));
+ xlate->code = code.code;
+ xlate++;
+ dev_dbg(dev,
+ "%s: providing format %s as byte swapped code #%d\n",
+ __func__, xlate->host_fmt->name, code.code);
+ }
+ default:
+ if (xlate)
+ dev_dbg(dev,
+ "%s: providing format %s in pass-through mode\n",
+ __func__, fmt->name);
+ }
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code.code;
+ xlate++;
+ }
+
+ return formats;
+}
+
+static bool is_dma_aligned(s32 bytes_per_line, unsigned int height,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ int size = bytes_per_line * height;
+
+ return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) &&
+ IS_ALIGNED(size, DMA_FRAME_SIZE(vb_mode) * DMA_ELEMENT_SIZE);
+}
+
+static int dma_align(int *width, int *height,
+ const struct soc_mbus_pixelfmt *fmt,
+ enum omap1_cam_vb_mode vb_mode, bool enlarge)
+{
+ s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ if (!is_dma_aligned(bytes_per_line, *height, vb_mode)) {
+ unsigned int pxalign = __fls(bytes_per_line / *width);
+ unsigned int salign = DMA_FRAME_SHIFT(vb_mode) +
+ DMA_ELEMENT_SHIFT - pxalign;
+ unsigned int incr = enlarge << salign;
+
+ v4l_bound_align_image(width, 1, *width + incr, 0,
+ height, 1, *height + incr, 0, salign);
+ return 0;
+ }
+ return 1;
+}
+
+#define subdev_call_with_sense(pcdev, dev, icd, sd, op, function, args...) \
+({ \
+ struct soc_camera_sense sense = { \
+ .master_clock = pcdev->camexclk, \
+ .pixel_clock_max = 0, \
+ }; \
+ int __ret; \
+ \
+ if (pcdev->pdata) \
+ sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \
+ icd->sense = &sense; \
+ __ret = v4l2_subdev_call(sd, op, function, ##args); \
+ icd->sense = NULL; \
+ \
+ if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \
+ if (sense.pixel_clock > sense.pixel_clock_max) { \
+ dev_err(dev, \
+ "%s: pixel clock %lu set by the camera too high!\n", \
+ __func__, sense.pixel_clock); \
+ __ret = -EINVAL; \
+ } \
+ } \
+ __ret; \
+})
+
+static int set_format(struct omap1_cam_dev *pcdev, struct device *dev,
+ struct soc_camera_device *icd, struct v4l2_subdev *sd,
+ struct v4l2_subdev_format *format,
+ const struct soc_camera_format_xlate *xlate)
+{
+ s32 bytes_per_line;
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ int ret = subdev_call_with_sense(pcdev, dev, icd, sd, pad, set_fmt, NULL, format);
+
+ if (ret < 0) {
+ dev_err(dev, "%s: set_fmt failed\n", __func__);
+ return ret;
+ }
+
+ if (mf->code != xlate->code) {
+ dev_err(dev, "%s: unexpected pixel code change\n", __func__);
+ return -EINVAL;
+ }
+
+ bytes_per_line = soc_mbus_bytes_per_line(mf->width, xlate->host_fmt);
+ if (bytes_per_line < 0) {
+ dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n",
+ __func__);
+ return bytes_per_line;
+ }
+
+ if (!is_dma_aligned(bytes_per_line, mf->height, pcdev->vb_mode)) {
+ dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n",
+ __func__, mf->width, mf->height);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int omap1_cam_set_crop(struct soc_camera_device *icd,
+ const struct v4l2_crop *crop)
+{
+ const struct v4l2_rect *rect = &crop->c;
+ const struct soc_camera_format_xlate *xlate = icd->current_fmt;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &fmt.format;
+ int ret;
+
+ ret = subdev_call_with_sense(pcdev, dev, icd, sd, video, s_crop, crop);
+ if (ret < 0) {
+ dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
+ rect->width, rect->height, rect->left, rect->top);
+ return ret;
+ }
+
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
+ if (ret < 0) {
+ dev_warn(dev, "%s: failed to fetch current format\n", __func__);
+ return ret;
+ }
+
+ ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode,
+ false);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+ __func__, mf->width, mf->height,
+ xlate->host_fmt->name);
+ return ret;
+ }
+
+ if (!ret) {
+ /* sensor returned geometry not DMA aligned, trying to fix */
+ ret = set_format(pcdev, dev, icd, sd, &fmt, xlate);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to set format\n", __func__);
+ return ret;
+ }
+ }
+
+ icd->user_width = mf->width;
+ icd->user_height = mf->height;
+
+ return 0;
+}
+
+static int omap1_cam_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(dev, "%s: format %#x not found\n", __func__,
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode,
+ true);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+ __func__, pix->width, pix->height,
+ xlate->host_fmt->name);
+ return ret;
+ }
+
+ ret = set_format(pcdev, dev, icd, sd, &format, xlate);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to set format\n", __func__);
+ return ret;
+ }
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->field = mf->field;
+ pix->colorspace = mf->colorspace;
+ icd->current_fmt = xlate;
+
+ return 0;
+}
+
+static int omap1_cam_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+ struct v4l2_mbus_framefmt *mf = &format.format;
+ int ret;
+ /* TODO: limit to mx1 hardware capabilities */
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %#x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf->width = pix->width;
+ mf->height = pix->height;
+ mf->field = pix->field;
+ mf->colorspace = pix->colorspace;
+ mf->code = xlate->code;
+
+ /* limit to sensor capabilities */
+ ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ pix->field = mf->field;
+ pix->colorspace = mf->colorspace;
+
+ return 0;
+}
+
+static bool sg_mode;
+
+/*
+ * Local mmap_mapper wrapper,
+ * used for detecting videobuf-dma-contig buffer allocation failures
+ * and switching to videobuf-dma-sg automatically for future attempts.
+ */
+static int omap1_cam_mmap_mapper(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma)
+{
+ struct soc_camera_device *icd = q->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ int ret;
+
+ ret = pcdev->mmap_mapper(q, buf, vma);
+
+ if (ret == -ENOMEM)
+ sg_mode = true;
+
+ return ret;
+}
+
+static void omap1_cam_init_videobuf(struct videobuf_queue *q,
+ struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ if (!sg_mode)
+ videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
+ icd->parent, &pcdev->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct omap1_cam_buf), icd, &ici->host_lock);
+ else
+ videobuf_queue_sg_init(q, &omap1_videobuf_ops,
+ icd->parent, &pcdev->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct omap1_cam_buf), icd, &ici->host_lock);
+
+ /* use videobuf mode (auto)selected with the module parameter */
+ pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG;
+
+ /*
+ * Ensure we substitute the videobuf-dma-contig version of the
+ * mmap_mapper() callback with our own wrapper, used for switching
+ * automatically to videobuf-dma-sg on buffer allocation failure.
+ */
+ if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) {
+ pcdev->mmap_mapper = q->int_ops->mmap_mapper;
+ q->int_ops->mmap_mapper = omap1_cam_mmap_mapper;
+ }
+}
+
+static int omap1_cam_reqbufs(struct soc_camera_device *icd,
+ struct v4l2_requestbuffers *p)
+{
+ int i;
+
+ /*
+ * This is for locking debugging only. I removed spinlocks and now I
+ * check whether .prepare is ever called on a linked buffer, or whether
+ * a dma IRQ can occur for an in-work or unlinked buffer. Until now
+ * it hadn't triggered
+ */
+ for (i = 0; i < p->count; i++) {
+ struct omap1_cam_buf *buf = container_of(icd->vb_vidq.bufs[i],
+ struct omap1_cam_buf, vb);
+ buf->inwork = 0;
+ INIT_LIST_HEAD(&buf->vb.queue);
+ }
+
+ return 0;
+}
+
+static int omap1_cam_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the friendly caller:-> */
+ strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int omap1_cam_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+ const struct soc_camera_format_xlate *xlate;
+ const struct soc_mbus_pixelfmt *fmt;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ u32 ctrlclock, mode;
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(dev,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, SOCAM_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = SOCAM_BUS_FLAGS;
+ }
+
+ /* Make choices, possibly based on platform configuration */
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (!pcdev->pdata ||
+ pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ if (ctrlclock & LCLK_EN)
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) {
+ dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
+ ctrlclock |= POLCLK;
+ } else {
+ dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n");
+ ctrlclock &= ~POLCLK;
+ }
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+ if (ctrlclock & LCLK_EN)
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ /* select bus endianness */
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ fmt = xlate->host_fmt;
+
+ mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA);
+ if (fmt->order == SOC_MBUS_ORDER_LE) {
+ dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n");
+ CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD);
+ } else {
+ dev_dbg(dev, "MODE_REG |= ORDERCAMD\n");
+ CAM_WRITE(pcdev, MODE, mode | ORDERCAMD);
+ }
+
+ return 0;
+}
+
+static unsigned int omap1_cam_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct omap1_cam_buf *buf;
+
+ buf = list_entry(icd->vb_vidq.stream.next, struct omap1_cam_buf,
+ vb.stream);
+
+ poll_wait(file, &buf->vb.done, pt);
+
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static struct soc_camera_host_ops omap1_host_ops = {
+ .owner = THIS_MODULE,
+ .add = omap1_cam_add_device,
+ .remove = omap1_cam_remove_device,
+ .clock_start = omap1_cam_clock_start,
+ .clock_stop = omap1_cam_clock_stop,
+ .get_formats = omap1_cam_get_formats,
+ .set_crop = omap1_cam_set_crop,
+ .set_fmt = omap1_cam_set_fmt,
+ .try_fmt = omap1_cam_try_fmt,
+ .init_videobuf = omap1_cam_init_videobuf,
+ .reqbufs = omap1_cam_reqbufs,
+ .querycap = omap1_cam_querycap,
+ .set_bus_param = omap1_cam_set_bus_param,
+ .poll = omap1_cam_poll,
+};
+
+static int omap1_cam_probe(struct platform_device *pdev)
+{
+ struct omap1_cam_dev *pcdev;
+ struct resource *res;
+ struct clk *clk;
+ void __iomem *base;
+ unsigned int irq;
+ int err = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || (int)irq <= 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ clk = clk_get(&pdev->dev, "armper_ck");
+ if (IS_ERR(clk)) {
+ err = PTR_ERR(clk);
+ goto exit;
+ }
+
+ pcdev = kzalloc(sizeof(*pcdev) + resource_size(res), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit_put_clk;
+ }
+
+ pcdev->res = res;
+ pcdev->clk = clk;
+
+ pcdev->pdata = pdev->dev.platform_data;
+ if (pcdev->pdata) {
+ pcdev->pflags = pcdev->pdata->flags;
+ pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
+ }
+
+ switch (pcdev->camexclk) {
+ case 6000000:
+ case 8000000:
+ case 9600000:
+ case 12000000:
+ case 24000000:
+ break;
+ default:
+ /* pcdev->camexclk != 0 => pcdev->pdata != NULL */
+ dev_warn(&pdev->dev,
+ "Incorrect sensor clock frequency %ld kHz, "
+ "should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
+ "please correct your platform data\n",
+ pcdev->pdata->camexclk_khz);
+ pcdev->camexclk = 0;
+ case 0:
+ dev_info(&pdev->dev, "Not providing sensor clock\n");
+ }
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ spin_lock_init(&pcdev->lock);
+
+ /*
+ * Request the region.
+ */
+ if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
+ err = -EBUSY;
+ goto exit_kfree;
+ }
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+ pcdev->irq = irq;
+ pcdev->base = base;
+
+ sensor_reset(pcdev, true);
+
+ err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME,
+ dma_isr, (void *)pcdev, &pcdev->dma_ch);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n");
+ err = -EBUSY;
+ goto exit_iounmap;
+ }
+ dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch);
+
+ /* preconfigure DMA */
+ omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA,
+ 0, 0);
+ omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4);
+ /* setup DMA autoinitialization */
+ omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch);
+
+ err = request_irq(pcdev->irq, cam_isr, 0, DRIVER_NAME, pcdev);
+ if (err) {
+ dev_err(&pdev->dev, "Camera interrupt register failed\n");
+ goto exit_free_dma;
+ }
+
+ pcdev->soc_host.drv_name = DRIVER_NAME;
+ pcdev->soc_host.ops = &omap1_host_ops;
+ pcdev->soc_host.priv = pcdev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
+ pcdev->soc_host.nr = pdev->id;
+
+ err = soc_camera_host_register(&pcdev->soc_host);
+ if (err)
+ goto exit_free_irq;
+
+ dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n");
+
+ return 0;
+
+exit_free_irq:
+ free_irq(pcdev->irq, pcdev);
+exit_free_dma:
+ omap_free_dma(pcdev->dma_ch);
+exit_iounmap:
+ iounmap(base);
+exit_release:
+ release_mem_region(res->start, resource_size(res));
+exit_kfree:
+ kfree(pcdev);
+exit_put_clk:
+ clk_put(clk);
+exit:
+ return err;
+}
+
+static int omap1_cam_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct omap1_cam_dev *pcdev = container_of(soc_host,
+ struct omap1_cam_dev, soc_host);
+ struct resource *res;
+
+ free_irq(pcdev->irq, pcdev);
+
+ omap_free_dma(pcdev->dma_ch);
+
+ soc_camera_host_unregister(soc_host);
+
+ iounmap(pcdev->base);
+
+ res = pcdev->res;
+ release_mem_region(res->start, resource_size(res));
+
+ clk_put(pcdev->clk);
+
+ kfree(pcdev);
+
+ dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n");
+
+ return 0;
+}
+
+static struct platform_driver omap1_cam_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = omap1_cam_probe,
+ .remove = omap1_cam_remove,
+};
+
+module_platform_driver(omap1_cam_driver);
+
+module_param(sg_mode, bool, 0644);
+MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
+
+MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c
index 30b473cfb..c5a5138b3 100644
--- a/drivers/staging/media/omap4iss/iss.c
+++ b/drivers/staging/media/omap4iss/iss.c
@@ -363,215 +363,6 @@ static irqreturn_t iss_isr(int irq, void *_iss)
}
/* -----------------------------------------------------------------------------
- * Pipeline power management
- *
- * Entities must be powered up when part of a pipeline that contains at least
- * one open video device node.
- *
- * To achieve this use the entity use_count field to track the number of users.
- * For entities corresponding to video device nodes the use_count field stores
- * the users count of the node. For entities corresponding to subdevs the
- * use_count field stores the total number of users of all video device nodes
- * in the pipeline.
- *
- * The omap4iss_pipeline_pm_use() function must be called in the open() and
- * close() handlers of video device nodes. It increments or decrements the use
- * count of all subdev entities in the pipeline.
- *
- * To react to link management on powered pipelines, the link setup notification
- * callback updates the use count of all entities in the source and sink sides
- * of the link.
- */
-
-/*
- * iss_pipeline_pm_use_count - Count the number of users of a pipeline
- * @entity: The entity
- *
- * Return the total number of users of all video device nodes in the pipeline.
- */
-static int iss_pipeline_pm_use_count(struct media_entity *entity,
- struct media_entity_graph *graph)
-{
- int use = 0;
-
- media_entity_graph_walk_start(graph, entity);
-
- while ((entity = media_entity_graph_walk_next(graph))) {
- if (is_media_entity_v4l2_io(entity))
- use += entity->use_count;
- }
-
- return use;
-}
-
-/*
- * iss_pipeline_pm_power_one - Apply power change to an entity
- * @entity: The entity
- * @change: Use count change
- *
- * Change the entity use count by @change. If the entity is a subdev update its
- * power state by calling the core::s_power operation when the use count goes
- * from 0 to != 0 or from != 0 to 0.
- *
- * Return 0 on success or a negative error code on failure.
- */
-static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
-{
- struct v4l2_subdev *subdev;
-
- subdev = is_media_entity_v4l2_subdev(entity)
- ? media_entity_to_v4l2_subdev(entity) : NULL;
-
- if (entity->use_count == 0 && change > 0 && subdev) {
- int ret;
-
- ret = v4l2_subdev_call(subdev, core, s_power, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
- }
-
- entity->use_count += change;
- WARN_ON(entity->use_count < 0);
-
- if (entity->use_count == 0 && change < 0 && subdev)
- v4l2_subdev_call(subdev, core, s_power, 0);
-
- return 0;
-}
-
-/*
- * iss_pipeline_pm_power - Apply power change to all entities in a pipeline
- * @entity: The entity
- * @change: Use count change
- *
- * Walk the pipeline to update the use count and the power state of all non-node
- * entities.
- *
- * Return 0 on success or a negative error code on failure.
- */
-static int iss_pipeline_pm_power(struct media_entity *entity, int change,
- struct media_entity_graph *graph)
-{
- struct media_entity *first = entity;
- int ret = 0;
-
- if (!change)
- return 0;
-
- media_entity_graph_walk_start(graph, entity);
-
- while (!ret && (entity = media_entity_graph_walk_next(graph)))
- if (is_media_entity_v4l2_subdev(entity))
- ret = iss_pipeline_pm_power_one(entity, change);
-
- if (!ret)
- return 0;
-
- media_entity_graph_walk_start(graph, first);
-
- while ((first = media_entity_graph_walk_next(graph)) &&
- first != entity)
- if (is_media_entity_v4l2_subdev(first))
- iss_pipeline_pm_power_one(first, -change);
-
- return ret;
-}
-
-/*
- * omap4iss_pipeline_pm_use - Update the use count of an entity
- * @entity: The entity
- * @use: Use (1) or stop using (0) the entity
- *
- * Update the use count of all entities in the pipeline and power entities on or
- * off accordingly.
- *
- * Return 0 on success or a negative error code on failure. Powering entities
- * off is assumed to never fail. No failure can occur when the use parameter is
- * set to 0.
- */
-int omap4iss_pipeline_pm_use(struct media_entity *entity, int use,
- struct media_entity_graph *graph)
-{
- int change = use ? 1 : -1;
- int ret;
-
- mutex_lock(&entity->graph_obj.mdev->graph_mutex);
-
- /* Apply use count to node. */
- entity->use_count += change;
- WARN_ON(entity->use_count < 0);
-
- /* Apply power change to connected non-nodes. */
- ret = iss_pipeline_pm_power(entity, change, graph);
- if (ret < 0)
- entity->use_count -= change;
-
- mutex_unlock(&entity->graph_obj.mdev->graph_mutex);
-
- return ret;
-}
-
-/*
- * iss_pipeline_link_notify - Link management notification callback
- * @link: The link
- * @flags: New link flags that will be applied
- *
- * React to link management on powered pipelines by updating the use count of
- * all entities in the source and sink sides of the link. Entities are powered
- * on or off accordingly.
- *
- * Return 0 on success or a negative error code on failure. Powering entities
- * off is assumed to never fail. This function will not fail for disconnection
- * events.
- */
-static int iss_pipeline_link_notify(struct media_link *link, u32 flags,
- unsigned int notification)
-{
- struct media_entity_graph *graph =
- &container_of(link->graph_obj.mdev, struct iss_device,
- media_dev)->pm_count_graph;
- struct media_entity *source = link->source->entity;
- struct media_entity *sink = link->sink->entity;
- int source_use;
- int sink_use;
- int ret;
-
- if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) {
- ret = media_entity_graph_walk_init(graph,
- link->graph_obj.mdev);
- if (ret)
- return ret;
- }
-
- source_use = iss_pipeline_pm_use_count(source, graph);
- sink_use = iss_pipeline_pm_use_count(sink, graph);
-
- if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
- !(flags & MEDIA_LNK_FL_ENABLED)) {
- /* Powering off entities is assumed to never fail. */
- iss_pipeline_pm_power(source, -sink_use, graph);
- iss_pipeline_pm_power(sink, -source_use, graph);
- return 0;
- }
-
- if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
- (flags & MEDIA_LNK_FL_ENABLED)) {
- ret = iss_pipeline_pm_power(source, sink_use, graph);
- if (ret < 0)
- return ret;
-
- ret = iss_pipeline_pm_power(sink, source_use, graph);
- if (ret < 0)
- iss_pipeline_pm_power(source, -sink_use, graph);
- }
-
- if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH)
- media_entity_graph_walk_cleanup(graph);
-
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
* Pipeline stream management
*/
@@ -1197,7 +988,7 @@ static int iss_register_entities(struct iss_device *iss)
strlcpy(iss->media_dev.model, "TI OMAP4 ISS",
sizeof(iss->media_dev.model));
iss->media_dev.hw_revision = iss->revision;
- iss->media_dev.link_notify = iss_pipeline_link_notify;
+ iss->media_dev.link_notify = v4l2_pipeline_link_notify;
ret = media_device_register(&iss->media_dev);
if (ret < 0) {
dev_err(iss->dev, "Media device registration failed (%d)\n",
@@ -1527,8 +1318,6 @@ error_modules:
error_iss:
omap4iss_put(iss);
error:
- platform_set_drvdata(pdev, NULL);
-
mutex_destroy(&iss->iss_mutex);
return ret;
diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h
index 05f08a3ca..760ee27da 100644
--- a/drivers/staging/media/omap4iss/iss.h
+++ b/drivers/staging/media/omap4iss/iss.h
@@ -15,6 +15,8 @@
#define _OMAP4_ISS_H_
#include <media/v4l2-device.h>
+#include <media/v4l2-mc.h>
+
#include <linux/device.h>
#include <linux/io.h>
#include <linux/platform_device.h>
@@ -87,7 +89,6 @@ struct iss_reg {
struct iss_device {
struct v4l2_device v4l2_dev;
struct media_device media_dev;
- struct media_entity_graph pm_count_graph;
struct device *dev;
u32 revision;
@@ -152,9 +153,6 @@ void omap4iss_isp_subclk_enable(struct iss_device *iss,
void omap4iss_isp_subclk_disable(struct iss_device *iss,
enum iss_isp_subclk_resource res);
-int omap4iss_pipeline_pm_use(struct media_entity *entity, int use,
- struct media_entity_graph *graph);
-
int omap4iss_register_entities(struct platform_device *pdev,
struct v4l2_device *v4l2_dev);
void omap4iss_unregister_entities(struct platform_device *pdev);
diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c
index 058233a9d..f54349bce 100644
--- a/drivers/staging/media/omap4iss/iss_video.c
+++ b/drivers/staging/media/omap4iss/iss_video.c
@@ -19,8 +19,10 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
+
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
#include "iss_video.h"
#include "iss.h"
@@ -1009,13 +1011,7 @@ static int iss_video_open(struct file *file)
goto done;
}
- ret = media_entity_graph_walk_init(&handle->graph,
- &video->iss->media_dev);
- if (ret)
- goto done;
-
- ret = omap4iss_pipeline_pm_use(&video->video.entity, 1,
- &handle->graph);
+ ret = v4l2_pipeline_pm_use(&video->video.entity, 1);
if (ret < 0) {
omap4iss_put(video->iss);
goto done;
@@ -1054,7 +1050,6 @@ static int iss_video_open(struct file *file)
done:
if (ret < 0) {
v4l2_fh_del(&handle->vfh);
- media_entity_graph_walk_cleanup(&handle->graph);
kfree(handle);
}
@@ -1070,13 +1065,11 @@ static int iss_video_release(struct file *file)
/* Disable streaming and free the buffers queue resources. */
iss_video_streamoff(file, vfh, video->type);
- omap4iss_pipeline_pm_use(&video->video.entity, 0, &handle->graph);
+ v4l2_pipeline_pm_use(&video->video.entity, 0);
/* Release the videobuf2 queue */
vb2_queue_release(&handle->queue);
- /* Release the file handle. */
- media_entity_graph_walk_cleanup(&handle->graph);
v4l2_fh_del(vfh);
kfree(handle);
file->private_data = NULL;
diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h
index 34588b717..c8bd2958a 100644
--- a/drivers/staging/media/omap4iss/iss_video.h
+++ b/drivers/staging/media/omap4iss/iss_video.h
@@ -183,7 +183,6 @@ struct iss_video_fh {
struct vb2_queue queue;
struct v4l2_format format;
struct v4l2_fract timeperframe;
- struct media_entity_graph graph;
};
#define to_iss_video_fh(fh) container_of(fh, struct iss_video_fh, vfh)
diff --git a/drivers/staging/media/timb/Kconfig b/drivers/staging/media/timb/Kconfig
new file mode 100644
index 000000000..e413fecc1
--- /dev/null
+++ b/drivers/staging/media/timb/Kconfig
@@ -0,0 +1,11 @@
+config VIDEO_TIMBERDALE
+ tristate "Support for timberdale Video In/LogiWIN"
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+ depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST
+ select VIDEO_ADV7180
+ select VIDEOBUF_DMA_CONTIG
+ ---help---
+ Add support for the Video In peripherial of the timberdale FPGA.
+
+ This driver is deprecated and will be removed soon unless someone
+ will start the work to convert this driver to the vb2 framework.
diff --git a/drivers/staging/media/timb/Makefile b/drivers/staging/media/timb/Makefile
new file mode 100644
index 000000000..4c989c23a
--- /dev/null
+++ b/drivers/staging/media/timb/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
diff --git a/drivers/staging/media/timb/timblogiw.c b/drivers/staging/media/timb/timblogiw.c
new file mode 100644
index 000000000..113c9f3c0
--- /dev/null
+++ b/drivers/staging/media/timb/timblogiw.c
@@ -0,0 +1,870 @@
+/*
+ * timblogiw.c timberdale FPGA LogiWin Video In driver
+ * Copyright (c) 2009-2010 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA LogiWin Video In
+ */
+
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/scatterlist.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf-dma-contig.h>
+#include <linux/platform_data/media/timb_video.h>
+
+#define DRIVER_NAME "timb-video"
+
+#define TIMBLOGIWIN_NAME "Timberdale Video-In"
+#define TIMBLOGIW_VERSION_CODE 0x04
+
+#define TIMBLOGIW_LINES_PER_DESC 44
+#define TIMBLOGIW_MAX_VIDEO_MEM 16
+
+#define TIMBLOGIW_HAS_DECODER(lw) (lw->pdata.encoder.module_name)
+
+
+struct timblogiw {
+ struct video_device video_dev;
+ struct v4l2_device v4l2_dev; /* mutual exclusion */
+ struct mutex lock;
+ struct device *dev;
+ struct timb_video_platform_data pdata;
+ struct v4l2_subdev *sd_enc; /* encoder */
+ bool opened;
+};
+
+struct timblogiw_tvnorm {
+ v4l2_std_id std;
+ u16 width;
+ u16 height;
+ u8 fps;
+};
+
+struct timblogiw_fh {
+ struct videobuf_queue vb_vidq;
+ struct timblogiw_tvnorm const *cur_norm;
+ struct list_head capture;
+ struct dma_chan *chan;
+ spinlock_t queue_lock; /* mutual exclusion */
+ unsigned int frame_count;
+};
+
+struct timblogiw_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+ struct scatterlist sg[16];
+ dma_cookie_t cookie;
+ struct timblogiw_fh *fh;
+};
+
+static const struct timblogiw_tvnorm timblogiw_tvnorms[] = {
+ {
+ .std = V4L2_STD_PAL,
+ .width = 720,
+ .height = 576,
+ .fps = 25
+ },
+ {
+ .std = V4L2_STD_NTSC,
+ .width = 720,
+ .height = 480,
+ .fps = 30
+ }
+};
+
+static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm)
+{
+ return norm->width * 2;
+}
+
+
+static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm)
+{
+ return norm->height * timblogiw_bytes_per_line(norm);
+}
+
+static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
+ if (timblogiw_tvnorms[i].std & std)
+ return timblogiw_tvnorms + i;
+
+ /* default to first element */
+ return timblogiw_tvnorms;
+}
+
+static void timblogiw_dma_cb(void *data)
+{
+ struct timblogiw_buffer *buf = data;
+ struct timblogiw_fh *fh = buf->fh;
+ struct videobuf_buffer *vb = &buf->vb;
+
+ spin_lock(&fh->queue_lock);
+
+ /* mark the transfer done */
+ buf->cookie = -1;
+
+ fh->frame_count++;
+
+ if (vb->state != VIDEOBUF_ERROR) {
+ list_del(&vb->queue);
+ v4l2_get_timestamp(&vb->ts);
+ vb->field_count = fh->frame_count * 2;
+ vb->state = VIDEOBUF_DONE;
+
+ wake_up(&vb->done);
+ }
+
+ if (!list_empty(&fh->capture)) {
+ vb = list_entry(fh->capture.next, struct videobuf_buffer,
+ queue);
+ vb->state = VIDEOBUF_ACTIVE;
+ }
+
+ spin_unlock(&fh->queue_lock);
+}
+
+static bool timblogiw_dma_filter_fn(struct dma_chan *chan, void *filter_param)
+{
+ return chan->chan_id == (uintptr_t)filter_param;
+}
+
+/* IOCTL functions */
+
+static int timblogiw_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s entry\n", __func__);
+
+ if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&lw->lock);
+
+ format->fmt.pix.width = fh->cur_norm->width;
+ format->fmt.pix.height = fh->cur_norm->height;
+ format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ format->fmt.pix.bytesperline = timblogiw_bytes_per_line(fh->cur_norm);
+ format->fmt.pix.sizeimage = timblogiw_frame_size(fh->cur_norm);
+ format->fmt.pix.field = V4L2_FIELD_NONE;
+
+ mutex_unlock(&lw->lock);
+
+ return 0;
+}
+
+static int timblogiw_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_pix_format *pix = &format->fmt.pix;
+
+ dev_dbg(&vdev->dev,
+ "%s - width=%d, height=%d, pixelformat=%d, field=%d\n"
+ "bytes per line %d, size image: %d, colorspace: %d\n",
+ __func__,
+ pix->width, pix->height, pix->pixelformat, pix->field,
+ pix->bytesperline, pix->sizeimage, pix->colorspace);
+
+ if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (pix->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ if (pix->pixelformat != V4L2_PIX_FMT_UYVY)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int timblogiw_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+ struct v4l2_pix_format *pix = &format->fmt.pix;
+ int err;
+
+ mutex_lock(&lw->lock);
+
+ err = timblogiw_try_fmt(file, priv, format);
+ if (err)
+ goto out;
+
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+ dev_err(&vdev->dev, "%s queue busy\n", __func__);
+ err = -EBUSY;
+ goto out;
+ }
+
+ pix->width = fh->cur_norm->width;
+ pix->height = fh->cur_norm->height;
+
+out:
+ mutex_unlock(&lw->lock);
+ return err;
+}
+
+static int timblogiw_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+ strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1);
+ strncpy(cap->driver, DRIVER_NAME, sizeof(cap->driver) - 1);
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vdev->name);
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int timblogiw_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s, index: %d\n", __func__, fmt->index);
+
+ if (fmt->index != 0)
+ return -EINVAL;
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->index = 0;
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strncpy(fmt->description, "4:2:2, packed, YUYV",
+ sizeof(fmt->description)-1);
+ fmt->pixelformat = V4L2_PIX_FMT_UYVY;
+
+ return 0;
+}
+
+static int timblogiw_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *sp)
+{
+ struct timblogiw_fh *fh = priv;
+ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = 1;
+ cp->timeperframe.denominator = fh->cur_norm->fps;
+
+ return 0;
+}
+
+static int timblogiw_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_reqbufs(&fh->vb_vidq, rb);
+}
+
+static int timblogiw_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_querybuf(&fh->vb_vidq, b);
+}
+
+static int timblogiw_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_qbuf(&fh->vb_vidq, b);
+}
+
+static int timblogiw_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
+}
+
+static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ *std = fh->cur_norm->std;
+ return 0;
+}
+
+static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id std)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+ int err = 0;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ mutex_lock(&lw->lock);
+
+ if (TIMBLOGIW_HAS_DECODER(lw))
+ err = v4l2_subdev_call(lw->sd_enc, video, s_std, std);
+
+ if (!err)
+ fh->cur_norm = timblogiw_get_norm(std);
+
+ mutex_unlock(&lw->lock);
+
+ return err;
+}
+
+static int timblogiw_enuminput(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct video_device *vdev = video_devdata(file);
+ int i;
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ if (inp->index != 0)
+ return -EINVAL;
+
+ inp->index = 0;
+
+ strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1);
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ inp->std = 0;
+ for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
+ inp->std |= timblogiw_tvnorms[i].std;
+
+ return 0;
+}
+
+static int timblogiw_g_input(struct file *file, void *priv,
+ unsigned int *input)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ *input = 0;
+
+ return 0;
+}
+
+static int timblogiw_s_input(struct file *file, void *priv, unsigned int input)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ if (input != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int timblogiw_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev_dbg(&vdev->dev, "%s - No capture device\n", __func__);
+ return -EINVAL;
+ }
+
+ fh->frame_count = 0;
+ return videobuf_streamon(&fh->vb_vidq);
+}
+
+static int timblogiw_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s entry\n", __func__);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return videobuf_streamoff(&fh->vb_vidq);
+}
+
+static int timblogiw_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s entry\n", __func__);
+
+ if (TIMBLOGIW_HAS_DECODER(lw))
+ return v4l2_subdev_call(lw->sd_enc, video, querystd, std);
+ else {
+ *std = fh->cur_norm->std;
+ return 0;
+ }
+}
+
+static int timblogiw_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s - index: %d, format: %d\n", __func__,
+ fsize->index, fsize->pixel_format);
+
+ if ((fsize->index != 0) ||
+ (fsize->pixel_format != V4L2_PIX_FMT_UYVY))
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = fh->cur_norm->width;
+ fsize->discrete.height = fh->cur_norm->height;
+
+ return 0;
+}
+
+/* Video buffer functions */
+
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+
+ *size = timblogiw_frame_size(fh->cur_norm);
+
+ if (!*count)
+ *count = 32;
+
+ while (*size * *count > TIMBLOGIW_MAX_VIDEO_MEM * 1024 * 1024)
+ (*count)--;
+
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+ struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
+ vb);
+ unsigned int data_size = timblogiw_frame_size(fh->cur_norm);
+ int err = 0;
+
+ if (vb->baddr && vb->bsize < data_size)
+ /* User provided buffer, but it is too small */
+ return -ENOMEM;
+
+ vb->size = data_size;
+ vb->width = fh->cur_norm->width;
+ vb->height = fh->cur_norm->height;
+ vb->field = field;
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ int i;
+ unsigned int size;
+ unsigned int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
+ timblogiw_bytes_per_line(fh->cur_norm);
+ dma_addr_t addr;
+
+ sg_init_table(buf->sg, ARRAY_SIZE(buf->sg));
+
+ err = videobuf_iolock(vq, vb, NULL);
+ if (err)
+ goto err;
+
+ addr = videobuf_to_dma_contig(vb);
+ for (i = 0, size = 0; size < data_size; i++) {
+ sg_dma_address(buf->sg + i) = addr + size;
+ size += bytes_per_desc;
+ sg_dma_len(buf->sg + i) = (size > data_size) ?
+ (bytes_per_desc - (size - data_size)) :
+ bytes_per_desc;
+ }
+
+ vb->state = VIDEOBUF_PREPARED;
+ buf->cookie = -1;
+ buf->fh = fh;
+ }
+
+ return 0;
+
+err:
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ return err;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+ struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
+ vb);
+ struct dma_async_tx_descriptor *desc;
+ int sg_elems;
+ int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
+ timblogiw_bytes_per_line(fh->cur_norm);
+
+ sg_elems = timblogiw_frame_size(fh->cur_norm) / bytes_per_desc;
+ sg_elems +=
+ (timblogiw_frame_size(fh->cur_norm) % bytes_per_desc) ? 1 : 0;
+
+ if (list_empty(&fh->capture))
+ vb->state = VIDEOBUF_ACTIVE;
+ else
+ vb->state = VIDEOBUF_QUEUED;
+
+ list_add_tail(&vb->queue, &fh->capture);
+
+ spin_unlock_irq(&fh->queue_lock);
+
+ desc = dmaengine_prep_slave_sg(fh->chan,
+ buf->sg, sg_elems, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc) {
+ spin_lock_irq(&fh->queue_lock);
+ list_del_init(&vb->queue);
+ vb->state = VIDEOBUF_PREPARED;
+ return;
+ }
+
+ desc->callback_param = buf;
+ desc->callback = timblogiw_dma_cb;
+
+ buf->cookie = desc->tx_submit(desc);
+
+ spin_lock_irq(&fh->queue_lock);
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+ struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
+ vb);
+
+ videobuf_waiton(vq, vb, 0, 0);
+ if (buf->cookie >= 0)
+ dma_sync_wait(fh->chan, buf->cookie);
+
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops timblogiw_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* Device Operations functions */
+
+static int timblogiw_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh;
+ v4l2_std_id std;
+ dma_cap_mask_t mask;
+ int err = 0;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ mutex_lock(&lw->lock);
+ if (lw->opened) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (TIMBLOGIW_HAS_DECODER(lw) && !lw->sd_enc) {
+ struct i2c_adapter *adapt;
+
+ /* find the video decoder */
+ adapt = i2c_get_adapter(lw->pdata.i2c_adapter);
+ if (!adapt) {
+ dev_err(&vdev->dev, "No I2C bus #%d\n",
+ lw->pdata.i2c_adapter);
+ err = -ENODEV;
+ goto out;
+ }
+
+ /* now find the encoder */
+ lw->sd_enc = v4l2_i2c_new_subdev_board(&lw->v4l2_dev, adapt,
+ lw->pdata.encoder.info, NULL);
+
+ i2c_put_adapter(adapt);
+
+ if (!lw->sd_enc) {
+ dev_err(&vdev->dev, "Failed to get encoder: %s\n",
+ lw->pdata.encoder.module_name);
+ err = -ENODEV;
+ goto out;
+ }
+ }
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (!fh) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ fh->cur_norm = timblogiw_tvnorms;
+ timblogiw_querystd(file, fh, &std);
+ fh->cur_norm = timblogiw_get_norm(std);
+
+ INIT_LIST_HEAD(&fh->capture);
+ spin_lock_init(&fh->queue_lock);
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+
+ /* find the DMA channel */
+ fh->chan = dma_request_channel(mask, timblogiw_dma_filter_fn,
+ (void *)(uintptr_t)lw->pdata.dma_channel);
+ if (!fh->chan) {
+ dev_err(&vdev->dev, "Failed to get DMA channel\n");
+ kfree(fh);
+ err = -ENODEV;
+ goto out;
+ }
+
+ file->private_data = fh;
+ videobuf_queue_dma_contig_init(&fh->vb_vidq,
+ &timblogiw_video_qops, lw->dev, &fh->queue_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct timblogiw_buffer), fh, NULL);
+
+ lw->opened = true;
+out:
+ mutex_unlock(&lw->lock);
+
+ return err;
+}
+
+static int timblogiw_close(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ videobuf_stop(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vidq);
+
+ dma_release_channel(fh->chan);
+
+ kfree(fh);
+
+ mutex_lock(&lw->lock);
+ lw->opened = false;
+ mutex_unlock(&lw->lock);
+ return 0;
+}
+
+static ssize_t timblogiw_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int timblogiw_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_poll_stream(file, &fh->vb_vidq, wait);
+}
+
+static int timblogiw_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_mmap_mapper(&fh->vb_vidq, vma);
+}
+
+/* Platform device functions */
+
+static struct v4l2_ioctl_ops timblogiw_ioctl_ops = {
+ .vidioc_querycap = timblogiw_querycap,
+ .vidioc_enum_fmt_vid_cap = timblogiw_enum_fmt,
+ .vidioc_g_fmt_vid_cap = timblogiw_g_fmt,
+ .vidioc_try_fmt_vid_cap = timblogiw_try_fmt,
+ .vidioc_s_fmt_vid_cap = timblogiw_s_fmt,
+ .vidioc_g_parm = timblogiw_g_parm,
+ .vidioc_reqbufs = timblogiw_reqbufs,
+ .vidioc_querybuf = timblogiw_querybuf,
+ .vidioc_qbuf = timblogiw_qbuf,
+ .vidioc_dqbuf = timblogiw_dqbuf,
+ .vidioc_g_std = timblogiw_g_std,
+ .vidioc_s_std = timblogiw_s_std,
+ .vidioc_enum_input = timblogiw_enuminput,
+ .vidioc_g_input = timblogiw_g_input,
+ .vidioc_s_input = timblogiw_s_input,
+ .vidioc_streamon = timblogiw_streamon,
+ .vidioc_streamoff = timblogiw_streamoff,
+ .vidioc_querystd = timblogiw_querystd,
+ .vidioc_enum_framesizes = timblogiw_enum_framesizes,
+};
+
+static struct v4l2_file_operations timblogiw_fops = {
+ .owner = THIS_MODULE,
+ .open = timblogiw_open,
+ .release = timblogiw_close,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = timblogiw_mmap,
+ .read = timblogiw_read,
+ .poll = timblogiw_poll,
+};
+
+static struct video_device timblogiw_template = {
+ .name = TIMBLOGIWIN_NAME,
+ .fops = &timblogiw_fops,
+ .ioctl_ops = &timblogiw_ioctl_ops,
+ .release = video_device_release_empty,
+ .minor = -1,
+ .tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC
+};
+
+static int timblogiw_probe(struct platform_device *pdev)
+{
+ int err;
+ struct timblogiw *lw = NULL;
+ struct timb_video_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data\n");
+ err = -EINVAL;
+ goto err;
+ }
+
+ if (!pdata->encoder.module_name)
+ dev_info(&pdev->dev, "Running without decoder\n");
+
+ lw = devm_kzalloc(&pdev->dev, sizeof(*lw), GFP_KERNEL);
+ if (!lw) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ if (pdev->dev.parent)
+ lw->dev = pdev->dev.parent;
+ else
+ lw->dev = &pdev->dev;
+
+ memcpy(&lw->pdata, pdata, sizeof(lw->pdata));
+
+ mutex_init(&lw->lock);
+
+ lw->video_dev = timblogiw_template;
+
+ strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name));
+ err = v4l2_device_register(NULL, &lw->v4l2_dev);
+ if (err)
+ goto err;
+
+ lw->video_dev.v4l2_dev = &lw->v4l2_dev;
+
+ platform_set_drvdata(pdev, lw);
+ video_set_drvdata(&lw->video_dev, lw);
+
+ err = video_register_device(&lw->video_dev, VFL_TYPE_GRABBER, 0);
+ if (err) {
+ dev_err(&pdev->dev, "Error reg video: %d\n", err);
+ goto err_request;
+ }
+
+ return 0;
+
+err_request:
+ v4l2_device_unregister(&lw->v4l2_dev);
+err:
+ dev_err(&pdev->dev, "Failed to register: %d\n", err);
+
+ return err;
+}
+
+static int timblogiw_remove(struct platform_device *pdev)
+{
+ struct timblogiw *lw = platform_get_drvdata(pdev);
+
+ video_unregister_device(&lw->video_dev);
+
+ v4l2_device_unregister(&lw->v4l2_dev);
+
+ return 0;
+}
+
+static struct platform_driver timblogiw_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = timblogiw_probe,
+ .remove = timblogiw_remove,
+};
+
+module_platform_driver(timblogiw_platform_driver);
+
+MODULE_DESCRIPTION(TIMBLOGIWIN_NAME);
+MODULE_AUTHOR("Pelagicore AB <info@pelagicore.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:"DRIVER_NAME);