From e51daefc228aa164adcc17fe8fce0f856ad0a1cc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 May 2014 17:39:16 -0300 Subject: [media] v4l: vsp1: Remove the unneeded vsp1_video_buffer video field The field is assigned but never read, remove it. This fixes a bug caused by the struct vb2_buffer field not being be the very first field of the vsp1_video_buffer buffer structure as required by videobuf2. Cc: stable@vger.kernel.org Reported-by: Takanari Hayama Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 8a1253e..677e3aa 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -654,8 +654,6 @@ static int vsp1_video_buffer_prepare(struct vb2_buffer *vb) if (vb->num_planes < format->num_planes) return -EINVAL; - buf->video = video; - for (i = 0; i < vb->num_planes; ++i) { buf->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); buf->length[i] = vb2_plane_size(vb, i); diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index c04d48f..7284320 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -90,7 +90,6 @@ static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e) } struct vsp1_video_buffer { - struct vsp1_video *video; struct vb2_buffer buf; struct list_head queue; -- cgit v0.10.2 From 619c027da030c532fb9a94911a117ffaa5d844b6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 17 Mar 2014 21:26:33 -0300 Subject: [media] drxd: get rid of EXPORT_SYMBOL(drxd_config_i2c) This symbol is not used externally. Get rid of it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/drxd.h b/drivers/media/dvb-frontends/drxd.h index 5f1d6b5..d998e4d 100644 --- a/drivers/media/dvb-frontends/drxd.h +++ b/drivers/media/dvb-frontends/drxd.h @@ -69,5 +69,4 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config, } #endif -extern int drxd_config_i2c(struct dvb_frontend *, int); #endif diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index 5b87ece..ae2276d 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -2840,7 +2840,7 @@ static int drxd_init(struct dvb_frontend *fe) return err; } -int drxd_config_i2c(struct dvb_frontend *fe, int onoff) +static int drxd_config_i2c(struct dvb_frontend *fe, int onoff) { struct drxd_state *state = fe->demodulator_priv; @@ -2849,7 +2849,6 @@ int drxd_config_i2c(struct dvb_frontend *fe, int onoff) return DRX_ConfigureI2CBridge(state, onoff); } -EXPORT_SYMBOL(drxd_config_i2c); static int drxd_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *sets) -- cgit v0.10.2 From 4015bef6495f5153fccbb8d4476f71511af82f4d Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Fri, 16 May 2014 10:33:47 -0300 Subject: [media] media: davinci: vpif_capture: drop unneeded module params Remove bogus 'numbuffers' and 'bufsize' module options. The number of buffers and buffer sizes are determined by VIDIOC_REQBUFS and VIDIOC_S_FMT and the amount of available memory (in the case of the MMAP stream I/O mode) and not by module options. These module params are a left-over from the original montavista code that used these parameters to pre-allocate the memory needed for the buffers. The code that allocated those buffers was never upstreamed since by the time the drivers were added to the kernel the TI cmem module could be used in combination with the USERPTR mode to reserve and pass physically contiguous memory pointers around. These days of course CMA is used instead of cmem. This patch removes these module options altogether since they no longer do what they originally were designed for. They should never have been part of the upstreamed code in the first place, so they've been pointless ever since 2.6.32 when this driver first appeared in the mainline kernel. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index a7ed164..cb746e9 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -39,32 +39,10 @@ MODULE_VERSION(VPIF_CAPTURE_VERSION); v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg) static int debug = 1; -static u32 ch0_numbuffers = 3; -static u32 ch1_numbuffers = 3; -static u32 ch0_bufsize = 1920 * 1080 * 2; -static u32 ch1_bufsize = 720 * 576 * 2; module_param(debug, int, 0644); -module_param(ch0_numbuffers, uint, S_IRUGO); -module_param(ch1_numbuffers, uint, S_IRUGO); -module_param(ch0_bufsize, uint, S_IRUGO); -module_param(ch1_bufsize, uint, S_IRUGO); MODULE_PARM_DESC(debug, "Debug level 0-1"); -MODULE_PARM_DESC(ch2_numbuffers, "Channel0 buffer count (default:3)"); -MODULE_PARM_DESC(ch3_numbuffers, "Channel1 buffer count (default:3)"); -MODULE_PARM_DESC(ch2_bufsize, "Channel0 buffer size (default:1920 x 1080 x 2)"); -MODULE_PARM_DESC(ch3_bufsize, "Channel1 buffer size (default:720 x 576 x 2)"); - -static struct vpif_config_params config_params = { - .min_numbuffers = 3, - .numbuffers[0] = 3, - .numbuffers[1] = 3, - .min_bufsize[0] = 720 * 480 * 2, - .min_bufsize[1] = 720 * 480 * 2, - .channel_bufsize[0] = 1920 * 1080 * 2, - .channel_bufsize[1] = 720 * 576 * 2, -}; #define VPIF_DRIVER_NAME "vpif_capture" @@ -610,9 +588,6 @@ static void vpif_config_format(struct channel_obj *ch) vpif_dbg(2, debug, "vpif_config_format\n"); common->fmt.fmt.pix.field = V4L2_FIELD_ANY; - common->fmt.fmt.pix.sizeimage - = config_params.channel_bufsize[ch->channel_id]; - if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; else @@ -1410,36 +1385,9 @@ static struct v4l2_file_operations vpif_fops = { */ static int initialize_vpif(void) { - int err = 0, i, j; + int err, i, j; int free_channel_objects_index; - /* Default number of buffers should be 3 */ - if ((ch0_numbuffers > 0) && - (ch0_numbuffers < config_params.min_numbuffers)) - ch0_numbuffers = config_params.min_numbuffers; - if ((ch1_numbuffers > 0) && - (ch1_numbuffers < config_params.min_numbuffers)) - ch1_numbuffers = config_params.min_numbuffers; - - /* Set buffer size to min buffers size if it is invalid */ - if (ch0_bufsize < config_params.min_bufsize[VPIF_CHANNEL0_VIDEO]) - ch0_bufsize = - config_params.min_bufsize[VPIF_CHANNEL0_VIDEO]; - if (ch1_bufsize < config_params.min_bufsize[VPIF_CHANNEL1_VIDEO]) - ch1_bufsize = - config_params.min_bufsize[VPIF_CHANNEL1_VIDEO]; - - config_params.numbuffers[VPIF_CHANNEL0_VIDEO] = ch0_numbuffers; - config_params.numbuffers[VPIF_CHANNEL1_VIDEO] = ch1_numbuffers; - if (ch0_numbuffers) { - config_params.channel_bufsize[VPIF_CHANNEL0_VIDEO] - = ch0_bufsize; - } - if (ch1_numbuffers) { - config_params.channel_bufsize[VPIF_CHANNEL1_VIDEO] - = ch1_bufsize; - } - /* Allocate memory for six channel objects */ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { vpif_obj.dev[i] = diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h index 1ee1782..f65d28d 100644 --- a/drivers/media/platform/davinci/vpif_capture.h +++ b/drivers/media/platform/davinci/vpif_capture.h @@ -119,15 +119,4 @@ struct vpif_device { struct vpif_capture_config *config; }; -struct vpif_config_params { - u8 min_numbuffers; - u8 numbuffers[VPIF_CAPTURE_NUM_CHANNELS]; - s8 device_type; - u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS]; - u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS]; - u8 default_device[VPIF_CAPTURE_NUM_CHANNELS]; - u32 video_limit[VPIF_CAPTURE_NUM_CHANNELS]; - u8 max_device_type; -}; - #endif /* VPIF_CAPTURE_H */ -- cgit v0.10.2 From a4965c592e3323967170daa8fdde798b6f84e8ee Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Fri, 16 May 2014 10:33:54 -0300 Subject: [media] media: davinci: vpif_capture: fix v4l-compliance issues This patch does the following: 1: sets initial default format during probe. 2: removes spurious messages. 3: optimize vpif_s/try_fmt_vid_out code. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index cb746e9..ee0a5fc 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -498,10 +498,28 @@ static int vpif_update_std_info(struct channel_obj *ch) common->width = std_info->width; common->fmt.fmt.pix.height = std_info->height; common->height = std_info->height; + common->fmt.fmt.pix.sizeimage = common->height * common->width * 2; common->fmt.fmt.pix.bytesperline = std_info->width; vpifparams->video_params.hpitch = std_info->width; vpifparams->video_params.storage_mode = std_info->frm_fmt; + if (vid_ch->stdid) + common->fmt.fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + else + common->fmt.fmt.pix.colorspace = V4L2_COLORSPACE_REC709; + + if (ch->vpifparams.std_info.frm_fmt) + common->fmt.fmt.pix.field = V4L2_FIELD_NONE; + else + common->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + + if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) + common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + else + common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; + + common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; } @@ -578,24 +596,6 @@ static void vpif_calculate_offsets(struct channel_obj *ch) } /** - * vpif_config_format: configure default frame format in the device - * ch : ptr to channel object - */ -static void vpif_config_format(struct channel_obj *ch) -{ - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - - vpif_dbg(2, debug, "vpif_config_format\n"); - - common->fmt.fmt.pix.field = V4L2_FIELD_ANY; - if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) - common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; - else - common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; - common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -} - -/** * vpif_get_default_field() - Get default field type based on interface * @vpif_params - ptr to vpif params */ @@ -607,112 +607,6 @@ static inline enum v4l2_field vpif_get_default_field( } /** - * vpif_check_format() - check given pixel format for compatibility - * @ch - channel ptr - * @pixfmt - Given pixel format - * @update - update the values as per hardware requirement - * - * Check the application pixel format for S_FMT and update the input - * values as per hardware limits for TRY_FMT. The default pixel and - * field format is selected based on interface type. - */ -static int vpif_check_format(struct channel_obj *ch, - struct v4l2_pix_format *pixfmt, - int update) -{ - struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); - struct vpif_params *vpif_params = &ch->vpifparams; - enum v4l2_field field = pixfmt->field; - u32 sizeimage, hpitch, vpitch; - int ret = -EINVAL; - - vpif_dbg(2, debug, "vpif_check_format\n"); - /** - * first check for the pixel format. If if_type is Raw bayer, - * only V4L2_PIX_FMT_SBGGR8 format is supported. Otherwise only - * V4L2_PIX_FMT_YUV422P is supported - */ - if (vpif_params->iface.if_type == VPIF_IF_RAW_BAYER) { - if (pixfmt->pixelformat != V4L2_PIX_FMT_SBGGR8) { - if (!update) { - vpif_dbg(2, debug, "invalid pix format\n"); - goto exit; - } - pixfmt->pixelformat = V4L2_PIX_FMT_SBGGR8; - } - } else { - if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) { - if (!update) { - vpif_dbg(2, debug, "invalid pixel format\n"); - goto exit; - } - pixfmt->pixelformat = V4L2_PIX_FMT_YUV422P; - } - } - - if (!(VPIF_VALID_FIELD(field))) { - if (!update) { - vpif_dbg(2, debug, "invalid field format\n"); - goto exit; - } - /** - * By default use FIELD_NONE for RAW Bayer capture - * and FIELD_INTERLACED for other interfaces - */ - field = vpif_get_default_field(&vpif_params->iface); - } else if (field == V4L2_FIELD_ANY) - /* unsupported field. Use default */ - field = vpif_get_default_field(&vpif_params->iface); - - /* validate the hpitch */ - hpitch = pixfmt->bytesperline; - if (hpitch < vpif_params->std_info.width) { - if (!update) { - vpif_dbg(2, debug, "invalid hpitch\n"); - goto exit; - } - hpitch = vpif_params->std_info.width; - } - - sizeimage = pixfmt->sizeimage; - - vpitch = sizeimage / (hpitch * 2); - - /* validate the vpitch */ - if (vpitch < vpif_params->std_info.height) { - if (!update) { - vpif_dbg(2, debug, "Invalid vpitch\n"); - goto exit; - } - vpitch = vpif_params->std_info.height; - } - - /* Check for 8 byte alignment */ - if (!ALIGN(hpitch, 8)) { - if (!update) { - vpif_dbg(2, debug, "invalid pitch alignment\n"); - goto exit; - } - /* adjust to next 8 byte boundary */ - hpitch = (((hpitch + 7) / 8) * 8); - } - /* if update is set, modify the bytesperline and sizeimage */ - if (update) { - pixfmt->bytesperline = hpitch; - pixfmt->sizeimage = hpitch * vpitch * 2; - } - /** - * Image width and height is always based on current standard width and - * height - */ - pixfmt->width = common->fmt.fmt.pix.width; - pixfmt->height = common->fmt.fmt.pix.height; - return 0; -exit: - return ret; -} - -/** * vpif_config_addr() - function to configure buffer address in vpif * @ch - channel ptr * @muxmode - channel mux mode @@ -922,9 +816,6 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) return -EINVAL; } - /* Configure the default format information */ - vpif_config_format(ch); - /* set standard in the sub device */ ret = v4l2_subdev_call(ch->sd, video, s_std, std_id); if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) { @@ -951,10 +842,8 @@ static int vpif_enum_input(struct file *file, void *priv, chan_cfg = &config->chan_config[ch->channel_id]; - if (input->index >= chan_cfg->input_count) { - vpif_dbg(1, debug, "Invalid input index\n"); + if (input->index >= chan_cfg->input_count) return -EINVAL; - } memcpy(input, &chan_cfg->inputs[input->index].input, sizeof(*input)); @@ -1043,8 +932,34 @@ static int vpif_try_fmt_vid_cap(struct file *file, void *priv, struct video_device *vdev = video_devdata(file); struct channel_obj *ch = video_get_drvdata(vdev); struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); + struct vpif_params *vpif_params = &ch->vpifparams; + + /* + * to supress v4l-compliance warnings silently correct + * the pixelformat + */ + if (vpif_params->iface.if_type == VPIF_IF_RAW_BAYER) { + if (pixfmt->pixelformat != V4L2_PIX_FMT_SBGGR8) + pixfmt->pixelformat = V4L2_PIX_FMT_SBGGR8; + } else { + if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) + pixfmt->pixelformat = V4L2_PIX_FMT_YUV422P; + } + + common->fmt.fmt.pix.pixelformat = pixfmt->pixelformat; - return vpif_check_format(ch, pixfmt, 1); + vpif_update_std_info(ch); + + pixfmt->field = common->fmt.fmt.pix.field; + pixfmt->colorspace = common->fmt.fmt.pix.colorspace; + pixfmt->bytesperline = common->fmt.fmt.pix.width; + pixfmt->width = common->fmt.fmt.pix.width; + pixfmt->height = common->fmt.fmt.pix.height; + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height * 2; + pixfmt->priv = 0; + + return 0; } @@ -1082,20 +997,17 @@ static int vpif_s_fmt_vid_cap(struct file *file, void *priv, struct video_device *vdev = video_devdata(file); struct channel_obj *ch = video_get_drvdata(vdev); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - struct v4l2_pix_format *pixfmt; - int ret = 0; + int ret; vpif_dbg(2, debug, "%s\n", __func__); if (vb2_is_busy(&common->buffer_queue)) return -EBUSY; - pixfmt = &fmt->fmt.pix; - /* Check for valid field format */ - ret = vpif_check_format(ch, pixfmt, 0); - + ret = vpif_try_fmt_vid_cap(file, priv, fmt); if (ret) return ret; + /* store the format in the channel object */ common->fmt = *fmt; return 0; @@ -1443,6 +1355,11 @@ static int vpif_probe_complete(void) if (err) goto probe_out; + /* set initial format */ + ch->video.stdid = V4L2_STD_525_60; + memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); + vpif_update_std_info(ch); + /* Initialize vb2 queue */ q = &common->buffer_queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -- cgit v0.10.2 From f8d36b8960019da9ba46721bd541af61d7497a00 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 12 May 2014 08:30:55 -0300 Subject: [media] smiapp: I2C address is the last part of the subdev name The I2C address of the sensor device was in the middle of the sub-device name and not in the end as it should have been. The smiapp sub-device names will change from e.g. "vs6555 1-0010 pixel array" to "vs6555 pixel array 1-0010". Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 06fb032..446c82c 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2544,9 +2544,9 @@ static int smiapp_registered(struct v4l2_subdev *subdev) } snprintf(this->sd.name, - sizeof(this->sd.name), "%s %d-%4.4x %s", - sensor->minfo.name, i2c_adapter_id(client->adapter), - client->addr, _this->name); + sizeof(this->sd.name), "%s %s %d-%4.4x", + sensor->minfo.name, _this->name, + i2c_adapter_id(client->adapter), client->addr); this->sink_fmt.width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; -- cgit v0.10.2 From ebdae6507b00c77f51b0c3ef70d25f65699f5644 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Fri, 23 May 2014 17:47:07 -0300 Subject: [media] usbtv: fix leak at failure path in usbtv_probe() Error handling code in usbtv_probe() misses usb_put_dev(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Acked-by: Lubomir Rintel Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index 2f87ddf..473fab8 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -91,6 +91,8 @@ static int usbtv_probe(struct usb_interface *intf, return 0; usbtv_video_fail: + usb_set_intfdata(intf, NULL); + usb_put_dev(usbtv->udev); kfree(usbtv); return ret; -- cgit v0.10.2 From 3c1e7f27561038d683f519a5fb723743e7b8e83b Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 24 May 2014 02:03:16 -0300 Subject: [media] m2m-deinterlace: Convert to devm* API Replace resource handling in the driver with managed device resource. Signed-off-by: Alexander Shiyan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index c21d14f..d36c507 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -1002,7 +1002,7 @@ static int deinterlace_probe(struct platform_device *pdev) dma_cap_mask_t mask; int ret = 0; - pcdev = kzalloc(sizeof *pcdev, GFP_KERNEL); + pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); if (!pcdev) return -ENOMEM; @@ -1012,7 +1012,7 @@ static int deinterlace_probe(struct platform_device *pdev) dma_cap_set(DMA_INTERLEAVE, mask); pcdev->dma_chan = dma_request_channel(mask, NULL, pcdev); if (!pcdev->dma_chan) - goto free_dev; + return -ENODEV; if (!dma_has_cap(DMA_INTERLEAVE, pcdev->dma_chan->device->cap_mask)) { v4l2_err(&pcdev->v4l2_dev, "DMA does not support INTERLEAVE\n"); @@ -1078,8 +1078,6 @@ unreg_dev: v4l2_device_unregister(&pcdev->v4l2_dev); rel_dma: dma_release_channel(pcdev->dma_chan); -free_dev: - kfree(pcdev); return ret; } @@ -1094,7 +1092,6 @@ static int deinterlace_remove(struct platform_device *pdev) v4l2_device_unregister(&pcdev->v4l2_dev); vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); dma_release_channel(pcdev->dma_chan); - kfree(pcdev); return 0; } -- cgit v0.10.2 From 3a370222b0c514360863ec79c1481dffbbf1ddab Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 24 May 2014 06:35:57 -0300 Subject: [media] lmedm04: rs2000 check if interrupt urb is over due Change handling of signal_lock on rs2000. Use ibuf[2] to detect lock as there is a longer wait for lock to appear in ibuf[6]. Remove last_key and key_timeout and use jiffies plus 60ms to detect that streaming is still active. If the current jiffies is time_after the interrupt urb overdue and clear signal lock. This results in far faster recovery of lock and streaming. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index f674dc0..7d685bc 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -125,14 +125,13 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define TUNER_RS2000 0x4 struct lme2510_state { + unsigned long int_urb_due; u8 id; u8 tuner_config; u8 signal_lock; u8 signal_level; u8 signal_sn; u8 time_key; - u8 last_key; - u8 key_timeout; u8 i2c_talk_onoff; u8 i2c_gate; u8 i2c_tuner_gate_w; @@ -323,7 +322,7 @@ static void lme2510_int_response(struct urb *lme_urb) } break; case TUNER_RS2000: - if (ibuf[1] == 0x3 && ibuf[6] == 0xff) + if (ibuf[2] & 0x1) st->signal_lock = 0xff; else st->signal_lock = 0x00; @@ -343,7 +342,12 @@ static void lme2510_int_response(struct urb *lme_urb) break; } } + usb_submit_urb(lme_urb, GFP_ATOMIC); + + /* interrupt urb is due every 48 msecs while streaming + * add 12msecs for system lag */ + st->int_urb_due = jiffies + msecs_to_jiffies(60); } static int lme2510_int_read(struct dvb_usb_adapter *adap) @@ -584,14 +588,13 @@ static int lme2510_msg(struct dvb_usb_device *d, switch (wbuf[3]) { case 0x8c: rbuf[0] = 0x55; - rbuf[1] = 0xff; - if (st->last_key == st->time_key) { - st->key_timeout++; - if (st->key_timeout > 5) - rbuf[1] = 0; - } else - st->key_timeout = 0; - st->last_key = st->time_key; + rbuf[1] = st->signal_lock; + + /* If int_urb_due overdue + * set rbuf[1] to 0 to clear lock */ + if (time_after(jiffies, st->int_urb_due)) + rbuf[1] = 0; + break; default: lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); -- cgit v0.10.2 From 4647f487083ded0d42a61bf2e066ce5328624ed3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 29 May 2014 09:20:13 -0300 Subject: [media] dvbdev: add a dvb_detach() macro The dvb_attach() was unbalanced, as there was no dvb_dettach. Ok, on current cases, the dettach is done by dvbdev, but that are some future corner cases where we may need to do this before registering the frontend. So, add a dvb_detach() and use it at dvb_frontend.c. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 6ce435a..6cc2631 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -2666,20 +2666,20 @@ void dvb_frontend_detach(struct dvb_frontend* fe) if (fe->ops.release_sec) { fe->ops.release_sec(fe); - symbol_put_addr(fe->ops.release_sec); + dvb_detach(fe->ops.release_sec); } if (fe->ops.tuner_ops.release) { fe->ops.tuner_ops.release(fe); - symbol_put_addr(fe->ops.tuner_ops.release); + dvb_detach(fe->ops.tuner_ops.release); } if (fe->ops.analog_ops.release) { fe->ops.analog_ops.release(fe); - symbol_put_addr(fe->ops.analog_ops.release); + dvb_detach(fe->ops.analog_ops.release); } ptr = (void*)fe->ops.release; if (ptr) { fe->ops.release(fe); - symbol_put_addr(ptr); + dvb_detach(ptr); } } #else diff --git a/drivers/media/dvb-core/dvbdev.h b/drivers/media/dvb-core/dvbdev.h index 93a9470..f96b28e 100644 --- a/drivers/media/dvb-core/dvbdev.h +++ b/drivers/media/dvb-core/dvbdev.h @@ -136,11 +136,15 @@ extern int dvb_usercopy(struct file *file, unsigned int cmd, unsigned long arg, __r; \ }) +#define dvb_detach(FUNC) symbol_put_addr(FUNC) + #else #define dvb_attach(FUNCTION, ARGS...) ({ \ FUNCTION(ARGS); \ }) +#define dvb_detach(FUNC) {} + #endif #endif /* #ifndef _DVBDEV_H_ */ -- cgit v0.10.2 From 7f67d96ab181aff4af2074ba0a56b3f81333e896 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 29 May 2014 09:20:14 -0300 Subject: [media] dib7000p: rename dib7000p_attach to dib7000p_init Well, what we call as "foo_attach" is the method that should be called by the dvb_attach() macro. It should be noticed that the name "dvb_attach" is really a bad name and don't express what it does. dvb_attach() basically does three things, if the frontend is compiled as a module: - It lookups for the module that it is known to have the given symbol name and requests such module; - It increments the module usage (anonymously - so lsmod doesn't print who loaded the module); - after loading the module, it runs the function associated with the dynamic symbol. When compiled as builtin, it just calls the function given to it. As dvb_attach() increments refcount, it can't be (easily) called more than once for the same module, or the kernel will deny to remove the module, because refcount will never be zeroed. In other words, the function name given to dvb_attach() should be one single symbol that will always be called before any other function on that module to be used. For almost all DVB frontends, there's just one function. However, the dib7000p initialization can require up to 3 functions to be called: - dib7000p_get_i2c_master; - dib7000p_i2c_enumeration; - dib7000p_init (before this patch dib7000_attach). (plus a bunch of other functions that the bridge driver will need to call). As we need to get rid of all those direct calls, because they cause compilation breakages when bridge is builtin and frontend is module, we'll need to add a new function that will be the first one to be called, whatever initialization is needed. So, let's rename the function that probes and init the hardware to dib7000p_init. A latter patch will add a new dib7000p_attach that will be used as originally conceived by dvb_attach() way. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index effb87f..4b33bce 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -2374,7 +2374,7 @@ int dib7090_slave_reset(struct dvb_frontend *fe) EXPORT_SYMBOL(dib7090_slave_reset); static struct dvb_frontend_ops dib7000p_ops; -struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) +struct dvb_frontend *dib7000p_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) { struct dvb_frontend *demod; struct dib7000p_state *st; @@ -2434,7 +2434,7 @@ error: kfree(st); return NULL; } -EXPORT_SYMBOL(dib7000p_attach); +EXPORT_SYMBOL(dib7000p_init); static struct dvb_frontend_ops dib7000p_ops = { .delsys = { SYS_DVBT }, diff --git a/drivers/media/dvb-frontends/dib7000p.h b/drivers/media/dvb-frontends/dib7000p.h index d08cdff..583c94e 100644 --- a/drivers/media/dvb-frontends/dib7000p.h +++ b/drivers/media/dvb-frontends/dib7000p.h @@ -47,7 +47,7 @@ struct dib7000p_config { #define DEFAULT_DIB7000P_I2C_ADDRESS 18 #if IS_ENABLED(CONFIG_DVB_DIB7000P) -extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); +extern struct dvb_frontend *dib7000p_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); extern struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]); extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); @@ -65,7 +65,7 @@ extern int dib7000p_get_agc_values(struct dvb_frontend *fe, u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd); extern int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v); #else -static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) +static inline struct dvb_frontend *dib7000p_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 4be01b3..69e5263 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -925,7 +925,7 @@ static int dvb_register(struct cx23885_tsport *port) break; case CX23885_BOARD_HAUPPAUGE_HVR1400: i2c_bus = &dev->i2c_bus[0]; - fe0->dvb.frontend = dvb_attach(dib7000p_attach, + fe0->dvb.frontend = dvb_attach(dib7000p_init, &i2c_bus->i2c_adap, 0x12, &hauppauge_hvr1400_dib7000_config); if (fe0->dvb.frontend != NULL) { diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index a1c641e..e81a2fd 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -1085,7 +1085,7 @@ static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, &cxusb_dualdig4_rev2_config); if (adap->fe_adap[0].fe == NULL) return -EIO; diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 10e0db8..3585089 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -281,7 +281,7 @@ static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap) } adap->fe_adap[0].fe = - dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, + dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80 + (adap->id << 1), &stk7700d_dib7000p_mt2266_config[adap->id]); @@ -310,7 +310,7 @@ static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap) } adap->fe_adap[0].fe = - dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, + dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80 + (adap->id << 1), &stk7700d_dib7000p_mt2266_config[adap->id]); @@ -452,7 +452,7 @@ static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, &stk7700ph_dib7700_xc3028_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -690,7 +690,7 @@ static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap) st->mt2060_if1[0] = 1220; if (dib7000pc_detection(&adap->dev->i2c_adap)) { - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config); + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config); st->is_dib7000pc = 1; } else adap->fe_adap[0].fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config); @@ -961,7 +961,7 @@ static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, &dib7070p_dib7000p_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -1013,7 +1013,7 @@ static int stk7770p_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, &dib7770p_dib7000p_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -2899,7 +2899,7 @@ static int nim7090_frontend_attach(struct dvb_usb_adapter *adap) err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config); + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -2945,7 +2945,7 @@ static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap) } dib0700_set_i2c_speed(adap->dev, 340); - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]); + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]); if (adap->fe_adap[0].fe == NULL) return -ENODEV; @@ -2969,7 +2969,7 @@ static int tfe7090pvr_frontend1_attach(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]); + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]); dib0700_set_i2c_speed(adap->dev, 200); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -3030,7 +3030,7 @@ static int tfe7790p_frontend_attach(struct dvb_usb_adapter *adap) __func__); return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, &tfe7790p_dib7000p_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -3115,13 +3115,13 @@ static int stk7070pd_frontend_attach0(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]); + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap) { - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]); + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -3181,7 +3181,7 @@ static int novatd_frontend_attach(struct dvb_usb_adapter *adap) } } - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &dev->i2c_adap, + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &dev->i2c_adap, adap->id == 0 ? 0x80 : 0x82, &stk7070pd_dib7000p_config[adap->id]); @@ -3402,7 +3402,7 @@ static int pctv340e_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x12, + adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x12, &pctv_340e_config); st->is_dib7000pc = 1; -- cgit v0.10.2 From 8abe4a0a3f6d4217b16a1a3f68cd5c72ab5a058e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 29 May 2014 09:20:15 -0300 Subject: [media] dib7000: export just one symbol Exporting multiple symbols don't work as it causes compilation breakages, due to the way dvb_attach() works. This were reported several times, like: drivers/built-in.o: In function `cxusb_dualdig4_rev2_tuner_attach': >> cxusb.c:(.text+0x27d4b5): undefined reference to `dib7000p_get_i2c_master' drivers/built-in.o: In function `dib7070_set_param_override': cxusb.c:(.text+0x27d5a5): undefined reference to `dib0070_wbd_offset' >> cxusb.c:(.text+0x27d5be): undefined reference to `dib7000p_set_wbd_ref' drivers/built-in.o: In function `dib7070_tuner_reset': >> cxusb.c:(.text+0x27d5f9): undefined reference to `dib7000p_set_gpio' drivers/built-in.o: In function `cxusb_dualdig4_rev2_frontend_attach': >> cxusb.c:(.text+0x27df5c): undefined reference to `dib7000p_i2c_enumeration' In this specific report: CONFIG_DVB_USB_CXUSB=y CONFIG_DVB_DIB7000P=m But the same type of bug can happen if: CONFIG_DVB_DIB7000P=m and one of the bridge drivers is compiled builtin (cxusb, cx23885-dvb and/or dib0700). As a bonus, dib7000p won't be loaded anymore if the device uses a different frontend, reducing the memory footprint. Tested with Hauppauge Nova-TD (2 frontends). Reported-by: Fengguang Wu Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 4b33bce..d36fa0d 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -401,7 +401,7 @@ static int dib7000p_sad_calib(struct dib7000p_state *state) return 0; } -int dib7000p_set_wbd_ref(struct dvb_frontend *demod, u16 value) +static int dib7000p_set_wbd_ref(struct dvb_frontend *demod, u16 value) { struct dib7000p_state *state = demod->demodulator_priv; if (value > 4095) @@ -409,9 +409,8 @@ int dib7000p_set_wbd_ref(struct dvb_frontend *demod, u16 value) state->wbd_ref = value; return dib7000p_write_word(state, 105, (dib7000p_read_word(state, 105) & 0xf000) | value); } -EXPORT_SYMBOL(dib7000p_set_wbd_ref); -int dib7000p_get_agc_values(struct dvb_frontend *fe, +static int dib7000p_get_agc_values(struct dvb_frontend *fe, u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd) { struct dib7000p_state *state = fe->demodulator_priv; @@ -427,14 +426,12 @@ int dib7000p_get_agc_values(struct dvb_frontend *fe, return 0; } -EXPORT_SYMBOL(dib7000p_get_agc_values); -int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v) +static int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v) { struct dib7000p_state *state = fe->demodulator_priv; return dib7000p_write_word(state, 108, v); } -EXPORT_SYMBOL(dib7000p_set_agc1_min); static void dib7000p_reset_pll(struct dib7000p_state *state) { @@ -478,7 +475,7 @@ static u32 dib7000p_get_internal_freq(struct dib7000p_state *state) return internal; } -int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw) +static int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw) { struct dib7000p_state *state = fe->demodulator_priv; u16 reg_1857, reg_1856 = dib7000p_read_word(state, 1856); @@ -513,7 +510,6 @@ int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config } return -EIO; } -EXPORT_SYMBOL(dib7000p_update_pll); static int dib7000p_reset_gpio(struct dib7000p_state *st) { @@ -546,12 +542,11 @@ static int dib7000p_cfg_gpio(struct dib7000p_state *st, u8 num, u8 dir, u8 val) return 0; } -int dib7000p_set_gpio(struct dvb_frontend *demod, u8 num, u8 dir, u8 val) +static int dib7000p_set_gpio(struct dvb_frontend *demod, u8 num, u8 dir, u8 val) { struct dib7000p_state *state = demod->demodulator_priv; return dib7000p_cfg_gpio(state, num, dir, val); } -EXPORT_SYMBOL(dib7000p_set_gpio); static u16 dib7000p_defaults[] = { // auto search configuration @@ -934,7 +929,7 @@ static void dib7000p_update_timf(struct dib7000p_state *state) } -u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf) +static u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf) { struct dib7000p_state *state = fe->demodulator_priv; switch (op) { @@ -950,7 +945,6 @@ u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf) dib7000p_set_bandwidth(state, state->current_bandwidth); return state->timf; } -EXPORT_SYMBOL(dib7000p_ctrl_timf); static void dib7000p_set_channel(struct dib7000p_state *state, struct dtv_frontend_properties *ch, u8 seq) @@ -1643,7 +1637,7 @@ static void dib7000p_release(struct dvb_frontend *demod) kfree(st); } -int dib7000pc_detection(struct i2c_adapter *i2c_adap) +static int dib7000pc_detection(struct i2c_adapter *i2c_adap) { u8 *tx, *rx; struct i2c_msg msg[2] = { @@ -1688,16 +1682,14 @@ rx_memory_error: kfree(tx); return ret; } -EXPORT_SYMBOL(dib7000pc_detection); -struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) +static struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) { struct dib7000p_state *st = demod->demodulator_priv; return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); } -EXPORT_SYMBOL(dib7000p_get_i2c_master); -int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +static int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) { struct dib7000p_state *state = fe->demodulator_priv; u16 val = dib7000p_read_word(state, 235) & 0xffef; @@ -1705,17 +1697,15 @@ int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) dprintk("PID filter enabled %d", onoff); return dib7000p_write_word(state, 235, val); } -EXPORT_SYMBOL(dib7000p_pid_filter_ctrl); -int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +static int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) { struct dib7000p_state *state = fe->demodulator_priv; dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff); return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0); } -EXPORT_SYMBOL(dib7000p_pid_filter); -int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) +static int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) { struct dib7000p_state *dpst; int k = 0; @@ -1774,7 +1764,6 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau kfree(dpst); return 0; } -EXPORT_SYMBOL(dib7000p_i2c_enumeration); static const s32 lut_1000ln_mant[] = { 6908, 6956, 7003, 7047, 7090, 7131, 7170, 7208, 7244, 7279, 7313, 7346, 7377, 7408, 7438, 7467, 7495, 7523, 7549, 7575, 7600 @@ -2032,12 +2021,11 @@ static struct i2c_algorithm dib7090_tuner_xfer_algo = { .functionality = dib7000p_i2c_func, }; -struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe) +static struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe) { struct dib7000p_state *st = fe->demodulator_priv; return &st->dib7090_tuner_adap; } -EXPORT_SYMBOL(dib7090_get_i2c_tuner); static int dib7090_host_bus_drive(struct dib7000p_state *state, u8 drive) { @@ -2329,7 +2317,7 @@ static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode) return ret; } -int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) +static int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) { struct dib7000p_state *state = fe->demodulator_priv; u16 en_cur_state; @@ -2352,15 +2340,13 @@ int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) return 0; } -EXPORT_SYMBOL(dib7090_tuner_sleep); -int dib7090_get_adc_power(struct dvb_frontend *fe) +static int dib7090_get_adc_power(struct dvb_frontend *fe) { return dib7000p_get_adc_power(fe); } -EXPORT_SYMBOL(dib7090_get_adc_power); -int dib7090_slave_reset(struct dvb_frontend *fe) +static int dib7090_slave_reset(struct dvb_frontend *fe) { struct dib7000p_state *state = fe->demodulator_priv; u16 reg; @@ -2371,10 +2357,9 @@ int dib7090_slave_reset(struct dvb_frontend *fe) dib7000p_write_word(state, 1032, 0xffff); return 0; } -EXPORT_SYMBOL(dib7090_slave_reset); static struct dvb_frontend_ops dib7000p_ops; -struct dvb_frontend *dib7000p_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) +static struct dvb_frontend *dib7000p_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) { struct dvb_frontend *demod; struct dib7000p_state *st; @@ -2434,7 +2419,32 @@ error: kfree(st); return NULL; } -EXPORT_SYMBOL(dib7000p_init); + +void *dib7000p_attach(struct dib7000p_ops *ops) +{ + if (!ops) + return NULL; + + ops->slave_reset = dib7090_slave_reset; + ops->get_adc_power = dib7090_get_adc_power; + ops->dib7000pc_detection = dib7000pc_detection; + ops->get_i2c_tuner = dib7090_get_i2c_tuner; + ops->tuner_sleep = dib7090_tuner_sleep; + ops->init = dib7000p_init; + ops->set_agc1_min = dib7000p_set_agc1_min; + ops->set_gpio = dib7000p_set_gpio; + ops->i2c_enumeration = dib7000p_i2c_enumeration; + ops->pid_filter = dib7000p_pid_filter; + ops->pid_filter_ctrl = dib7000p_pid_filter_ctrl; + ops->get_i2c_master = dib7000p_get_i2c_master; + ops->update_pll = dib7000p_update_pll; + ops->ctrl_timf = dib7000p_ctrl_timf; + ops->get_agc_values = dib7000p_get_agc_values; + ops->set_wbd_ref = dib7000p_set_wbd_ref; + + return ops; +} +EXPORT_SYMBOL(dib7000p_attach); static struct dvb_frontend_ops dib7000p_ops = { .delsys = { SYS_DVBT }, diff --git a/drivers/media/dvb-frontends/dib7000p.h b/drivers/media/dvb-frontends/dib7000p.h index 583c94e..1fea0e9 100644 --- a/drivers/media/dvb-frontends/dib7000p.h +++ b/drivers/media/dvb-frontends/dib7000p.h @@ -46,121 +46,34 @@ struct dib7000p_config { #define DEFAULT_DIB7000P_I2C_ADDRESS 18 -#if IS_ENABLED(CONFIG_DVB_DIB7000P) -extern struct dvb_frontend *dib7000p_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); -extern struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); -extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]); -extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); -extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value); -extern int dib7000pc_detection(struct i2c_adapter *i2c_adap); -extern int dib7000p_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); -extern int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); -extern int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw); -extern u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf); -extern int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff); -extern int dib7090_get_adc_power(struct dvb_frontend *fe); -extern struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe); -extern int dib7090_slave_reset(struct dvb_frontend *fe); -extern int dib7000p_get_agc_values(struct dvb_frontend *fe, +struct dib7000p_ops { + int (*set_wbd_ref)(struct dvb_frontend *demod, u16 value); + int (*get_agc_values)(struct dvb_frontend *fe, u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd); -extern int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v); -#else -static inline struct dvb_frontend *dib7000p_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} - -static inline struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface i, int x) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} - -static inline int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib7000pc_detection(struct i2c_adapter *i2c_adap) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, uint8_t onoff) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return 0; -} - -static inline int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib7090_get_adc_power(struct dvb_frontend *fe) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} + int (*set_agc1_min)(struct dvb_frontend *fe, u16 v); + int (*update_pll)(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw); + int (*set_gpio)(struct dvb_frontend *demod, u8 num, u8 dir, u8 val); + u32 (*ctrl_timf)(struct dvb_frontend *fe, u8 op, u32 timf); + int (*dib7000pc_detection)(struct i2c_adapter *i2c_adap); + struct i2c_adapter *(*get_i2c_master)(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating); + int (*pid_filter_ctrl)(struct dvb_frontend *fe, u8 onoff); + int (*pid_filter)(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff); + int (*i2c_enumeration)(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]); + struct i2c_adapter *(*get_i2c_tuner)(struct dvb_frontend *fe); + int (*tuner_sleep)(struct dvb_frontend *fe, int onoff); + int (*get_adc_power)(struct dvb_frontend *fe); + int (*slave_reset)(struct dvb_frontend *fe); + struct dvb_frontend *(*init)(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); +}; -static inline struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe) +#if IS_ENABLED(CONFIG_DVB_DIB7000P) +void *dib7000p_attach(struct dib7000p_ops *ops); +#else +static inline void *dib7000p_attach(struct dib7000p_ops *ops) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } - -static inline int dib7090_slave_reset(struct dvb_frontend *fe) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib7000p_get_agc_values(struct dvb_frontend *fe, - u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} #endif #endif diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 69e5263..d037459 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -748,6 +748,7 @@ static int netup_altera_fpga_rw(void *device, int flag, int data, int read) static int dvb_register(struct cx23885_tsport *port) { + struct dib7000p_ops dib7000p_ops; struct cx23885_dev *dev = port->dev; struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL; struct videobuf_dvb_frontend *fe0, *fe1 = NULL; @@ -925,8 +926,11 @@ static int dvb_register(struct cx23885_tsport *port) break; case CX23885_BOARD_HAUPPAUGE_HVR1400: i2c_bus = &dev->i2c_bus[0]; - fe0->dvb.frontend = dvb_attach(dib7000p_init, - &i2c_bus->i2c_adap, + + if (!dvb_attach(dib7000p_attach, &dib7000p_ops)) + return -ENODEV; + + fe0->dvb.frontend = dib7000p_ops.init(&i2c_bus->i2c_adap, 0x12, &hauppauge_hvr1400_dib7000_config); if (fe0->dvb.frontend != NULL) { struct dvb_frontend *fe; diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index e81a2fd..6acde5e 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -1070,8 +1070,15 @@ static struct dib7000p_config cxusb_dualdig4_rev2_config = { .hostbus_diversity = 1, }; +struct dib0700_adapter_state { + int (*set_param_save)(struct dvb_frontend *); + struct dib7000p_ops dib7000p_ops; +}; + static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap) { + struct dib0700_adapter_state *state = adap->priv; + if (usb_set_interface(adap->dev->udev, 0, 1) < 0) err("set interface failed"); @@ -1079,14 +1086,17 @@ static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap) cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); - if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, - &cxusb_dualdig4_rev2_config) < 0) { + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; + + if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + &cxusb_dualdig4_rev2_config) < 0) { printk(KERN_WARNING "Unable to enumerate dib7000p\n"); return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, - &cxusb_dualdig4_rev2_config); + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80, + &cxusb_dualdig4_rev2_config); if (adap->fe_adap[0].fe == NULL) return -EIO; @@ -1095,7 +1105,10 @@ static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap) static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) { - return dib7000p_set_gpio(fe, 8, 0, !onoff); + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + return state->dib7000p_ops.set_gpio(fe, 8, 0, !onoff); } static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff) @@ -1110,10 +1123,6 @@ static struct dib0070_config dib7070p_dib0070_config = { .clock_khz = 12000, }; -struct dib0700_adapter_state { - int (*set_param_save) (struct dvb_frontend *); -}; - static int dib7070_set_param_override(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; @@ -1128,7 +1137,7 @@ static int dib7070_set_param_override(struct dvb_frontend *fe) case BAND_UHF: offset = 550; break; } - dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); + state->dib7000p_ops.set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); return state->set_param_save(fe); } @@ -1136,8 +1145,14 @@ static int dib7070_set_param_override(struct dvb_frontend *fe) static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = - dib7000p_get_i2c_master(adap->fe_adap[0].fe, + struct i2c_adapter *tun_i2c; + + /* + * No need to call dvb7000p_attach here, as it was called + * already, as frontend_attach method is called first, and + * tuner_attach is only called on sucess. + */ + tun_i2c = st->dib7000p_ops.get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 3585089..c09505f 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -32,6 +32,7 @@ MODULE_PARM_DESC(force_lna_activation, "force the activation of Low-Noise-Amplif struct dib0700_adapter_state { int (*set_param_save) (struct dvb_frontend *); const struct firmware *frontend_firmware; + struct dib7000p_ops dib7000p_ops; }; /* Hauppauge Nova-T 500 (aka Bristol) @@ -262,6 +263,11 @@ static struct mt2266_config stk7700d_mt2266_config[2] = { static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap) { + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; + if (adap->id == 0) { dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); @@ -272,16 +278,16 @@ static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap) msleep(10); dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10); - if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 18, stk7700d_dib7000p_mt2266_config) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); + dvb_detach(&state->dib7000p_ops); return -ENODEV; } } - adap->fe_adap[0].fe = - dvb_attach(dib7000p_init, &adap->dev->i2c_adap, + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80 + (adap->id << 1), &stk7700d_dib7000p_mt2266_config[adap->id]); @@ -290,6 +296,11 @@ static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap) static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap) { + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; + if (adap->id == 0) { dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); @@ -301,16 +312,16 @@ static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap) dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, + if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 2, 18, stk7700d_dib7000p_mt2266_config) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); + dvb_detach(&state->dib7000p_ops); return -ENODEV; } } - adap->fe_adap[0].fe = - dvb_attach(dib7000p_init, &adap->dev->i2c_adap, + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80 + (adap->id << 1), &stk7700d_dib7000p_mt2266_config[adap->id]); @@ -320,7 +331,10 @@ static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap) static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap) { struct i2c_adapter *tun_i2c; - tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + struct dib0700_adapter_state *state = adap->priv; + + tun_i2c = state->dib7000p_ops.get_i2c_master(adap->fe_adap[0].fe, + DIBX000_I2C_INTERFACE_TUNER, 1); return dvb_attach(mt2266_attach, adap->fe_adap[0].fe, tun_i2c, &stk7700d_mt2266_config[adap->id]) == NULL ? -ENODEV : 0; } @@ -397,12 +411,14 @@ static int stk7700ph_xc3028_callback(void *ptr, int component, int command, int arg) { struct dvb_usb_adapter *adap = ptr; + struct dib0700_adapter_state *state = adap->priv; switch (command) { case XC2028_TUNER_RESET: /* Send the tuner in then out of reset */ - dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0); msleep(10); - dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + state->dib7000p_ops.set_gpio(adap->fe_adap[0].fe, 8, 0, 0); + msleep(10); + state->dib7000p_ops.set_gpio(adap->fe_adap[0].fe, 8, 0, 1); break; case XC2028_RESET_CLK: break; @@ -428,12 +444,16 @@ static struct xc2028_config stk7700ph_xc3028_config = { static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap) { struct usb_device_descriptor *desc = &adap->dev->udev->descriptor; + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; if (desc->idVendor == cpu_to_le16(USB_VID_PINNACLE) && desc->idProduct == cpu_to_le16(USB_PID_PINNACLE_EXPRESSCARD_320CX)) - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); else - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(20); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); @@ -445,14 +465,15 @@ static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap) dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); msleep(10); - if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 18, &stk7700ph_dib7700_xc3028_config) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); + dvb_detach(&state->dib7000p_ops); return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80, &stk7700ph_dib7700_xc3028_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -461,8 +482,9 @@ static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap) static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap) { struct i2c_adapter *tun_i2c; + struct dib0700_adapter_state *state = adap->priv; - tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, + tun_i2c = state->dib7000p_ops.get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); stk7700ph_xc3028_config.i2c_adap = tun_i2c; @@ -673,6 +695,11 @@ static struct dib7000p_config stk7700p_dib7000p_config = { static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap) { struct dib0700_state *st = adap->dev->priv; + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; + /* unless there is no real power management in DVB - we leave the device on GPIO6 */ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); @@ -689,11 +716,14 @@ static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap) st->mt2060_if1[0] = 1220; - if (dib7000pc_detection(&adap->dev->i2c_adap)) { - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config); + if (state->dib7000p_ops.dib7000pc_detection(&adap->dev->i2c_adap)) { + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config); st->is_dib7000pc = 1; - } else + } else { + dvb_detach(&state->dib7000p_ops); + memset(&state->dib7000p_ops, 0, sizeof(state->dib7000p_ops)); adap->fe_adap[0].fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config); + } return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -707,14 +737,16 @@ static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap) struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap; struct dib0700_state *st = adap->dev->priv; struct i2c_adapter *tun_i2c; + struct dib0700_adapter_state *state = adap->priv; s8 a; int if1=1220; + if (adap->dev->udev->descriptor.idVendor == cpu_to_le16(USB_VID_HAUPPAUGE) && adap->dev->udev->descriptor.idProduct == cpu_to_le16(USB_PID_HAUPPAUGE_NOVA_T_STICK)) { if (!eeprom_read(prim_i2c,0x58,&a)) if1=1220+a; } if (st->is_dib7000pc) - tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + tun_i2c = state->dib7000p_ops.get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); else tun_i2c = dib7000m_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); @@ -767,14 +799,20 @@ static struct dibx000_agc_config dib7070_agc_config = { static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) { + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + deb_info("reset: %d", onoff); - return dib7000p_set_gpio(fe, 8, 0, !onoff); + return state->dib7000p_ops.set_gpio(fe, 8, 0, !onoff); } static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff) { + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + deb_info("sleep: %d", onoff); - return dib7000p_set_gpio(fe, 9, 0, onoff); + return state->dib7000p_ops.set_gpio(fe, 9, 0, onoff); } static struct dib0070_config dib7070p_dib0070_config[2] = { @@ -818,7 +856,7 @@ static int dib7070_set_param_override(struct dvb_frontend *fe) default: offset = 550; break; } deb_info("WBD for DiB7000P: %d\n", offset + dib0070_wbd_offset(fe)); - dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); + state->dib7000p_ops.set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); return state->set_param_save(fe); } @@ -832,39 +870,39 @@ static int dib7770_set_param_override(struct dvb_frontend *fe) u8 band = BAND_OF_FREQUENCY(p->frequency/1000); switch (band) { case BAND_VHF: - dib7000p_set_gpio(fe, 0, 0, 1); + state->dib7000p_ops.set_gpio(fe, 0, 0, 1); offset = 850; break; case BAND_UHF: default: - dib7000p_set_gpio(fe, 0, 0, 0); + state->dib7000p_ops.set_gpio(fe, 0, 0, 0); offset = 250; break; } deb_info("WBD for DiB7000P: %d\n", offset + dib0070_wbd_offset(fe)); - dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); + state->dib7000p_ops.set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); return state->set_param_save(fe); } static int dib7770p_tuner_attach(struct dvb_usb_adapter *adap) { - struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = st->dib7000p_ops.get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); - if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, - &dib7770p_dib0070_config) == NULL) - return -ENODEV; + if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, + &dib7770p_dib0070_config) == NULL) + return -ENODEV; - st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; - adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7770_set_param_override; - return 0; + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7770_set_param_override; + return 0; } static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + struct i2c_adapter *tun_i2c = st->dib7000p_ops.get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); if (adap->id == 0) { if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config[0]) == NULL) @@ -882,28 +920,33 @@ static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap) static int stk7700p_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) { + struct dib0700_adapter_state *state = adapter->priv; struct dib0700_state *st = adapter->dev->priv; + if (st->is_dib7000pc) - return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); + return state->dib7000p_ops.pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); return dib7000m_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); } static int stk7700p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) { struct dib0700_state *st = adapter->dev->priv; + struct dib0700_adapter_state *state = adapter->priv; if (st->is_dib7000pc) - return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); + return state->dib7000p_ops.pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); return dib7000m_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); } static int stk70x0p_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) { - return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); + struct dib0700_adapter_state *state = adapter->priv; + return state->dib7000p_ops.pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); } static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) { - return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); + struct dib0700_adapter_state *state = adapter->priv; + return state->dib7000p_ops.pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); } static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = { @@ -936,6 +979,11 @@ static struct dib7000p_config dib7070p_dib7000p_config = { static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap) { struct usb_device_descriptor *p = &adap->dev->udev->descriptor; + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; + if (p->idVendor == cpu_to_le16(USB_VID_PINNACLE) && p->idProduct == cpu_to_le16(USB_PID_PINNACLE_PCTV72E)) dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); @@ -954,14 +1002,15 @@ static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap) msleep(10); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 18, &dib7070p_dib7000p_config) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); + dvb_detach(&state->dib7000p_ops); return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80, &dib7070p_dib7000p_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -988,6 +1037,11 @@ static struct dib7000p_config dib7770p_dib7000p_config = { static int stk7770p_frontend_attach(struct dvb_usb_adapter *adap) { struct usb_device_descriptor *p = &adap->dev->udev->descriptor; + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; + if (p->idVendor == cpu_to_le16(USB_VID_PINNACLE) && p->idProduct == cpu_to_le16(USB_PID_PINNACLE_PCTV72E)) dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); @@ -1006,14 +1060,15 @@ static int stk7770p_frontend_attach(struct dvb_usb_adapter *adap) msleep(10); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 18, &dib7770p_dib7000p_config) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); + dvb_detach(&state->dib7000p_ops); return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80, &dib7770p_dib7000p_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -2479,14 +2534,14 @@ static int dib7090_agc_startup(struct dvb_frontend *fe) memset(&pll, 0, sizeof(struct dibx000_bandwidth_config)); dib0090_pwm_gain_reset(fe); target = (dib0090_get_wbd_target(fe) * 8 + 1) / 2; - dib7000p_set_wbd_ref(fe, target); + state->dib7000p_ops.set_wbd_ref(fe, target); if (dib7090p_get_best_sampling(fe, &adc) == 0) { pll.pll_ratio = adc.pll_loopdiv; pll.pll_prediv = adc.pll_prediv; - dib7000p_update_pll(fe, &pll); - dib7000p_ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf); + state->dib7000p_ops.update_pll(fe, &pll); + state->dib7000p_ops.ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf); } return 0; } @@ -2501,14 +2556,17 @@ static int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart) static int tfe7790p_update_lna(struct dvb_frontend *fe, u16 agc_global) { + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + deb_info("update LNA: agc global=%i", agc_global); if (agc_global < 25000) { - dib7000p_set_gpio(fe, 8, 0, 0); - dib7000p_set_agc1_min(fe, 0); + state->dib7000p_ops.set_gpio(fe, 8, 0, 0); + state->dib7000p_ops.set_agc1_min(fe, 0); } else { - dib7000p_set_gpio(fe, 8, 0, 1); - dib7000p_set_agc1_min(fe, 32768); + state->dib7000p_ops.set_gpio(fe, 8, 0, 1); + state->dib7000p_ops.set_agc1_min(fe, 32768); } return 0; @@ -2644,13 +2702,16 @@ static struct dib7000p_config nim7090_dib7000p_config = { static int tfe7090p_pvr_update_lna(struct dvb_frontend *fe, u16 agc_global) { + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + deb_info("TFE7090P-PVR update LNA: agc global=%i", agc_global); if (agc_global < 25000) { - dib7000p_set_gpio(fe, 5, 0, 0); - dib7000p_set_agc1_min(fe, 0); + state->dib7000p_ops.set_gpio(fe, 5, 0, 0); + state->dib7000p_ops.set_agc1_min(fe, 0); } else { - dib7000p_set_gpio(fe, 5, 0, 1); - dib7000p_set_agc1_min(fe, 32768); + state->dib7000p_ops.set_gpio(fe, 5, 0, 1); + state->dib7000p_ops.set_agc1_min(fe, 32768); } return 0; @@ -2714,7 +2775,7 @@ static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = { } }; -static const struct dib0090_config nim7090_dib0090_config = { +static struct dib0090_config nim7090_dib0090_config = { .io.clock_khz = 12000, .io.pll_bypass = 0, .io.pll_range = 0, @@ -2722,14 +2783,10 @@ static const struct dib0090_config nim7090_dib0090_config = { .io.pll_loopdiv = 6, .io.adc_clock_ratio = 0, .io.pll_int_loop_filt = 0, - .reset = dib7090_tuner_sleep, - .sleep = dib7090_tuner_sleep, .freq_offset_khz_uhf = 0, .freq_offset_khz_vhf = 0, - .get_adc_power = dib7090_get_adc_power, - .clkouttobamse = 1, .analog_output = 0, @@ -2776,7 +2833,7 @@ static struct dib7000p_config tfe7790p_dib7000p_config = { .enMpegOutput = 1, }; -static const struct dib0090_config tfe7790p_dib0090_config = { +static struct dib0090_config tfe7790p_dib0090_config = { .io.clock_khz = 12000, .io.pll_bypass = 0, .io.pll_range = 0, @@ -2784,14 +2841,10 @@ static const struct dib0090_config tfe7790p_dib0090_config = { .io.pll_loopdiv = 6, .io.adc_clock_ratio = 0, .io.pll_int_loop_filt = 0, - .reset = dib7090_tuner_sleep, - .sleep = dib7090_tuner_sleep, .freq_offset_khz_uhf = 0, .freq_offset_khz_vhf = 0, - .get_adc_power = dib7090_get_adc_power, - .clkouttobamse = 1, .analog_output = 0, @@ -2813,7 +2866,7 @@ static const struct dib0090_config tfe7790p_dib0090_config = { .force_crystal_mode = 1, }; -static const struct dib0090_config tfe7090pvr_dib0090_config[2] = { +static struct dib0090_config tfe7090pvr_dib0090_config[2] = { { .io.clock_khz = 12000, .io.pll_bypass = 0, @@ -2822,14 +2875,10 @@ static const struct dib0090_config tfe7090pvr_dib0090_config[2] = { .io.pll_loopdiv = 6, .io.adc_clock_ratio = 0, .io.pll_int_loop_filt = 0, - .reset = dib7090_tuner_sleep, - .sleep = dib7090_tuner_sleep, .freq_offset_khz_uhf = 50, .freq_offset_khz_vhf = 70, - .get_adc_power = dib7090_get_adc_power, - .clkouttobamse = 1, .analog_output = 0, @@ -2854,14 +2903,10 @@ static const struct dib0090_config tfe7090pvr_dib0090_config[2] = { .io.pll_loopdiv = 6, .io.adc_clock_ratio = 0, .io.pll_int_loop_filt = 0, - .reset = dib7090_tuner_sleep, - .sleep = dib7090_tuner_sleep, .freq_offset_khz_uhf = -50, .freq_offset_khz_vhf = -70, - .get_adc_power = dib7090_get_adc_power, - .clkouttobamse = 1, .analog_output = 0, @@ -2883,6 +2928,11 @@ static const struct dib0090_config tfe7090pvr_dib0090_config[2] = { static int nim7090_frontend_attach(struct dvb_usb_adapter *adap) { + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(20); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); @@ -2895,11 +2945,12 @@ static int nim7090_frontend_attach(struct dvb_usb_adapter *adap) msleep(20); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, &nim7090_dib7000p_config) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, &nim7090_dib7000p_config) != 0) { + err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); + dvb_detach(&state->dib7000p_ops); return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config); + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -2907,12 +2958,16 @@ static int nim7090_frontend_attach(struct dvb_usb_adapter *adap) static int nim7090_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); + struct i2c_adapter *tun_i2c = st->dib7000p_ops.get_i2c_tuner(adap->fe_adap[0].fe); + + nim7090_dib0090_config.reset = st->dib7000p_ops.tuner_sleep, + nim7090_dib0090_config.sleep = st->dib7000p_ops.tuner_sleep, + nim7090_dib0090_config.get_adc_power = st->dib7000p_ops.get_adc_power; if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &nim7090_dib0090_config) == NULL) return -ENODEV; - dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + st->dib7000p_ops.set_gpio(adap->fe_adap[0].fe, 8, 0, 1); st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; @@ -2922,6 +2977,10 @@ static int nim7090_tuner_attach(struct dvb_usb_adapter *adap) static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap) { struct dib0700_state *st = adap->dev->priv; + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; /* The TFE7090 requires the dib0700 to not be in master mode */ st->disable_streaming_master_mode = 1; @@ -2939,17 +2998,18 @@ static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap) dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); /* initialize IC 0 */ - if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, &tfe7090pvr_dib7000p_config[0]) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, &tfe7090pvr_dib7000p_config[0]) != 0) { + err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); + dvb_detach(&state->dib7000p_ops); return -ENODEV; } dib0700_set_i2c_speed(adap->dev, 340); - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]); + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]); if (adap->fe_adap[0].fe == NULL) return -ENODEV; - dib7090_slave_reset(adap->fe_adap[0].fe); + state->dib7000p_ops.slave_reset(adap->fe_adap[0].fe); return 0; } @@ -2957,19 +3017,24 @@ static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap) static int tfe7090pvr_frontend1_attach(struct dvb_usb_adapter *adap) { struct i2c_adapter *i2c; + struct dib0700_adapter_state *state = adap->priv; if (adap->dev->adapter[0].fe_adap[0].fe == NULL) { err("the master dib7090 has to be initialized first"); return -ENODEV; /* the master device has not been initialized */ } - i2c = dib7000p_get_i2c_master(adap->dev->adapter[0].fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1); - if (dib7000p_i2c_enumeration(i2c, 1, 0x10, &tfe7090pvr_dib7000p_config[1]) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; + + i2c = state->dib7000p_ops.get_i2c_master(adap->dev->adapter[0].fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1); + if (state->dib7000p_ops.i2c_enumeration(i2c, 1, 0x10, &tfe7090pvr_dib7000p_config[1]) != 0) { + err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); + dvb_detach(&state->dib7000p_ops); return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]); + adap->fe_adap[0].fe = state->dib7000p_ops.init(i2c, 0x92, &tfe7090pvr_dib7000p_config[1]); dib0700_set_i2c_speed(adap->dev, 200); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -2978,12 +3043,16 @@ static int tfe7090pvr_frontend1_attach(struct dvb_usb_adapter *adap) static int tfe7090pvr_tuner0_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); + struct i2c_adapter *tun_i2c = st->dib7000p_ops.get_i2c_tuner(adap->fe_adap[0].fe); + + tfe7090pvr_dib0090_config[0].reset = st->dib7000p_ops.tuner_sleep; + tfe7090pvr_dib0090_config[0].sleep = st->dib7000p_ops.tuner_sleep; + tfe7090pvr_dib0090_config[0].get_adc_power = st->dib7000p_ops.get_adc_power; if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[0]) == NULL) return -ENODEV; - dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + st->dib7000p_ops.set_gpio(adap->fe_adap[0].fe, 8, 0, 1); st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; @@ -2993,12 +3062,16 @@ static int tfe7090pvr_tuner0_attach(struct dvb_usb_adapter *adap) static int tfe7090pvr_tuner1_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); + struct i2c_adapter *tun_i2c = st->dib7000p_ops.get_i2c_tuner(adap->fe_adap[0].fe); + + tfe7090pvr_dib0090_config[1].reset = st->dib7000p_ops.tuner_sleep; + tfe7090pvr_dib0090_config[1].sleep = st->dib7000p_ops.tuner_sleep; + tfe7090pvr_dib0090_config[1].get_adc_power = st->dib7000p_ops.get_adc_power; if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[1]) == NULL) return -ENODEV; - dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + st->dib7000p_ops.set_gpio(adap->fe_adap[0].fe, 8, 0, 1); st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; @@ -3008,6 +3081,10 @@ static int tfe7090pvr_tuner1_attach(struct dvb_usb_adapter *adap) static int tfe7790p_frontend_attach(struct dvb_usb_adapter *adap) { struct dib0700_state *st = adap->dev->priv; + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; /* The TFE7790P requires the dib0700 to not be in master mode */ st->disable_streaming_master_mode = 1; @@ -3024,13 +3101,14 @@ static int tfe7790p_frontend_attach(struct dvb_usb_adapter *adap) msleep(20); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, + if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, &tfe7790p_dib7000p_config) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); + dvb_detach(&state->dib7000p_ops); return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80, &tfe7790p_dib7000p_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -3040,13 +3118,18 @@ static int tfe7790p_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; struct i2c_adapter *tun_i2c = - dib7090_get_i2c_tuner(adap->fe_adap[0].fe); + st->dib7000p_ops.get_i2c_tuner(adap->fe_adap[0].fe); + + + tfe7790p_dib0090_config.reset = st->dib7000p_ops.tuner_sleep; + tfe7790p_dib0090_config.sleep = st->dib7000p_ops.tuner_sleep; + tfe7790p_dib0090_config.get_adc_power = st->dib7000p_ops.get_adc_power; if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7790p_dib0090_config) == NULL) return -ENODEV; - dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + st->dib7000p_ops.set_gpio(adap->fe_adap[0].fe, 8, 0, 1); st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; @@ -3103,25 +3186,36 @@ static void stk7070pd_init(struct dvb_usb_device *dev) static int stk7070pd_frontend_attach0(struct dvb_usb_adapter *adap) { + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; + stk7070pd_init(adap->dev); msleep(10); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, + if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 2, 18, stk7070pd_dib7000p_config) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); + dvb_detach(&state->dib7000p_ops); return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]); + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap) { - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]); + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; + + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -3164,6 +3258,10 @@ static int novatd_frontend_attach(struct dvb_usb_adapter *adap) { struct dvb_usb_device *dev = adap->dev; struct dib0700_state *st = dev->priv; + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; if (adap->id == 0) { stk7070pd_init(dev); @@ -3173,15 +3271,16 @@ static int novatd_frontend_attach(struct dvb_usb_adapter *adap) dib0700_set_gpio(dev, GPIO1, GPIO_OUT, 0); dib0700_set_gpio(dev, GPIO2, GPIO_OUT, 1); - if (dib7000p_i2c_enumeration(&dev->i2c_adap, 2, 18, + if (state->dib7000p_ops.i2c_enumeration(&dev->i2c_adap, 2, 18, stk7070pd_dib7000p_config) != 0) { - err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); + dvb_detach(&state->dib7000p_ops); return -ENODEV; } } - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &dev->i2c_adap, + adap->fe_adap[0].fe = state->dib7000p_ops.init(&dev->i2c_adap, adap->id == 0 ? 0x80 : 0x82, &stk7070pd_dib7000p_config[adap->id]); @@ -3291,12 +3390,13 @@ static int dib0700_xc4000_tuner_callback(void *priv, int component, int command, int arg) { struct dvb_usb_adapter *adap = priv; + struct dib0700_adapter_state *state = adap->priv; if (command == XC4000_TUNER_RESET) { /* Reset the tuner */ - dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0); + state->dib7000p_ops.set_gpio(adap->fe_adap[0].fe, 8, 0, 0); msleep(10); - dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + state->dib7000p_ops.set_gpio(adap->fe_adap[0].fe, 8, 0, 1); } else { err("xc4000: unknown tuner callback command: %d\n", command); return -EINVAL; @@ -3374,6 +3474,10 @@ static struct dib7000p_config pctv_340e_config = { static int pctv340e_frontend_attach(struct dvb_usb_adapter *adap) { struct dib0700_state *st = adap->dev->priv; + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib7000p_attach, &state->dib7000p_ops)) + return -ENODEV; /* Power Supply on */ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); @@ -3397,12 +3501,13 @@ static int pctv340e_frontend_attach(struct dvb_usb_adapter *adap) msleep(500); - if (dib7000pc_detection(&adap->dev->i2c_adap) == 0) { + if (state->dib7000p_ops.dib7000pc_detection(&adap->dev->i2c_adap) == 0) { /* Demodulator not found for some reason? */ + dvb_detach(&state->dib7000p_ops); return -ENODEV; } - adap->fe_adap[0].fe = dvb_attach(dib7000p_init, &adap->dev->i2c_adap, 0x12, + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x12, &pctv_340e_config); st->is_dib7000pc = 1; @@ -3420,9 +3525,10 @@ static struct xc4000_config dib7000p_xc4000_tunerconfig = { static int xc4000_tuner_attach(struct dvb_usb_adapter *adap) { struct i2c_adapter *tun_i2c; + struct dib0700_adapter_state *state = adap->priv; /* The xc4000 is not on the main i2c bus */ - tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, + tun_i2c = state->dib7000p_ops.get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); if (tun_i2c == NULL) { printk(KERN_ERR "Could not reach tuner i2c bus\n"); -- cgit v0.10.2 From b9bc7d59b72511f8a51e847aedc39f74bfc102a3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 29 May 2014 09:20:16 -0300 Subject: [media] dib8000: rename dib8000_attach to dib8000_init Well, what we call as "foo_attach" is the method that should be called by the dvb_attach() macro. It should be noticed that the name "dvb_attach" is really a bad name and don't express what it does. dvb_attach() basically does three things, if the frontend is compiled as a module: - It lookups for the module that it is known to have the given symbol name and requests such module; - It increments the module usage (anonymously - so lsmod doesn't print who loaded the module); - after loading the module, it runs the function associated with the dynamic symbol. When compiled as builtin, it just calls the function given to it. As dvb_attach() increments refcount, it can't be (easily) called more than once for the same module, or the kernel will deny to remove the module, because refcount will never be zeroed. In other words, the function name given to dvb_attach() should be one single symbol that will always be called before any other function on that module to be used. For almost all DVB frontends, there's just one function, but, on dib8000, there are several exported symbols. We need to get rid of all those direct calls, because they cause compilation breakages when bridge is builtin and frontend is module, we'll need to add a new function that will be the first one to be called, whatever initialization is needed. So, let's rename this function, in order to prepare for a next patch that will add a new attach() function that will be the only one exported by this module. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 1632d78..c1c8c92 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -4405,12 +4405,12 @@ static const struct dvb_frontend_ops dib8000_ops = { .read_ucblocks = dib8000_read_unc_blocks, }; -struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) +struct dvb_frontend *dib8000_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) { struct dvb_frontend *fe; struct dib8000_state *state; - dprintk("dib8000_attach"); + dprintk("dib8000_init"); state = kzalloc(sizeof(struct dib8000_state), GFP_KERNEL); if (state == NULL) @@ -4467,7 +4467,7 @@ error: return NULL; } -EXPORT_SYMBOL(dib8000_attach); +EXPORT_SYMBOL(dib8000_init); MODULE_AUTHOR("Olivier Grenie "); MODULE_DESCRIPTION("Driver for the DiBcom 8000 ISDB-T demodulator"); diff --git a/drivers/media/dvb-frontends/dib8000.h b/drivers/media/dvb-frontends/dib8000.h index b8c11e5..89962d6 100644 --- a/drivers/media/dvb-frontends/dib8000.h +++ b/drivers/media/dvb-frontends/dib8000.h @@ -40,7 +40,7 @@ struct dib8000_config { #define DEFAULT_DIB8000_I2C_ADDRESS 18 #if IS_ENABLED(CONFIG_DVB_DIB8000) -extern struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); +extern struct dvb_frontend *dib8000_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); extern struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); extern int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, @@ -65,7 +65,7 @@ extern int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_fronte extern int dib8000_remove_slave_frontend(struct dvb_frontend *fe); extern struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); #else -static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) +static inline struct dvb_frontend *dib8000_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index c09505f..292f0f1 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -1337,7 +1337,7 @@ static int stk807x_frontend_attach(struct dvb_usb_adapter *adap) dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80, 0); - adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x80, &dib807x_dib8000_config[0]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -1366,7 +1366,7 @@ static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap) /* initialize IC 0 */ dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x80, 0); - adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x80, &dib807x_dib8000_config[0]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -1377,7 +1377,7 @@ static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap) /* initialize IC 1 */ dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x82, 0); - adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, + adap->fe_adap[0].fe = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x82, &dib807x_dib8000_config[1]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -1709,7 +1709,7 @@ static int stk809x_frontend_attach(struct dvb_usb_adapter *adap) dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80, 0); - adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); + adap->fe_adap[0].fe = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -1760,11 +1760,11 @@ static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap) dib8000_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, 0x80, 0); - adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); + adap->fe_adap[0].fe = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); if (adap->fe_adap[0].fe == NULL) return -ENODEV; - fe_slave = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]); + fe_slave = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]); dib8000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave); return fe_slave == NULL ? -ENODEV : 0; @@ -2078,7 +2078,7 @@ static int tfe8096p_frontend_attach(struct dvb_usb_adapter *adap) dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, 0x80, 1); - adap->fe_adap[0].fe = dvb_attach(dib8000_attach, + adap->fe_adap[0].fe = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x80, &tfe8096p_dib8000_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; -- cgit v0.10.2 From d44913c1e547df19b2dc0b527f92a4b4354be23a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 29 May 2014 09:20:17 -0300 Subject: [media] dib8000: export just one symbol Exporting multiple symbols don't work as it causes compilation breakages, due to the way dvb_attach() works. The bug happens when: CONFIG_DVB_DIB8000=m CONFIG_DVB_USB_DIB0700=y As a bonus, dib8000 won't be loaded anymore if the device uses a different frontend, reducing the memory footprint. Tested with both Pixelview PV-D231 and MyGica S870. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index c1c8c92..270a58e 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -656,7 +656,7 @@ static int dib8000_sad_calib(struct dib8000_state *state) return 0; } -int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) +static int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) { struct dib8000_state *state = fe->demodulator_priv; if (value > 4095) @@ -664,7 +664,6 @@ int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) state->wbd_ref = value; return dib8000_write_word(state, 106, value); } -EXPORT_SYMBOL(dib8000_set_wbd_ref); static void dib8000_reset_pll_common(struct dib8000_state *state, const struct dibx000_bandwidth_config *bw) { @@ -739,7 +738,7 @@ static void dib8000_reset_pll(struct dib8000_state *state) dib8000_reset_pll_common(state, pll); } -int dib8000_update_pll(struct dvb_frontend *fe, +static int dib8000_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio) { struct dib8000_state *state = fe->demodulator_priv; @@ -815,8 +814,6 @@ int dib8000_update_pll(struct dvb_frontend *fe, return 0; } -EXPORT_SYMBOL(dib8000_update_pll); - static int dib8000_reset_gpio(struct dib8000_state *st) { @@ -849,13 +846,12 @@ static int dib8000_cfg_gpio(struct dib8000_state *st, u8 num, u8 dir, u8 val) return 0; } -int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +static int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) { struct dib8000_state *state = fe->demodulator_priv; return dib8000_cfg_gpio(state, num, dir, val); } -EXPORT_SYMBOL(dib8000_set_gpio); static const u16 dib8000_defaults[] = { /* auto search configuration - lock0 by default waiting * for cpil_lock; lock1 cpil_lock; lock2 tmcc_sync_lock */ @@ -1228,13 +1224,12 @@ static int dib8000_set_agc_config(struct dib8000_state *state, u8 band) return 0; } -void dib8000_pwm_agc_reset(struct dvb_frontend *fe) +static void dib8000_pwm_agc_reset(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; dib8000_set_adc_state(state, DIBX000_ADC_ON); dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000))); } -EXPORT_SYMBOL(dib8000_pwm_agc_reset); static int dib8000_agc_soft_split(struct dib8000_state *state) { @@ -1881,14 +1876,13 @@ static struct i2c_algorithm dib8096p_tuner_xfer_algo = { .functionality = dib8096p_i2c_func, }; -struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe) +static struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe) { struct dib8000_state *st = fe->demodulator_priv; return &st->dib8096p_tuner_adap; } -EXPORT_SYMBOL(dib8096p_get_i2c_tuner); -int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff) +static int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff) { struct dib8000_state *state = fe->demodulator_priv; u16 en_cur_state; @@ -1912,14 +1906,13 @@ int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff) return 0; } -EXPORT_SYMBOL(dib8096p_tuner_sleep); static const s32 lut_1000ln_mant[] = { 908, 7003, 7090, 7170, 7244, 7313, 7377, 7438, 7495, 7549, 7600 }; -s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) +static s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) { struct dib8000_state *state = fe->demodulator_priv; u32 ix = 0, tmp_val = 0, exp = 0, mant = 0; @@ -1937,9 +1930,8 @@ s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) } return val; } -EXPORT_SYMBOL(dib8000_get_adc_power); -int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ) +static int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ) { struct dib8000_state *state = fe->demodulator_priv; int val = 0; @@ -1957,7 +1949,6 @@ int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ) return val; } -EXPORT_SYMBOL(dib8090p_get_dc_power); static void dib8000_update_timf(struct dib8000_state *state) { @@ -1968,7 +1959,7 @@ static void dib8000_update_timf(struct dib8000_state *state) dprintk("Updated timing frequency: %d (default: %d)", state->timf, state->timf_default); } -u32 dib8000_ctrl_timf(struct dvb_frontend *fe, uint8_t op, uint32_t timf) +static u32 dib8000_ctrl_timf(struct dvb_frontend *fe, uint8_t op, uint32_t timf) { struct dib8000_state *state = fe->demodulator_priv; @@ -1986,7 +1977,6 @@ u32 dib8000_ctrl_timf(struct dvb_frontend *fe, uint8_t op, uint32_t timf) return state->timf; } -EXPORT_SYMBOL(dib8000_ctrl_timf); static const u16 adc_target_16dB[11] = { (1 << 13) - 825 - 117, @@ -2870,21 +2860,19 @@ static s32 dib8000_get_status(struct dvb_frontend *fe) return state->status; } -enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) +static enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; return state->tune_state; } -EXPORT_SYMBOL(dib8000_get_tune_state); -int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +static int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) { struct dib8000_state *state = fe->demodulator_priv; state->tune_state = tune_state; return 0; } -EXPORT_SYMBOL(dib8000_set_tune_state); static int dib8000_tune_restart_from_demod(struct dvb_frontend *fe) { @@ -4201,7 +4189,7 @@ static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) return 0; } -int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +static int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) { struct dib8000_state *state = fe->demodulator_priv; u8 index_frontend = 1; @@ -4217,9 +4205,8 @@ int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_ dprintk("too many slave frontend"); return -ENOMEM; } -EXPORT_SYMBOL(dib8000_set_slave_frontend); -int dib8000_remove_slave_frontend(struct dvb_frontend *fe) +static int dib8000_remove_slave_frontend(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; u8 index_frontend = 1; @@ -4235,9 +4222,8 @@ int dib8000_remove_slave_frontend(struct dvb_frontend *fe) dprintk("no frontend to be removed"); return -ENODEV; } -EXPORT_SYMBOL(dib8000_remove_slave_frontend); -struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +static struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) { struct dib8000_state *state = fe->demodulator_priv; @@ -4245,10 +4231,8 @@ struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int sla return NULL; return state->fe[slave_index]; } -EXPORT_SYMBOL(dib8000_get_slave_frontend); - -int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, +static int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr, u8 is_dib8096p) { int k = 0, ret = 0; @@ -4325,7 +4309,6 @@ error_memory_read: return ret; } -EXPORT_SYMBOL(dib8000_i2c_enumeration); static int dib8000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) { tune->min_delay_ms = 1000; @@ -4348,15 +4331,13 @@ static void dib8000_release(struct dvb_frontend *fe) kfree(st); } -struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) +static struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) { struct dib8000_state *st = fe->demodulator_priv; return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); } -EXPORT_SYMBOL(dib8000_get_i2c_master); - -int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +static int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) { struct dib8000_state *st = fe->demodulator_priv; u16 val = dib8000_read_word(st, 299) & 0xffef; @@ -4365,15 +4346,13 @@ int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) dprintk("pid filter enabled %d", onoff); return dib8000_write_word(st, 299, val); } -EXPORT_SYMBOL(dib8000_pid_filter_ctrl); -int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +static int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) { struct dib8000_state *st = fe->demodulator_priv; dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0); } -EXPORT_SYMBOL(dib8000_pid_filter); static const struct dvb_frontend_ops dib8000_ops = { .delsys = { SYS_ISDBT }, @@ -4405,7 +4384,7 @@ static const struct dvb_frontend_ops dib8000_ops = { .read_ucblocks = dib8000_read_unc_blocks, }; -struct dvb_frontend *dib8000_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) +static struct dvb_frontend *dib8000_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) { struct dvb_frontend *fe; struct dib8000_state *state; @@ -4467,7 +4446,34 @@ error: return NULL; } -EXPORT_SYMBOL(dib8000_init); +void *dib8000_attach(struct dib8000_ops *ops) +{ + if (!ops) + return NULL; + + ops->pwm_agc_reset = dib8000_pwm_agc_reset; + ops->get_dc_power = dib8090p_get_dc_power; + ops->set_gpio = dib8000_set_gpio; + ops->get_slave_frontend = dib8000_get_slave_frontend; + ops->set_tune_state = dib8000_set_tune_state; + ops->pid_filter_ctrl = dib8000_pid_filter_ctrl; + ops->remove_slave_frontend = dib8000_remove_slave_frontend; + ops->get_adc_power = dib8000_get_adc_power; + ops->update_pll = dib8000_update_pll; + ops->tuner_sleep = dib8096p_tuner_sleep; + ops->get_tune_state = dib8000_get_tune_state; + ops->get_i2c_tuner = dib8096p_get_i2c_tuner; + ops->set_slave_frontend = dib8000_set_slave_frontend; + ops->pid_filter = dib8000_pid_filter; + ops->ctrl_timf = dib8000_ctrl_timf; + ops->init = dib8000_init; + ops->get_i2c_master = dib8000_get_i2c_master; + ops->i2c_enumeration = dib8000_i2c_enumeration; + ops->set_wbd_ref = dib8000_set_wbd_ref; + + return ops; +} +EXPORT_SYMBOL(dib8000_attach); MODULE_AUTHOR("Olivier Grenie "); MODULE_DESCRIPTION("Driver for the DiBcom 8000 ISDB-T demodulator"); diff --git a/drivers/media/dvb-frontends/dib8000.h b/drivers/media/dvb-frontends/dib8000.h index 89962d6..84cc103 100644 --- a/drivers/media/dvb-frontends/dib8000.h +++ b/drivers/media/dvb-frontends/dib8000.h @@ -39,134 +39,34 @@ struct dib8000_config { #define DEFAULT_DIB8000_I2C_ADDRESS 18 -#if IS_ENABLED(CONFIG_DVB_DIB8000) -extern struct dvb_frontend *dib8000_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); -extern struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); - -extern int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, +struct dib8000_ops { + int (*set_wbd_ref)(struct dvb_frontend *fe, u16 value); + int (*update_pll)(struct dvb_frontend *fe, + struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio); + int (*set_gpio)(struct dvb_frontend *fe, u8 num, u8 dir, u8 val); + void (*pwm_agc_reset)(struct dvb_frontend *fe); + struct i2c_adapter *(*get_i2c_tuner)(struct dvb_frontend *fe); + int (*tuner_sleep)(struct dvb_frontend *fe, int onoff); + s32 (*get_adc_power)(struct dvb_frontend *fe, u8 mode); + int (*get_dc_power)(struct dvb_frontend *fe, u8 IQ); + u32 (*ctrl_timf)(struct dvb_frontend *fe, uint8_t op, uint32_t timf); + enum frontend_tune_state (*get_tune_state)(struct dvb_frontend *fe); + int (*set_tune_state)(struct dvb_frontend *fe, enum frontend_tune_state tune_state); + int (*set_slave_frontend)(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); + int (*remove_slave_frontend)(struct dvb_frontend *fe); + struct dvb_frontend *(*get_slave_frontend)(struct dvb_frontend *fe, int slave_index); + int (*i2c_enumeration)(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr, u8 is_dib8096p); + struct i2c_adapter *(*get_i2c_master)(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating); + int (*pid_filter_ctrl)(struct dvb_frontend *fe, u8 onoff); + int (*pid_filter)(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff); + struct dvb_frontend *(*init)(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); +}; -extern int dib8000_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); -extern int dib8000_set_wbd_ref(struct dvb_frontend *, u16 value); -extern int dib8000_pid_filter_ctrl(struct dvb_frontend *, u8 onoff); -extern int dib8000_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); -extern int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state); -extern enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe); -extern void dib8000_pwm_agc_reset(struct dvb_frontend *fe); -extern s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode); -extern struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe); -extern int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff); -extern int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ); -extern u32 dib8000_ctrl_timf(struct dvb_frontend *fe, - uint8_t op, uint32_t timf); -extern int dib8000_update_pll(struct dvb_frontend *fe, - struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio); -extern int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); -extern int dib8000_remove_slave_frontend(struct dvb_frontend *fe); -extern struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); +#if IS_ENABLED(CONFIG_DVB_DIB8000) +void *dib8000_attach(struct dib8000_ops *ops); #else -static inline struct dvb_frontend *dib8000_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} - -static inline struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface i, int x) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} - -static inline int dib8000_i2c_enumeration(struct i2c_adapter *host, - int no_of_demods, u8 default_addr, u8 first_addr, - u8 is_dib8096p) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} -static inline int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} -static inline enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return CT_SHUTDOWN; -} -static inline void dib8000_pwm_agc_reset(struct dvb_frontend *fe) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); -} -static inline struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -static inline int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return 0; -} -static inline s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return 0; -} -static inline int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return 0; -} -static inline u32 dib8000_ctrl_timf(struct dvb_frontend *fe, - uint8_t op, uint32_t timf) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return 0; -} -static inline int dib8000_update_pll(struct dvb_frontend *fe, - struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} -static inline int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -int dib8000_remove_slave_frontend(struct dvb_frontend *fe) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} - -static inline struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +static inline int dib8000_attach(struct dib8000_ops *ops) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 292f0f1..d067bb7 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -33,6 +33,7 @@ struct dib0700_adapter_state { int (*set_param_save) (struct dvb_frontend *); const struct firmware *frontend_firmware; struct dib7000p_ops dib7000p_ops; + struct dib8000_ops dib8000_ops; }; /* Hauppauge Nova-T 500 (aka Bristol) @@ -1216,12 +1217,18 @@ static struct dib8000_config dib807x_dib8000_config[2] = { static int dib80xx_tuner_reset(struct dvb_frontend *fe, int onoff) { - return dib8000_set_gpio(fe, 5, 0, !onoff); + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + return state->dib8000_ops.set_gpio(fe, 5, 0, !onoff); } static int dib80xx_tuner_sleep(struct dvb_frontend *fe, int onoff) { - return dib8000_set_gpio(fe, 0, 0, onoff); + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + return state->dib8000_ops.set_gpio(fe, 0, 0, onoff); } static const struct dib0070_wbd_gain_cfg dib8070_wbd_gain_cfg[] = { @@ -1278,7 +1285,7 @@ static int dib807x_set_param_override(struct dvb_frontend *fe) offset += 250; break; } deb_info("WBD for DiB8000: %d\n", offset); - dib8000_set_wbd_ref(fe, offset); + state->dib8000_ops.set_wbd_ref(fe, offset); return state->set_param_save(fe); } @@ -1286,7 +1293,7 @@ static int dib807x_set_param_override(struct dvb_frontend *fe) static int dib807x_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, + struct i2c_adapter *tun_i2c = st->dib8000_ops.get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); if (adap->id == 0) { @@ -1307,18 +1314,27 @@ static int dib807x_tuner_attach(struct dvb_usb_adapter *adap) static int stk80xx_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) { - return dib8000_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); + struct dib0700_adapter_state *state = adapter->priv; + + return state->dib8000_ops.pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); } static int stk80xx_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) { - return dib8000_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); + struct dib0700_adapter_state *state = adapter->priv; + + return state->dib8000_ops.pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); } /* STK807x */ static int stk807x_frontend_attach(struct dvb_usb_adapter *adap) { + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib8000_attach, &state->dib8000_ops)) + return -ENODEV; + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); @@ -1334,10 +1350,10 @@ static int stk807x_frontend_attach(struct dvb_usb_adapter *adap) msleep(10); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + state->dib8000_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80, 0); - adap->fe_adap[0].fe = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = state->dib8000_ops.init(&adap->dev->i2c_adap, 0x80, &dib807x_dib8000_config[0]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -1346,6 +1362,11 @@ static int stk807x_frontend_attach(struct dvb_usb_adapter *adap) /* STK807xPVR */ static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap) { + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib8000_attach, &state->dib8000_ops)) + return -ENODEV; + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(30); dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); @@ -1364,9 +1385,9 @@ static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap) dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); /* initialize IC 0 */ - dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x80, 0); + state->dib8000_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x80, 0); - adap->fe_adap[0].fe = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x80, + adap->fe_adap[0].fe = state->dib8000_ops.init(&adap->dev->i2c_adap, 0x80, &dib807x_dib8000_config[0]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -1374,10 +1395,15 @@ static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap) static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap) { + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib8000_attach, &state->dib8000_ops)) + return -ENODEV; + /* initialize IC 1 */ - dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x82, 0); + state->dib8000_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x82, 0); - adap->fe_adap[0].fe = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x82, + adap->fe_adap[0].fe = state->dib8000_ops.init(&adap->dev->i2c_adap, 0x82, &dib807x_dib8000_config[1]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; @@ -1483,7 +1509,10 @@ static struct dibx000_bandwidth_config dib8090_pll_config_12mhz = { static int dib8090_get_adc_power(struct dvb_frontend *fe) { - return dib8000_get_adc_power(fe, 1); + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dib0700_adapter_state *state = adap->priv; + + return state->dib8000_ops.get_adc_power(fe, 1); } static void dib8090_agc_control(struct dvb_frontend *fe, u8 restart) @@ -1606,10 +1635,10 @@ static int dib8096_set_param_override(struct dvb_frontend *fe) default: deb_info("Warning : Rf frequency (%iHz) is not in the supported range, using VHF switch ", fe->dtv_property_cache.frequency); case BAND_VHF: - dib8000_set_gpio(fe, 3, 0, 1); + state->dib8000_ops.set_gpio(fe, 3, 0, 1); break; case BAND_UHF: - dib8000_set_gpio(fe, 3, 0, 0); + state->dib8000_ops.set_gpio(fe, 3, 0, 0); break; } @@ -1623,7 +1652,7 @@ static int dib8096_set_param_override(struct dvb_frontend *fe) } /** Update PLL if needed ratio **/ - dib8000_update_pll(fe, &dib8090_pll_config_12mhz, fe->dtv_property_cache.bandwidth_hz / 1000, 0); + state->dib8000_ops.update_pll(fe, &dib8090_pll_config_12mhz, fe->dtv_property_cache.bandwidth_hz / 1000, 0); /** Get optimize PLL ratio to remove spurious **/ pll_ratio = dib8090_compute_pll_parameters(fe); @@ -1637,14 +1666,14 @@ static int dib8096_set_param_override(struct dvb_frontend *fe) timf = 18179756; /** Update ratio **/ - dib8000_update_pll(fe, &dib8090_pll_config_12mhz, fe->dtv_property_cache.bandwidth_hz / 1000, pll_ratio); + state->dib8000_ops.update_pll(fe, &dib8090_pll_config_12mhz, fe->dtv_property_cache.bandwidth_hz / 1000, pll_ratio); - dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, timf); + state->dib8000_ops.ctrl_timf(fe, DEMOD_TIMF_SET, timf); if (band != BAND_CBAND) { /* dib0090_get_wbd_target is returning any possible temperature compensated wbd-target */ target = (dib0090_get_wbd_target(fe) * 8 * 18 / 33 + 1) / 2; - dib8000_set_wbd_ref(fe, target); + state->dib8000_ops.set_wbd_ref(fe, target); } if (band == BAND_CBAND) { @@ -1656,18 +1685,18 @@ static int dib8096_set_param_override(struct dvb_frontend *fe) msleep(ret); tune_state = dib0090_get_tune_state(fe); if (tune_state == CT_AGC_STEP_0) - dib8000_set_gpio(fe, 6, 0, 1); + state->dib8000_ops.set_gpio(fe, 6, 0, 1); else if (tune_state == CT_AGC_STEP_1) { dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, <gain); if (rf_gain_limit < 2000) /* activate the external attenuator in case of very high input power */ - dib8000_set_gpio(fe, 6, 0, 0); + state->dib8000_ops.set_gpio(fe, 6, 0, 0); } } while (tune_state < CT_AGC_STOP); deb_info("switching to PWM AGC\n"); dib0090_pwm_gain_reset(fe); - dib8000_pwm_agc_reset(fe); - dib8000_set_tune_state(fe, CT_DEMOD_START); + state->dib8000_ops.pwm_agc_reset(fe); + state->dib8000_ops.set_tune_state(fe, CT_DEMOD_START); } else { /* for everything else than CBAND we are using standard AGC */ deb_info("not tuning in CBAND - standard AGC startup\n"); @@ -1680,7 +1709,7 @@ static int dib8096_set_param_override(struct dvb_frontend *fe) static int dib809x_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + struct i2c_adapter *tun_i2c = st->dib8000_ops.get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) return -ENODEV; @@ -1692,6 +1721,11 @@ static int dib809x_tuner_attach(struct dvb_usb_adapter *adap) static int stk809x_frontend_attach(struct dvb_usb_adapter *adap) { + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib8000_attach, &state->dib8000_ops)) + return -ENODEV; + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); @@ -1707,9 +1741,9 @@ static int stk809x_frontend_attach(struct dvb_usb_adapter *adap) msleep(10); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80, 0); + state->dib8000_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80, 0); - adap->fe_adap[0].fe = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); + adap->fe_adap[0].fe = state->dib8000_ops.init(&adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -1718,16 +1752,16 @@ static int nim8096md_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; struct i2c_adapter *tun_i2c; - struct dvb_frontend *fe_slave = dib8000_get_slave_frontend(adap->fe_adap[0].fe, 1); + struct dvb_frontend *fe_slave = st->dib8000_ops.get_slave_frontend(adap->fe_adap[0].fe, 1); if (fe_slave) { - tun_i2c = dib8000_get_i2c_master(fe_slave, DIBX000_I2C_INTERFACE_TUNER, 1); + tun_i2c = st->dib8000_ops.get_i2c_master(fe_slave, DIBX000_I2C_INTERFACE_TUNER, 1); if (dvb_attach(dib0090_register, fe_slave, tun_i2c, &dib809x_dib0090_config) == NULL) return -ENODEV; fe_slave->dvb = adap->fe_adap[0].fe->dvb; fe_slave->ops.tuner_ops.set_params = dib8096_set_param_override; } - tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); + tun_i2c = st->dib8000_ops.get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) return -ENODEV; @@ -1740,6 +1774,10 @@ static int nim8096md_tuner_attach(struct dvb_usb_adapter *adap) static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap) { struct dvb_frontend *fe_slave; + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib8000_attach, &state->dib8000_ops)) + return -ENODEV; dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(20); @@ -1758,14 +1796,18 @@ static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap) msleep(20); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - dib8000_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, 0x80, 0); + state->dib8000_ops.i2c_enumeration(&adap->dev->i2c_adap, 2, 18, 0x80, 0); - adap->fe_adap[0].fe = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); + adap->fe_adap[0].fe = state->dib8000_ops.init(&adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); if (adap->fe_adap[0].fe == NULL) return -ENODEV; - fe_slave = dvb_attach(dib8000_init, &adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]); - dib8000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave); + /* Needed to increment refcount */ + if (!dvb_attach(dib8000_attach, &state->dib8000_ops)) + return -ENODEV; + + fe_slave = state->dib8000_ops.init(&adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]); + state->dib8000_ops.set_slave_frontend(adap->fe_adap[0].fe, fe_slave); return fe_slave == NULL ? -ENODEV : 0; } @@ -1900,7 +1942,7 @@ static struct dib0090_wbd_slope dib8096p_wbd_table[] = { { 0xFFFF, 0, 0, 0, 0, 0}, }; -static const struct dib0090_config tfe8096p_dib0090_config = { +static struct dib0090_config tfe8096p_dib0090_config = { .io.clock_khz = 12000, .io.pll_bypass = 0, .io.pll_range = 0, @@ -1908,8 +1950,6 @@ static const struct dib0090_config tfe8096p_dib0090_config = { .io.pll_loopdiv = 6, .io.adc_clock_ratio = 0, .io.pll_int_loop_filt = 0, - .reset = dib8096p_tuner_sleep, - .sleep = dib8096p_tuner_sleep, .freq_offset_khz_uhf = -143, .freq_offset_khz_vhf = -143, @@ -1926,8 +1966,6 @@ static const struct dib0090_config tfe8096p_dib0090_config = { .fref_clock_ratio = 1, - .wbd = dib8096p_wbd_table, - .ls_cfg_pad_drv = 0, .data_tx_drv = 0, .low_if = NULL, @@ -2038,15 +2076,15 @@ static int dib8096p_agc_startup(struct dvb_frontend *fe) /* dib0090_get_wbd_target is returning any possible temperature compensated wbd-target */ target = (dib0090_get_wbd_target(fe) * 8 + 1) / 2; - dib8000_set_wbd_ref(fe, target); + state->dib8000_ops.set_wbd_ref(fe, target); if (dib8096p_get_best_sampling(fe, &adc) == 0) { pll.pll_ratio = adc.pll_loopdiv; pll.pll_prediv = adc.pll_prediv; dib0700_set_i2c_speed(adap->dev, 200); - dib8000_update_pll(fe, &pll, fe->dtv_property_cache.bandwidth_hz / 1000, 0); - dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf); + state->dib8000_ops.update_pll(fe, &pll, fe->dtv_property_cache.bandwidth_hz / 1000, 0); + state->dib8000_ops.ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf); dib0700_set_i2c_speed(adap->dev, 1000); } return 0; @@ -2056,6 +2094,10 @@ static int tfe8096p_frontend_attach(struct dvb_usb_adapter *adap) { struct dib0700_state *st = adap->dev->priv; u32 fw_version; + struct dib0700_adapter_state *state = adap->priv; + + if (!dvb_attach(dib8000_attach, &state->dib8000_ops)) + return -ENODEV; dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL); if (fw_version >= 0x10200) @@ -2076,10 +2118,10 @@ static int tfe8096p_frontend_attach(struct dvb_usb_adapter *adap) msleep(20); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); - dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, 0x80, 1); + state->dib8000_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, 0x80, 1); - adap->fe_adap[0].fe = dvb_attach(dib8000_init, - &adap->dev->i2c_adap, 0x80, &tfe8096p_dib8000_config); + adap->fe_adap[0].fe = state->dib8000_ops.init(&adap->dev->i2c_adap, + 0x80, &tfe8096p_dib8000_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } @@ -2087,13 +2129,17 @@ static int tfe8096p_frontend_attach(struct dvb_usb_adapter *adap) static int tfe8096p_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; - struct i2c_adapter *tun_i2c = dib8096p_get_i2c_tuner(adap->fe_adap[0].fe); + struct i2c_adapter *tun_i2c = st->dib8000_ops.get_i2c_tuner(adap->fe_adap[0].fe); + + tfe8096p_dib0090_config.reset = st->dib8000_ops.tuner_sleep; + tfe8096p_dib0090_config.sleep = st->dib8000_ops.tuner_sleep; + tfe8096p_dib0090_config.wbd = dib8096p_wbd_table; if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe8096p_dib0090_config) == NULL) return -ENODEV; - dib8000_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + st->dib8000_ops.set_gpio(adap->fe_adap[0].fe, 8, 0, 1); st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096p_agc_startup; -- cgit v0.10.2 From 041ad449683bb2d54a7f082d78ec15bbc958a175 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 29 May 2014 14:44:56 -0300 Subject: [media] dib7000p: Add DVBv5 stats support Adds DVBv5 stats support. For now, just mimic whatever dib8000 does, as they're very similar, with regards to statistics. However, dib7000p_get_time_us() likely require some adjustments, as I didn't actually reviewed the formula for it to work with DVB-T. Still, better than nothing, as latter patches can improve it. This patch also doesn't show the signal strength in dB yet. The code is already there, but it requires to be callibrated. A latter patch will do the calibration. It seems that this patch is also a bug fix: Before this patch, the frontend were not tuning with some userspace tools. I suspect that dib7000p firmware or hardware internally expects that the statistics to be collected, in order for it to work. With this patch, the DVB core will always retrive statistics, even if userspace doesn't request. So, it makes the device work on all tested apps. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index d36fa0d..c41f90d 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "dvb_math.h" #include "dvb_frontend.h" @@ -72,6 +73,12 @@ struct dib7000p_state { struct mutex i2c_buffer_lock; u8 input_mode_mpeg; + + /* for DVBv5 stats */ + s64 old_ucb; + unsigned long per_jiffies_stats; + unsigned long ber_jiffies_stats; + unsigned long get_stats_time; }; enum dib7000p_power_mode { @@ -631,6 +638,8 @@ static u16 dib7000p_defaults[] = { 0, }; +static void dib7000p_reset_stats(struct dvb_frontend *fe); + static int dib7000p_demod_reset(struct dib7000p_state *state) { dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); @@ -1354,6 +1363,9 @@ static int dib7000p_tune(struct dvb_frontend *demod) dib7000p_spur_protect(state, ch->frequency / 1000, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); + + dib7000p_reset_stats(demod); + return 0; } @@ -1546,6 +1558,8 @@ static int dib7000p_set_frontend(struct dvb_frontend *fe) return ret; } +static int dib7000p_get_stats(struct dvb_frontend *fe, fe_status_t stat); + static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t * stat) { struct dib7000p_state *state = fe->demodulator_priv; @@ -1564,6 +1578,8 @@ static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t * stat) if ((lock & 0x0038) == 0x38) *stat |= FE_HAS_LOCK; + dib7000p_get_stats(fe, *stat); + return 0; } @@ -1589,7 +1605,7 @@ static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 * strength return 0; } -static int dib7000p_read_snr(struct dvb_frontend *fe, u16 * snr) +static u32 dib7000p_get_snr(struct dvb_frontend *fe) { struct dib7000p_state *state = fe->demodulator_priv; u16 val; @@ -1619,10 +1635,311 @@ static int dib7000p_read_snr(struct dvb_frontend *fe, u16 * snr) else result -= intlog10(2) * 10 * noise_exp - 100; + return result; +} + +static int dib7000p_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + u32 result; + + result = dib7000p_get_snr(fe); + *snr = result / ((1 << 24) / 10); return 0; } +static void dib7000p_reset_stats(struct dvb_frontend *demod) +{ + struct dib7000p_state *state = demod->demodulator_priv; + struct dtv_frontend_properties *c = &demod->dtv_property_cache; + u32 ucb; + + memset(&c->strength, 0, sizeof(c->strength)); + memset(&c->cnr, 0, sizeof(c->cnr)); + memset(&c->post_bit_error, 0, sizeof(c->post_bit_error)); + memset(&c->post_bit_count, 0, sizeof(c->post_bit_count)); + memset(&c->block_error, 0, sizeof(c->block_error)); + + c->strength.len = 1; + c->cnr.len = 1; + c->block_error.len = 1; + c->block_count.len = 1; + c->post_bit_error.len = 1; + c->post_bit_count.len = 1; + + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].uvalue = 0; + + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + dib7000p_read_unc_blocks(demod, &ucb); + + state->old_ucb = ucb; + state->ber_jiffies_stats = 0; + state->per_jiffies_stats = 0; +} + +struct linear_segments { + unsigned x; + signed y; +}; + +/* + * Table to estimate signal strength in dBm. + * This table should be empirically determinated by measuring the signal + * strength generated by a RF generator directly connected into + * a device. + */ +/* FIXME: Calibrate the table */ + +#define DB_OFFSET 0 + +static struct linear_segments strength_to_db_table[] = { + { 65535, 65535}, + { 0, 0}, +}; + +static u32 interpolate_value(u32 value, struct linear_segments *segments, + unsigned len) +{ + u64 tmp64; + u32 dx; + s32 dy; + int i, ret; + + if (value >= segments[0].x) + return segments[0].y; + if (value < segments[len-1].x) + return segments[len-1].y; + + for (i = 1; i < len - 1; i++) { + /* If value is identical, no need to interpolate */ + if (value == segments[i].x) + return segments[i].y; + if (value > segments[i].x) + break; + } + + /* Linear interpolation between the two (x,y) points */ + dy = segments[i - 1].y - segments[i].y; + dx = segments[i - 1].x - segments[i].x; + + tmp64 = value - segments[i].x; + tmp64 *= dy; + do_div(tmp64, dx); + ret = segments[i].y + tmp64; + + return ret; +} + +/* FIXME: may require changes - this one was borrowed from dib8000 */ +static u32 dib7000p_get_time_us(struct dvb_frontend *demod, int layer) +{ + struct dtv_frontend_properties *c = &demod->dtv_property_cache; + u64 time_us, tmp64; + u32 tmp, denom; + int guard, rate_num, rate_denum = 1, bits_per_symbol; + int interleaving = 0, fft_div; + + switch (c->guard_interval) { + case GUARD_INTERVAL_1_4: + guard = 4; + break; + case GUARD_INTERVAL_1_8: + guard = 8; + break; + case GUARD_INTERVAL_1_16: + guard = 16; + break; + default: + case GUARD_INTERVAL_1_32: + guard = 32; + break; + } + + switch (c->transmission_mode) { + case TRANSMISSION_MODE_2K: + fft_div = 4; + break; + case TRANSMISSION_MODE_4K: + fft_div = 2; + break; + default: + case TRANSMISSION_MODE_8K: + fft_div = 1; + break; + } + + switch (c->modulation) { + case DQPSK: + case QPSK: + bits_per_symbol = 2; + break; + case QAM_16: + bits_per_symbol = 4; + break; + default: + case QAM_64: + bits_per_symbol = 6; + break; + } + + switch ((c->hierarchy == 0 || 1 == 1) ? c->code_rate_HP : c->code_rate_LP) { + case FEC_1_2: + rate_num = 1; + rate_denum = 2; + break; + case FEC_2_3: + rate_num = 2; + rate_denum = 3; + break; + case FEC_3_4: + rate_num = 3; + rate_denum = 4; + break; + case FEC_5_6: + rate_num = 5; + rate_denum = 6; + break; + default: + case FEC_7_8: + rate_num = 7; + rate_denum = 8; + break; + } + + interleaving = interleaving; + + denom = bits_per_symbol * rate_num * fft_div * 384; + + /* If calculus gets wrong, wait for 1s for the next stats */ + if (!denom) + return 0; + + /* Estimate the period for the total bit rate */ + time_us = rate_denum * (1008 * 1562500L); + tmp64 = time_us; + do_div(tmp64, guard); + time_us = time_us + tmp64; + time_us += denom / 2; + do_div(time_us, denom); + + tmp = 1008 * 96 * interleaving; + time_us += tmp + tmp / guard; + + return time_us; +} + +static int dib7000p_get_stats(struct dvb_frontend *demod, fe_status_t stat) +{ + struct dib7000p_state *state = demod->demodulator_priv; + struct dtv_frontend_properties *c = &demod->dtv_property_cache; + int i; + int show_per_stats = 0; + u32 time_us = 0, val, snr; + u64 blocks, ucb; + s32 db; + u16 strength; + + /* Get Signal strength */ + dib7000p_read_signal_strength(demod, &strength); + val = strength; + db = interpolate_value(val, + strength_to_db_table, + ARRAY_SIZE(strength_to_db_table)) - DB_OFFSET; + c->strength.stat[0].svalue = db; + + /* FIXME: Remove this when calibrated to DB */ + c->strength.stat[0].scale = FE_SCALE_COUNTER; + + /* UCB/BER/CNR measures require lock */ + if (!(stat & FE_HAS_LOCK)) { + c->cnr.len = 1; + c->block_count.len = 1; + c->block_error.len = 1; + c->post_bit_error.len = 1; + c->post_bit_count.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + /* Check if time for stats was elapsed */ + if (time_after(jiffies, state->per_jiffies_stats)) { + state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000); + + /* Get SNR */ + snr = dib7000p_get_snr(demod); + if (snr) + snr = (1000L * snr) >> 24; + else + snr = 0; + c->cnr.stat[0].svalue = snr; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + + /* Get UCB measures */ + dib7000p_read_unc_blocks(demod, &val); + ucb = val - state->old_ucb; + if (val < state->old_ucb) + ucb += 0x100000000LL; + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = ucb; + + /* Estimate the number of packets based on bitrate */ + if (!time_us) + time_us = dib7000p_get_time_us(demod, -1); + + if (time_us) { + blocks = 1250000ULL * 1000000ULL; + do_div(blocks, time_us * 8 * 204); + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += blocks; + } + + show_per_stats = 1; + } + + /* Get post-BER measures */ + if (time_after(jiffies, state->ber_jiffies_stats)) { + time_us = dib7000p_get_time_us(demod, -1); + state->ber_jiffies_stats = jiffies + msecs_to_jiffies((time_us + 500) / 1000); + + dprintk("Next all layers stats available in %u us.", time_us); + + dib7000p_read_ber(demod, &val); + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += val; + + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += 100000000; + } + + /* Get PER measures */ + if (show_per_stats) { + dib7000p_read_unc_blocks(demod, &val); + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += val; + + time_us = dib7000p_get_time_us(demod, i); + if (time_us) { + blocks = 1250000ULL * 1000000ULL; + do_div(blocks, time_us * 8 * 204); + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += blocks; + } + } + return 0; +} + static int dib7000p_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) { tune->min_delay_ms = 1000; @@ -2408,6 +2725,8 @@ static struct dvb_frontend *dib7000p_init(struct i2c_adapter *i2c_adap, u8 i2c_a dib7000p_demod_reset(st); + dib7000p_reset_stats(demod); + if (st->version == SOC7090) { dib7090_set_output_mode(demod, st->cfg.output_mode); dib7090_set_diversity_in(demod, 0); -- cgit v0.10.2 From 3926d91a6b93e42519a48325bf360a5b65460c14 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 29 May 2014 14:44:57 -0300 Subject: [media] dib7000p: Callibrate signal strength Calibrate the signal strength to dB. For this callibration, I used adapter 0 (antenna 1), connected via a normal cable to a DTF-2111 generator. The same cabling and RF generator connected to adapter 1 (antenna 2) has a difference of +3dBm (signal was stronger there). Yet, changing the RF for a difference of, let's say, 6dB reflected on a 6dB difference at the measured signal, with is good. So, the signal strengh can indeed be used to measure the antenna gain, if the antenna is repositioned. It is not precise to measure the absolute dBm value, however. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index c41f90d..661760d 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -1693,13 +1693,56 @@ struct linear_segments { * This table should be empirically determinated by measuring the signal * strength generated by a RF generator directly connected into * a device. + * This table was determinated by measuring the signal strength generated + * by a DTA-2111 RF generator directly connected into a dib7000p device + * (a Hauppauge Nova-TD stick), using a good quality 3 meters length + * RC6 cable and good RC6 connectors, connected directly to antenna 1. + * As the minimum output power of DTA-2111 is -31dBm, a 16 dBm attenuator + * were used, for the lower power values. + * The real value can actually be on other devices, or even at the + * second antena input, depending on several factors, like if LNA + * is enabled or not, if diversity is enabled, type of connectors, etc. + * Yet, it is better to use this measure in dB than a random non-linear + * percentage value, especially for antenna adjustments. + * On my tests, the precision of the measure using this table is about + * 0.5 dB, with sounds reasonable enough to adjust antennas. */ -/* FIXME: Calibrate the table */ - -#define DB_OFFSET 0 +#define DB_OFFSET 131000 static struct linear_segments strength_to_db_table[] = { - { 65535, 65535}, + { 63630, DB_OFFSET - 20500}, + { 62273, DB_OFFSET - 21000}, + { 60162, DB_OFFSET - 22000}, + { 58730, DB_OFFSET - 23000}, + { 58294, DB_OFFSET - 24000}, + { 57778, DB_OFFSET - 25000}, + { 57320, DB_OFFSET - 26000}, + { 56779, DB_OFFSET - 27000}, + { 56293, DB_OFFSET - 28000}, + { 55724, DB_OFFSET - 29000}, + { 55145, DB_OFFSET - 30000}, + { 54680, DB_OFFSET - 31000}, + { 54293, DB_OFFSET - 32000}, + { 53813, DB_OFFSET - 33000}, + { 53427, DB_OFFSET - 34000}, + { 52981, DB_OFFSET - 35000}, + + { 52636, DB_OFFSET - 36000}, + { 52014, DB_OFFSET - 37000}, + { 51674, DB_OFFSET - 38000}, + { 50692, DB_OFFSET - 39000}, + { 49824, DB_OFFSET - 40000}, + { 49052, DB_OFFSET - 41000}, + { 48436, DB_OFFSET - 42000}, + { 47836, DB_OFFSET - 43000}, + { 47368, DB_OFFSET - 44000}, + { 46468, DB_OFFSET - 45000}, + { 45597, DB_OFFSET - 46000}, + { 44586, DB_OFFSET - 47000}, + { 43667, DB_OFFSET - 48000}, + { 42673, DB_OFFSET - 49000}, + { 41816, DB_OFFSET - 50000}, + { 40876, DB_OFFSET - 51000}, { 0, 0}, }; @@ -1853,9 +1896,6 @@ static int dib7000p_get_stats(struct dvb_frontend *demod, fe_status_t stat) ARRAY_SIZE(strength_to_db_table)) - DB_OFFSET; c->strength.stat[0].svalue = db; - /* FIXME: Remove this when calibrated to DB */ - c->strength.stat[0].scale = FE_SCALE_COUNTER; - /* UCB/BER/CNR measures require lock */ if (!(stat & FE_HAS_LOCK)) { c->cnr.len = 1; -- cgit v0.10.2 From 057c2a2ef576a21968e838194b8b9d3b6b106769 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Sat, 3 May 2014 13:05:08 -0300 Subject: [media] V4L: soc-camera: explicitly free allocated managed memory on error devm_kzalloc() allocations are freed when the device is unbound. But if a certain path fails and the allocated memory cannot be used anyway it is better to free it explicitly immediately. This patch does exactly this if asynchronous group probing in scan_async_group() fails after memory has been allocated. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 7fec8cd..dc626b9 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1524,14 +1524,14 @@ static int scan_async_group(struct soc_camera_host *ici, ret = soc_camera_dyn_pdev(&sdesc, sasc); if (ret < 0) - return ret; + goto eallocpdev; sasc->sensor = &sasd->asd; icd = soc_camera_add_pdev(sasc); if (!icd) { - platform_device_put(sasc->pdev); - return -ENOMEM; + ret = -ENOMEM; + goto eaddpdev; } sasc->notifier.subdevs = asd; @@ -1559,7 +1559,11 @@ static int scan_async_group(struct soc_camera_host *ici, v4l2_clk_unregister(icd->clk); eclkreg: icd->clk = NULL; - platform_device_unregister(sasc->pdev); + platform_device_del(sasc->pdev); +eaddpdev: + platform_device_put(sasc->pdev); +eallocpdev: + devm_kfree(ici->v4l2_dev.dev, sasc); dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); return ret; -- cgit v0.10.2 From 25dfa02ce21981ef08d375be987862f96676d8ab Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 14 Apr 2014 07:36:40 -0300 Subject: [media] rcar_vin: copy flags from pdata The platform data is a single word, so simply copy it into the device's private data structure than keeping a copy of the pointer. This will make changing to device-tree binding easier as it is one allocation instead of two. Signed-off-by: Ben Dooks Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index e594230..7c4299d 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -126,13 +126,13 @@ struct rcar_vin_priv { int sequence; /* State of the VIN module in capturing mode */ enum rcar_vin_state state; - struct rcar_vin_platform_data *pdata; struct soc_camera_host ici; struct list_head capture; #define MAX_BUFFER_NUM 3 struct vb2_buffer *queue_buf[MAX_BUFFER_NUM]; struct vb2_alloc_ctx *alloc_ctx; enum v4l2_field field; + unsigned int pdata_flags; unsigned int vb_count; unsigned int nr_hw_slots; bool request_to_stop; @@ -275,12 +275,12 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv) break; case V4L2_MBUS_FMT_YUYV8_2X8: /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ - vnmc |= priv->pdata->flags & RCAR_VIN_BT656 ? + vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; break; case V4L2_MBUS_FMT_YUYV10_2X10: /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ - vnmc |= priv->pdata->flags & RCAR_VIN_BT656 ? + vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; break; default: @@ -797,7 +797,7 @@ static int rcar_vin_set_bus_param(struct soc_camera_device *icd) /* Make choises, based on platform preferences */ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (priv->pdata->flags & RCAR_VIN_HSYNC_ACTIVE_LOW) + if (priv->pdata_flags & RCAR_VIN_HSYNC_ACTIVE_LOW) common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; else common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; @@ -805,7 +805,7 @@ static int rcar_vin_set_bus_param(struct soc_camera_device *icd) if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { - if (priv->pdata->flags & RCAR_VIN_VSYNC_ACTIVE_LOW) + if (priv->pdata_flags & RCAR_VIN_VSYNC_ACTIVE_LOW) common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; else common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; @@ -1445,7 +1445,7 @@ static int rcar_vin_probe(struct platform_device *pdev) priv->ici.drv_name = dev_name(&pdev->dev); priv->ici.ops = &rcar_vin_host_ops; - priv->pdata = pdata; + priv->pdata_flags = pdata->flags; priv->chip = pdev->id_entry->driver_data; spin_lock_init(&priv->lock); INIT_LIST_HEAD(&priv->capture); -- cgit v0.10.2 From 0b91199984239b1944c353a7b8b600e1ec887306 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 23 May 2014 05:50:50 -0300 Subject: [media] V4L2: soc_camera: add run-time dependencies to R-Car VIN driver The R-Car Video Input driver is only useful on shmobile unless build testing. Signed-off-by: Jean Delvare Cc: Vladimir Barinov Cc: Sergei Shtylyov Cc: Guennadi Liakhovetski Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index af39c46..5f54530 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -47,6 +47,7 @@ config VIDEO_PXA27x config VIDEO_RCAR_VIN tristate "R-Car Video Input (VIN) support" depends on VIDEO_DEV && SOC_CAMERA + depends on ARCH_SHMOBILE || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select SOC_CAMERA_SCALE_CROP ---help--- -- cgit v0.10.2 From 90b055898e9d6be6bfeaecd82311aa6989dbbbed Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 24 May 2014 01:56:22 -0300 Subject: [media] media: mx1_camera: Remove driver That driver hasn't been really maintained for a long time. It doesn't compile in any way, it includes non-existent headers, has no users, and marked as "BROKEN" more than year. Due to these factors, mx1_camera is now removed from the tree. Signed-off-by: Alexander Shiyan Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 5f54530..b3d9a1d 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -17,19 +17,6 @@ config SOC_CAMERA_PLATFORM help This is a generic SoC camera platform driver, useful for testing -config MX1_VIDEO - bool - -config VIDEO_MX1 - tristate "i.MX1/i.MXL CMOS Sensor Interface driver" - depends on BROKEN - depends on VIDEO_DEV && ARCH_MX1 && SOC_CAMERA - select FIQ - select VIDEOBUF_DMA_CONTIG - select MX1_VIDEO - ---help--- - This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface - config VIDEO_MX3 tristate "i.MX3x Camera Sensor Interface driver" depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 8aed26d..2826382 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o # soc-camera host drivers have to be linked after camera drivers obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o -obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c deleted file mode 100644 index fea3e61..0000000 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ /dev/null @@ -1,866 +0,0 @@ -/* - * V4L2 Driver for i.MXL/i.MXL camera (CSI) host - * - * Copyright (C) 2008, Paulius Zaleckas - * Copyright (C) 2009, Darius Augulis - * - * Based on PXA SoC camera driver - * Copyright (C) 2006, Sascha Hauer, Pengutronix - * Copyright (C) 2008, Guennadi Liakhovetski - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* - * CSI registers - */ -#define CSICR1 0x00 /* CSI Control Register 1 */ -#define CSISR 0x08 /* CSI Status Register */ -#define CSIRXR 0x10 /* CSI RxFIFO Register */ - -#define CSICR1_RXFF_LEVEL(x) (((x) & 0x3) << 19) -#define CSICR1_SOF_POL (1 << 17) -#define CSICR1_SOF_INTEN (1 << 16) -#define CSICR1_MCLKDIV(x) (((x) & 0xf) << 12) -#define CSICR1_MCLKEN (1 << 9) -#define CSICR1_FCC (1 << 8) -#define CSICR1_BIG_ENDIAN (1 << 7) -#define CSICR1_CLR_RXFIFO (1 << 5) -#define CSICR1_GCLK_MODE (1 << 4) -#define CSICR1_DATA_POL (1 << 2) -#define CSICR1_REDGE (1 << 1) -#define CSICR1_EN (1 << 0) - -#define CSISR_SFF_OR_INT (1 << 25) -#define CSISR_RFF_OR_INT (1 << 24) -#define CSISR_STATFF_INT (1 << 21) -#define CSISR_RXFF_INT (1 << 18) -#define CSISR_SOF_INT (1 << 16) -#define CSISR_DRDY (1 << 0) - -#define DRIVER_VERSION "0.0.2" -#define DRIVER_NAME "mx1-camera" - -#define CSI_IRQ_MASK (CSISR_SFF_OR_INT | CSISR_RFF_OR_INT | \ - CSISR_STATFF_INT | CSISR_RXFF_INT | CSISR_SOF_INT) - -#define CSI_BUS_FLAGS (V4L2_MBUS_MASTER | V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ - 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) - -#define MAX_VIDEO_MEM 16 /* Video memory limit in megabytes */ - -/* - * Structures - */ - -/* buffer for one video frame */ -struct mx1_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - enum v4l2_mbus_pixelcode code; - int inwork; -}; - -/* - * i.MX1/i.MXL is only supposed to handle one camera on its Camera Sensor - * Interface. If anyone ever builds hardware to enable more than - * one camera, they will have to modify this driver too - */ -struct mx1_camera_dev { - struct soc_camera_host soc_host; - struct mx1_camera_pdata *pdata; - struct mx1_buffer *active; - struct resource *res; - struct clk *clk; - struct list_head capture; - - void __iomem *base; - int dma_chan; - unsigned int irq; - unsigned long mclk; - - spinlock_t lock; -}; - -/* - * Videobuf operations - */ -static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct soc_camera_device *icd = vq->priv_data; - - *size = icd->sizeimage; - - if (!*count) - *count = 32; - - if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; - - dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf) -{ - struct soc_camera_device *icd = vq->priv_data; - struct videobuf_buffer *vb = &buf->vb; - - BUG_ON(in_interrupt()); - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - /* - * This waits until this buffer is out of danger, i.e., until it is no - * longer in STATE_QUEUED or STATE_ACTIVE - */ - videobuf_waiton(vq, vb, 0, 0); - videobuf_dma_contig_free(vq, vb); - - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static int mx1_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) -{ - struct soc_camera_device *icd = vq->priv_data; - struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); - int ret; - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - /* Added list head initialization on alloc */ - WARN_ON(!list_empty(&vb->queue)); - - BUG_ON(NULL == icd->current_fmt); - - /* - * I think, in buf_prepare you only have to protect global data, - * the actual buffer is yours - */ - buf->inwork = 1; - - if (buf->code != icd->current_fmt->code || - vb->width != icd->user_width || - vb->height != icd->user_height || - vb->field != field) { - 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 (0 != 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); -out: - buf->inwork = 0; - return ret; -} - -static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) -{ - struct videobuf_buffer *vbuf = &pcdev->active->vb; - struct device *dev = pcdev->soc_host.icd->parent; - int ret; - - if (unlikely(!pcdev->active)) { - dev_err(dev, "DMA End IRQ with no active buffer\n"); - return -EFAULT; - } - - /* setup sg list for future DMA */ - ret = imx_dma_setup_single(pcdev->dma_chan, - videobuf_to_dma_contig(vbuf), - vbuf->size, pcdev->res->start + - CSIRXR, DMA_MODE_READ); - if (unlikely(ret)) - dev_err(dev, "Failed to setup DMA sg list\n"); - - return ret; -} - -/* Called under spinlock_irqsave(&pcdev->lock, ...) */ -static void mx1_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 mx1_camera_dev *pcdev = ici->priv; - struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - list_add_tail(&vb->queue, &pcdev->capture); - - vb->state = VIDEOBUF_ACTIVE; - - if (!pcdev->active) { - pcdev->active = buf; - - /* setup sg list for future DMA */ - if (!mx1_camera_setup_dma(pcdev)) { - unsigned int temp; - /* enable SOF irq */ - temp = __raw_readl(pcdev->base + CSICR1) | - CSICR1_SOF_INTEN; - __raw_writel(temp, pcdev->base + CSICR1); - } - } -} - -static void mx1_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); -#ifdef DEBUG - struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->parent; - - dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - switch (vb->state) { - 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)\n", __func__); - break; - } -#endif - - free_buffer(vq, buf); -} - -static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev, - struct videobuf_buffer *vb, - struct mx1_buffer *buf) -{ - /* _init is used to debug races, see comment in mx1_camera_reqbufs() */ - list_del_init(&vb->queue); - vb->state = VIDEOBUF_DONE; - v4l2_get_timestamp(&vb->ts); - vb->field_count++; - wake_up(&vb->done); - - if (list_empty(&pcdev->capture)) { - pcdev->active = NULL; - return; - } - - pcdev->active = list_entry(pcdev->capture.next, - struct mx1_buffer, vb.queue); - - /* setup sg list for future DMA */ - if (likely(!mx1_camera_setup_dma(pcdev))) { - unsigned int temp; - - /* enable SOF irq */ - temp = __raw_readl(pcdev->base + CSICR1) | CSICR1_SOF_INTEN; - __raw_writel(temp, pcdev->base + CSICR1); - } -} - -static void mx1_camera_dma_irq(int channel, void *data) -{ - struct mx1_camera_dev *pcdev = data; - struct device *dev = pcdev->soc_host.icd->parent; - struct mx1_buffer *buf; - struct videobuf_buffer *vb; - unsigned long flags; - - spin_lock_irqsave(&pcdev->lock, flags); - - imx_dma_disable(channel); - - if (unlikely(!pcdev->active)) { - dev_err(dev, "DMA End IRQ with no active buffer\n"); - goto out; - } - - vb = &pcdev->active->vb; - buf = container_of(vb, struct mx1_buffer, vb); - WARN_ON(buf->inwork || list_empty(&vb->queue)); - dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - mx1_camera_wakeup(pcdev, vb, buf); -out: - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static struct videobuf_queue_ops mx1_videobuf_ops = { - .buf_setup = mx1_videobuf_setup, - .buf_prepare = mx1_videobuf_prepare, - .buf_queue = mx1_videobuf_queue, - .buf_release = mx1_videobuf_release, -}; - -static void mx1_camera_init_videobuf(struct videobuf_queue *q, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx1_camera_dev *pcdev = ici->priv; - - videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent, - &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, - sizeof(struct mx1_buffer), icd, &ici->host_lock); -} - -static int mclk_get_divisor(struct mx1_camera_dev *pcdev) -{ - unsigned int mclk = pcdev->mclk; - unsigned long div; - unsigned long lcdclk; - - lcdclk = clk_get_rate(pcdev->clk); - - /* - * We verify platform_mclk_10khz != 0, so if anyone breaks it, here - * they get a nice Oops - */ - div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1; - - dev_dbg(pcdev->soc_host.icd->parent, - "System clock %lukHz, target freq %dkHz, divisor %lu\n", - lcdclk / 1000, mclk / 1000, div); - - return div; -} - -static void mx1_camera_activate(struct mx1_camera_dev *pcdev) -{ - unsigned int csicr1 = CSICR1_EN; - - dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Activate device\n"); - - clk_prepare_enable(pcdev->clk); - - /* enable CSI before doing anything else */ - __raw_writel(csicr1, pcdev->base + CSICR1); - - csicr1 |= CSICR1_MCLKEN | CSICR1_FCC | CSICR1_GCLK_MODE; - csicr1 |= CSICR1_MCLKDIV(mclk_get_divisor(pcdev)); - csicr1 |= CSICR1_RXFF_LEVEL(2); /* 16 words */ - - __raw_writel(csicr1, pcdev->base + CSICR1); -} - -static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev) -{ - dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Deactivate device\n"); - - /* Disable all CSI interface */ - __raw_writel(0x00, pcdev->base + CSICR1); - - clk_disable_unprepare(pcdev->clk); -} - -static int mx1_camera_add_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void mx1_camera_remove_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "MX1 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 i.MX1/i.MXL camera sensor interface - */ -static int mx1_camera_clock_start(struct soc_camera_host *ici) -{ - struct mx1_camera_dev *pcdev = ici->priv; - - mx1_camera_activate(pcdev); - - return 0; -} - -static void mx1_camera_clock_stop(struct soc_camera_host *ici) -{ - struct mx1_camera_dev *pcdev = ici->priv; - unsigned int csicr1; - - /* disable interrupts */ - csicr1 = __raw_readl(pcdev->base + CSICR1) & ~CSI_IRQ_MASK; - __raw_writel(csicr1, pcdev->base + CSICR1); - - /* Stop DMA engine */ - imx_dma_disable(pcdev->dma_chan); - - mx1_camera_deactivate(pcdev); -} - -static int mx1_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 mx1_camera_dev *pcdev = ici->priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long common_flags; - unsigned int csicr1; - int ret; - - /* MX1 supports only 8bit buswidth */ - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, CSI_BUS_FLAGS); - if (!common_flags) { - dev_warn(icd->parent, - "Flags incompatible: camera 0x%x, host 0x%x\n", - cfg.flags, CSI_BUS_FLAGS); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = CSI_BUS_FLAGS; - } - - /* Make choises, based on platform choice */ - if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { - if (!pcdev->pdata || - pcdev->pdata->flags & MX1_CAMERA_VSYNC_HIGH) - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; - else - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; - } - - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (!pcdev->pdata || - pcdev->pdata->flags & MX1_CAMERA_PCLK_RISING) - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; - else - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; - } - - if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) { - if (!pcdev->pdata || - pcdev->pdata->flags & MX1_CAMERA_DATA_HIGH) - common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW; - else - common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH; - } - - 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 = __raw_readl(pcdev->base + 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_DATA_ACTIVE_LOW) - csicr1 |= CSICR1_DATA_POL; - - __raw_writel(csicr1, pcdev->base + CSICR1); - - return 0; -} - -static int mx1_camera_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 v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; - int ret, buswidth; - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(icd->parent, "Format %x not found\n", - pix->pixelformat); - return -EINVAL; - } - - buswidth = xlate->host_fmt->bits_per_sample; - if (buswidth > 8) { - dev_warn(icd->parent, - "bits-per-sample %d for format %x unsupported\n", - buswidth, 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, video, s_mbus_fmt, &mf); - if (ret < 0) - return ret; - - 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; - - return ret; -} - -static int mx1_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_mbus_framefmt mf; - 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, video, try_mbus_fmt, &mf); - if (ret < 0) - return ret; - - pix->width = mf.width; - pix->height = mf.height; - pix->field = mf.field; - pix->colorspace = mf.colorspace; - - return 0; -} - -static int mx1_camera_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 mx1_buffer *buf = container_of(icd->vb_vidq.bufs[i], - struct mx1_buffer, vb); - buf->inwork = 0; - INIT_LIST_HEAD(&buf->vb.queue); - } - - return 0; -} - -static unsigned int mx1_camera_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - struct mx1_buffer *buf; - - buf = list_entry(icd->vb_vidq.stream.next, struct mx1_buffer, - 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 int mx1_camera_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - /* cap->name is set by the friendly caller:-> */ - strlcpy(cap->card, "i.MX1/i.MXL Camera", sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - - return 0; -} - -static struct soc_camera_host_ops mx1_soc_camera_host_ops = { - .owner = THIS_MODULE, - .add = mx1_camera_add_device, - .remove = mx1_camera_remove_device, - .clock_start = mx1_camera_clock_start, - .clock_stop = mx1_camera_clock_stop, - .set_bus_param = mx1_camera_set_bus_param, - .set_fmt = mx1_camera_set_fmt, - .try_fmt = mx1_camera_try_fmt, - .init_videobuf = mx1_camera_init_videobuf, - .reqbufs = mx1_camera_reqbufs, - .poll = mx1_camera_poll, - .querycap = mx1_camera_querycap, -}; - -static struct fiq_handler fh = { - .name = "csi_sof" -}; - -static int __init mx1_camera_probe(struct platform_device *pdev) -{ - struct mx1_camera_dev *pcdev; - struct resource *res; - struct pt_regs regs; - 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, "csi_clk"); - if (IS_ERR(clk)) { - err = PTR_ERR(clk); - goto exit; - } - - pcdev = kzalloc(sizeof(*pcdev), 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->mclk = pcdev->pdata->mclk_10khz * 10000; - - if (!pcdev->mclk) { - dev_warn(&pdev->dev, - "mclk_10khz == 0! Please, fix your platform data. " - "Using default 20MHz\n"); - pcdev->mclk = 20000000; - } - - INIT_LIST_HEAD(&pcdev->capture); - spin_lock_init(&pcdev->lock); - - /* - * Request the regions. - */ - 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; - - /* request dma */ - pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH); - if (pcdev->dma_chan < 0) { - dev_err(&pdev->dev, "Can't request DMA for MX1 CSI\n"); - err = -EBUSY; - goto exit_iounmap; - } - dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chan); - - imx_dma_setup_handlers(pcdev->dma_chan, mx1_camera_dma_irq, NULL, - pcdev); - - imx_dma_config_channel(pcdev->dma_chan, IMX_DMA_TYPE_FIFO, - IMX_DMA_MEMSIZE_32, MX1_DMA_REQ_CSI_R, 0); - /* burst length : 16 words = 64 bytes */ - imx_dma_config_burstlen(pcdev->dma_chan, 0); - - /* request irq */ - err = claim_fiq(&fh); - if (err) { - dev_err(&pdev->dev, "Camera interrupt register failed\n"); - goto exit_free_dma; - } - - set_fiq_handler(&mx1_camera_sof_fiq_start, &mx1_camera_sof_fiq_end - - &mx1_camera_sof_fiq_start); - - regs.ARM_r8 = (long)MX1_DMA_DIMR; - regs.ARM_r9 = (long)MX1_DMA_CCR(pcdev->dma_chan); - regs.ARM_r10 = (long)pcdev->base + CSICR1; - regs.ARM_fp = (long)pcdev->base + CSISR; - regs.ARM_sp = 1 << pcdev->dma_chan; - set_fiq_regs(®s); - - mxc_set_irq_fiq(irq, 1); - enable_fiq(irq); - - pcdev->soc_host.drv_name = DRIVER_NAME; - pcdev->soc_host.ops = &mx1_soc_camera_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, "MX1 Camera driver loaded\n"); - - return 0; - -exit_free_irq: - disable_fiq(irq); - mxc_set_irq_fiq(irq, 0); - release_fiq(&fh); -exit_free_dma: - imx_dma_free(pcdev->dma_chan); -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 __exit mx1_camera_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct mx1_camera_dev *pcdev = container_of(soc_host, - struct mx1_camera_dev, soc_host); - struct resource *res; - - imx_dma_free(pcdev->dma_chan); - disable_fiq(pcdev->irq); - mxc_set_irq_fiq(pcdev->irq, 0); - release_fiq(&fh); - - clk_put(pcdev->clk); - - soc_camera_host_unregister(soc_host); - - iounmap(pcdev->base); - - res = pcdev->res; - release_mem_region(res->start, resource_size(res)); - - kfree(pcdev); - - dev_info(&pdev->dev, "MX1 Camera driver unloaded\n"); - - return 0; -} - -static struct platform_driver mx1_camera_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .remove = __exit_p(mx1_camera_remove), -}; - -module_platform_driver_probe(mx1_camera_driver, mx1_camera_probe); - -MODULE_DESCRIPTION("i.MX1/i.MXL SoC Camera Host driver"); -MODULE_AUTHOR("Paulius Zaleckas "); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(DRIVER_VERSION); -MODULE_ALIAS("platform:" DRIVER_NAME); -- cgit v0.10.2 From 50f06b88b4c4458560968748fc213bc20ace8d4d Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 24 May 2014 01:56:23 -0300 Subject: [media] media: mx2_camera: Change Kconfig dependency This patch change MACH_MX27 dependency to SOC_IMX27 for MX2 camera driver, since MACH_MX27 symbol is scheduled for removal. Signed-off-by: Alexander Shiyan Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index b3d9a1d..dddca60 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -64,7 +64,7 @@ config VIDEO_OMAP1 config VIDEO_MX2 tristate "i.MX27 Camera Sensor Interface driver" - depends on VIDEO_DEV && SOC_CAMERA && MACH_MX27 + depends on VIDEO_DEV && SOC_CAMERA && SOC_IMX27 select VIDEOBUF2_DMA_CONTIG ---help--- This is a v4l2 driver for the i.MX27 Camera Sensor Interface -- cgit v0.10.2 From 163438b8c76aa0a49dd89bae88ad7cfb3e189c64 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 26 May 2014 10:17:32 -0300 Subject: [media] V4L2: soc_camera: Add run-time dependencies to sh_mobile drivers The sh_mobile_ceu_camera and sh_mobile_csi2 drivers are only useful on SuperH and shmobile unless build testing. Signed-off-by: Jean Delvare Cc: Guennadi Liakhovetski Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index dddca60..6540847 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -43,12 +43,14 @@ config VIDEO_RCAR_VIN config VIDEO_SH_MOBILE_CSI2 tristate "SuperH Mobile MIPI CSI-2 Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK + depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST ---help--- This is a v4l2 driver for the SuperH MIPI CSI-2 Interface config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK + depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select SOC_CAMERA_SCALE_CROP ---help--- -- cgit v0.10.2 From 2ac6f6305efef8c10994be48b20723cc70654189 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Wed, 26 Mar 2014 18:08:33 -0300 Subject: [media] rc: img-ir: Expand copyright headers with GPL notices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the basic GPLv2+ license notice to the copyright headers at the top of all the source files in the img-ir driver. Reported-by: David Härdeman Signed-off-by: James Hogan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/img-ir/img-ir-core.c b/drivers/media/rc/img-ir/img-ir-core.c index 6b78348..a0cac2f 100644 --- a/drivers/media/rc/img-ir/img-ir-core.c +++ b/drivers/media/rc/img-ir/img-ir-core.c @@ -3,6 +3,11 @@ * * Copyright 2010-2014 Imagination Technologies Ltd. * + * 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 contains core img-ir code for setting up the driver. The two interfaces * (raw and hardware decode) are handled separately. */ diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 0127dd2..153c084 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -3,6 +3,11 @@ * * Copyright 2010-2014 Imagination Technologies Ltd. * + * 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 ties into the input subsystem using the RC-core. Protocol support is * provided in separate modules which provide the parameters and scancode * translation functions to set up the hardware decoder and interpret the diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h index 6c9a94a..450f17d 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.h +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -2,6 +2,11 @@ * ImgTec IR Hardware Decoder found in PowerDown Controller. * * Copyright 2010-2014 Imagination Technologies Ltd. + * + * 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. */ #ifndef _IMG_IR_HW_H_ diff --git a/drivers/media/rc/img-ir/img-ir-jvc.c b/drivers/media/rc/img-ir/img-ir-jvc.c index 10209d2..85ee90f 100644 --- a/drivers/media/rc/img-ir/img-ir-jvc.c +++ b/drivers/media/rc/img-ir/img-ir-jvc.c @@ -2,6 +2,11 @@ * ImgTec IR Decoder setup for JVC protocol. * * Copyright 2012-2014 Imagination Technologies Ltd. + * + * 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 "img-ir-hw.h" diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c index 751d9d9..f7520e2 100644 --- a/drivers/media/rc/img-ir/img-ir-nec.c +++ b/drivers/media/rc/img-ir/img-ir-nec.c @@ -2,6 +2,11 @@ * ImgTec IR Decoder setup for NEC protocol. * * Copyright 2010-2014 Imagination Technologies Ltd. + * + * 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 "img-ir-hw.h" diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c index cfb01d9..33f37ed 100644 --- a/drivers/media/rc/img-ir/img-ir-raw.c +++ b/drivers/media/rc/img-ir/img-ir-raw.c @@ -3,6 +3,11 @@ * * Copyright 2010-2014 Imagination Technologies Ltd. * + * 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 ties into the input subsystem using the RC-core in raw mode. Raw IR * signal edges are reported and decoded by generic software decoders. */ diff --git a/drivers/media/rc/img-ir/img-ir-raw.h b/drivers/media/rc/img-ir/img-ir-raw.h index 9802ffd..4c9b767 100644 --- a/drivers/media/rc/img-ir/img-ir-raw.h +++ b/drivers/media/rc/img-ir/img-ir-raw.h @@ -2,6 +2,11 @@ * ImgTec IR Raw Decoder found in PowerDown Controller. * * Copyright 2010-2014 Imagination Technologies Ltd. + * + * 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. */ #ifndef _IMG_IR_RAW_H_ diff --git a/drivers/media/rc/img-ir/img-ir-sanyo.c b/drivers/media/rc/img-ir/img-ir-sanyo.c index c2c763e..6755c94 100644 --- a/drivers/media/rc/img-ir/img-ir-sanyo.c +++ b/drivers/media/rc/img-ir/img-ir-sanyo.c @@ -3,6 +3,11 @@ * * Copyright 2012-2014 Imagination Technologies Ltd. * + * 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. + * * From ir-sanyo-decoder.c: * * This protocol uses the NEC protocol timings. However, data is formatted as: diff --git a/drivers/media/rc/img-ir/img-ir-sharp.c b/drivers/media/rc/img-ir/img-ir-sharp.c index 3397cc5..5867be0 100644 --- a/drivers/media/rc/img-ir/img-ir-sharp.c +++ b/drivers/media/rc/img-ir/img-ir-sharp.c @@ -2,6 +2,11 @@ * ImgTec IR Decoder setup for Sharp protocol. * * Copyright 2012-2014 Imagination Technologies Ltd. + * + * 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 "img-ir-hw.h" diff --git a/drivers/media/rc/img-ir/img-ir-sony.c b/drivers/media/rc/img-ir/img-ir-sony.c index 993409a..b9029ae 100644 --- a/drivers/media/rc/img-ir/img-ir-sony.c +++ b/drivers/media/rc/img-ir/img-ir-sony.c @@ -2,6 +2,11 @@ * ImgTec IR Decoder setup for Sony (SIRC) protocol. * * Copyright 2012-2014 Imagination Technologies Ltd. + * + * 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 "img-ir-hw.h" diff --git a/drivers/media/rc/img-ir/img-ir.h b/drivers/media/rc/img-ir/img-ir.h index afb1893..2ddf560 100644 --- a/drivers/media/rc/img-ir/img-ir.h +++ b/drivers/media/rc/img-ir/img-ir.h @@ -2,6 +2,11 @@ * ImgTec IR Decoder found in PowerDown Controller. * * Copyright 2010-2014 Imagination Technologies Ltd. + * + * 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. */ #ifndef _IMG_IR_H_ -- cgit v0.10.2 From 0bf49ec9f7e29347ad2a384b9c207f4966f6d258 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 9 Jun 2014 18:49:01 -0300 Subject: [media] au0828: add missing tuner Kconfig dependency The analog part of au0828 is missing the tuner Kconfig dependency. That makes the device to not work while in analog mode. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 953a37c..fe48403 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -20,6 +20,7 @@ config VIDEO_AU0828_V4L2 bool "Auvitek AU0828 v4l2 analog video support" depends on VIDEO_AU0828 && VIDEO_V4L2 select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TUNER default y ---help--- This is a video4linux driver for Auvitek's USB device. -- cgit v0.10.2 From 364693166cf4d49e6b72d1f43d4baeebbadf08fa Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 8 Jun 2014 13:54:51 -0300 Subject: [media] au8522: move input_mode out one level The input mode is used not only inside the setup_decoder_defaults() but also at au8522_*_mode routines. So, move it one level up. As an advantage, we can now group the function that sets the input into just one. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 23a0d05..21d2049 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -346,7 +346,7 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode) au8522_writereg(state, AU8522_REG436H, 0x3c); } -static void au8522_setup_cvbs_mode(struct au8522_state *state) +static void au8522_setup_cvbs_mode(struct au8522_state *state, u8 input_mode) { /* here we're going to try the pre-programmed route */ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, @@ -358,16 +358,16 @@ static void au8522_setup_cvbs_mode(struct au8522_state *state) /* Enable clamping control */ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00); - au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, - AU8522_INPUT_CONTROL_REG081H_CVBS_CH1); + au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, input_mode); - setup_decoder_defaults(state, AU8522_INPUT_CONTROL_REG081H_CVBS_CH1); + setup_decoder_defaults(state, input_mode); au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); } -static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state) +static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state, + u8 input_mode) { /* here we're going to try the pre-programmed route */ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, @@ -384,24 +384,22 @@ static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state) au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x10); /* Set input mode to CVBS on channel 4 with SIF audio input enabled */ - au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, - AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF); + au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, input_mode); - setup_decoder_defaults(state, - AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF); + setup_decoder_defaults(state, input_mode); au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); } -static void au8522_setup_svideo_mode(struct au8522_state *state) +static void au8522_setup_svideo_mode(struct au8522_state *state, + u8 input_mode) { au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO); /* Set input to Y on Channe1, C on Channel 3 */ - au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, - AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13); + au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, input_mode); /* PGA in automatic mode */ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00); @@ -409,8 +407,7 @@ static void au8522_setup_svideo_mode(struct au8522_state *state) /* Enable clamping control */ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00); - setup_decoder_defaults(state, - AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13); + setup_decoder_defaults(state, input_mode); au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); @@ -558,10 +555,8 @@ static int au8522_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int au8522_reset(struct v4l2_subdev *sd, u32 val) +static int __au8522_reset(struct au8522_state *state) { - struct au8522_state *state = to_state(sd); - state->operational_mode = AU8522_ANALOG_MODE; /* Clear out any state associated with the digital side of the @@ -574,23 +569,69 @@ static int au8522_reset(struct v4l2_subdev *sd, u32 val) return 0; } +static int au8522_reset(struct v4l2_subdev *sd, u32 val) +{ + struct au8522_state *state = to_state(sd); + + return __au8522_reset(state); +} + +static void au8522_video_set(struct au8522_state *state) + +{ + u8 input_mode; + + __au8522_reset(state); + + switch (state->vid_input) { + case AU8522_COMPOSITE_CH1: + input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH1; + au8522_setup_cvbs_mode(state, input_mode); + break; + case AU8522_COMPOSITE_CH2: + input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH2; + au8522_setup_cvbs_mode(state, input_mode); + break; + case AU8522_COMPOSITE_CH3: + input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH3; + au8522_setup_cvbs_mode(state, input_mode); + break; + case AU8522_COMPOSITE_CH4: + input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH4; + au8522_setup_cvbs_mode(state, input_mode); + break; + case AU8522_SVIDEO_CH13: + input_mode = AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13; + au8522_setup_svideo_mode(state, input_mode); + break; + case AU8522_SVIDEO_CH24: + input_mode = AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24; + au8522_setup_svideo_mode(state, input_mode); + break; + default: + case AU8522_COMPOSITE_CH4_SIF: + input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF; + au8522_setup_cvbs_tuner_mode(state, input_mode); + break; + } +} + static int au8522_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { struct au8522_state *state = to_state(sd); - au8522_reset(sd, 0); - - if (input == AU8522_COMPOSITE_CH1) { - au8522_setup_cvbs_mode(state); - } else if (input == AU8522_SVIDEO_CH13) { - au8522_setup_svideo_mode(state); - } else if (input == AU8522_COMPOSITE_CH4_SIF) { - au8522_setup_cvbs_tuner_mode(state); - } else { + switch(input) { + case AU8522_COMPOSITE_CH1: + case AU8522_SVIDEO_CH13: + case AU8522_COMPOSITE_CH4_SIF: + state->vid_input = input; + break; + default: printk(KERN_ERR "au8522 mode not currently supported\n"); return -EINVAL; } + au8522_video_set(state); return 0; } -- cgit v0.10.2 From 38fe3510fa8fb5e75ee3b196e44a7b717d167e5d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 8 Jun 2014 13:54:52 -0300 Subject: [media] au8522: be sure that the setup will happen at streamon time The same demod is used on both analog and digital mode. We should not let the commands for analog mode to happen while the device is in digital mode. So, monitor it via streamon. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 21d2049..b2b9f04 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -536,52 +536,11 @@ static int au8522_s_register(struct v4l2_subdev *sd, } #endif -static int au8522_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct au8522_state *state = to_state(sd); - - if (enable) { - au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, - 0x01); - msleep(1); - au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, - AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); - } else { - /* This does not completely power down the device - (it only reduces it from around 140ma to 80ma) */ - au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, - 1 << 5); - } - return 0; -} - -static int __au8522_reset(struct au8522_state *state) -{ - state->operational_mode = AU8522_ANALOG_MODE; - - /* Clear out any state associated with the digital side of the - chip, so that when it gets powered back up it won't think - that it is already tuned */ - state->current_frequency = 0; - - au8522_writereg(state, 0xa4, 1 << 5); - - return 0; -} - -static int au8522_reset(struct v4l2_subdev *sd, u32 val) -{ - struct au8522_state *state = to_state(sd); - - return __au8522_reset(state); -} - static void au8522_video_set(struct au8522_state *state) - { u8 input_mode; - __au8522_reset(state); + au8522_writereg(state, 0xa4, 1 << 5); switch (state->vid_input) { case AU8522_COMPOSITE_CH1: @@ -616,6 +575,37 @@ static void au8522_video_set(struct au8522_state *state) } } +static int au8522_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct au8522_state *state = to_state(sd); + + if (enable) { + state->operational_mode = AU8522_ANALOG_MODE; + + /* + * Clear out any state associated with the digital side of the + * chip, so that when it gets powered back up it won't think + * that it is already tuned + */ + state->current_frequency = 0; + + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, + 0x01); + msleep(1); + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, + AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); + + au8522_video_set(state); + } else { + /* This does not completely power down the device + (it only reduces it from around 140ma to 80ma) */ + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, + 1 << 5); + state->operational_mode = AU8522_SUSPEND_MODE; + } + return 0; +} + static int au8522_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -631,7 +621,10 @@ static int au8522_s_video_routing(struct v4l2_subdev *sd, printk(KERN_ERR "au8522 mode not currently supported\n"); return -EINVAL; } - au8522_video_set(state); + + if (state->operational_mode == AU8522_ANALOG_MODE) + au8522_video_set(state); + return 0; } @@ -670,7 +663,6 @@ static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) static const struct v4l2_subdev_core_ops au8522_core_ops = { .log_status = v4l2_ctrl_subdev_log_status, - .reset = au8522_reset, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = au8522_g_register, .s_register = au8522_s_register, diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h index aa0f16d..a781489 100644 --- a/drivers/media/dvb-frontends/au8522_priv.h +++ b/drivers/media/dvb-frontends/au8522_priv.h @@ -37,6 +37,7 @@ #define AU8522_ANALOG_MODE 0 #define AU8522_DIGITAL_MODE 1 +#define AU8522_SUSPEND_MODE 2 struct au8522_state { struct i2c_client *c; -- cgit v0.10.2 From d289cdf022c5bebf09c73097404aa9faf2211381 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 8 Jun 2014 13:54:53 -0300 Subject: [media] au8522: be sure that we'll setup audio routing at the right time Let's set the the audio routing also at stream start. With this change, we don't risk enabling the analog demux while not streaming, reducing the risk of interfering with a DVB demux that might be happening. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index b2b9f04..53f6dea 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -429,8 +429,9 @@ static void disable_audio_input(struct au8522_state *state) } /* 0=disable, 1=SIF */ -static void set_audio_input(struct au8522_state *state, int aud_input) +static void set_audio_input(struct au8522_state *state) { + int aud_input = state->aud_input; int i; /* Note that this function needs to be used in conjunction with setting @@ -580,8 +581,6 @@ static int au8522_s_stream(struct v4l2_subdev *sd, int enable) struct au8522_state *state = to_state(sd); if (enable) { - state->operational_mode = AU8522_ANALOG_MODE; - /* * Clear out any state associated with the digital side of the * chip, so that when it gets powered back up it won't think @@ -596,6 +595,10 @@ static int au8522_s_stream(struct v4l2_subdev *sd, int enable) AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); au8522_video_set(state); + + set_audio_input(state); + + state->operational_mode = AU8522_ANALOG_MODE; } else { /* This does not completely power down the device (it only reduces it from around 140ma to 80ma) */ @@ -632,7 +635,12 @@ static int au8522_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { struct au8522_state *state = to_state(sd); - set_audio_input(state, input); + + state->aud_input = input; + + if (state->operational_mode == AU8522_ANALOG_MODE) + set_audio_input(state); + return 0; } -- cgit v0.10.2 From 65c8820912d9469de75e5c607cefda92f11e3d2f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 8 Jun 2014 13:54:54 -0300 Subject: [media] au8522: cleanup s-video settings at setup_decoder_defaults() setup_decoder_defaults() doesn't really care about the input port. All it needs to know is if the input port is s-video or not. As the caller function already knows that, just pass a boolean instead. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 53f6dea..5699222 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -220,7 +220,7 @@ static void setup_vbi(struct au8522_state *state, int aud_input) } -static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode) +static void setup_decoder_defaults(struct au8522_state *state, bool is_svideo) { int i; int filter_coef_type; @@ -237,13 +237,10 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode) /* Other decoder registers */ au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00); - if (input_mode == 0x23) { - /* S-Video input mapping */ + if (is_svideo) au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x04); - } else { - /* All other modes (CVBS/ATVRF etc.) */ + else au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x00); - } au8522_writereg(state, AU8522_TVDEC_PGA_REG012H, AU8522_TVDEC_PGA_REG012H_CVBS); @@ -275,8 +272,7 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode) AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS); au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR3_REG06BH, AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS); - if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 || - input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) { + if (is_svideo) { au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO); au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH, @@ -317,8 +313,7 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode) setup_vbi(state, 0); - if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 || - input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) { + if (is_svideo) { /* Despite what the table says, for the HVR-950q we still need to be in CVBS mode for the S-Video input (reason unknown). */ /* filter_coef_type = 3; */ @@ -360,7 +355,7 @@ static void au8522_setup_cvbs_mode(struct au8522_state *state, u8 input_mode) au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, input_mode); - setup_decoder_defaults(state, input_mode); + setup_decoder_defaults(state, false); au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); @@ -386,7 +381,7 @@ static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state, /* Set input mode to CVBS on channel 4 with SIF audio input enabled */ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, input_mode); - setup_decoder_defaults(state, input_mode); + setup_decoder_defaults(state, false); au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); @@ -407,7 +402,7 @@ static void au8522_setup_svideo_mode(struct au8522_state *state, /* Enable clamping control */ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00); - setup_decoder_defaults(state, input_mode); + setup_decoder_defaults(state, true); au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); -- cgit v0.10.2 From c9f5ccc24d1c08705ab5ea2bf0d6166c8e8dc545 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 8 Jun 2014 13:54:55 -0300 Subject: [media] au8522: Fix demod analog mode setting There are several issues on the current code: 1) msleep(1) is actually equivalent to msleep(10); 2) au8522_video_set() will set reg 0xa4 to the proper value for SIF, CVBS or S-Video. No need to force it to CVBS; 3) Let's not hardcode 0x9d for CBS on audio_set. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 5699222..b971c20 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -458,8 +458,9 @@ static void set_audio_input(struct au8522_state *state) au8522_writereg(state, AU8522_I2C_CONTROL_REG0_REG090H, 0x84); msleep(150); au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x00); - msleep(1); - au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x9d); + msleep(10); + au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, + AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); msleep(50); au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F); au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F); @@ -585,12 +586,9 @@ static int au8522_s_stream(struct v4l2_subdev *sd, int enable) au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x01); - msleep(1); - au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, - AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); + msleep(10); au8522_video_set(state); - set_audio_input(state); state->operational_mode = AU8522_ANALOG_MODE; -- cgit v0.10.2 From f2fd7ce6f2dbf286fd07fb82109599df741b57d3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 8 Jun 2014 13:54:56 -0300 Subject: [media] au0828/au8522: Add PAL-M support Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index b971c20..33aa941 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -248,12 +248,23 @@ static void setup_decoder_defaults(struct au8522_state *state, bool is_svideo) AU8522_TVDEC_COMB_MODE_REG015H_CVBS); au8522_writereg(state, AU8522_TVDED_DBG_MODE_REG060H, AU8522_TVDED_DBG_MODE_REG060H_CVBS); - au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H, - AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 | - AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 | - AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_MN); - au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H, - AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_NTSC); + + if (state->std == V4L2_STD_PAL_M) { + au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H, + AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 | + AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 | + AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_AUTO); + au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H, + AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_PAL_M); + } else { + /* NTSC */ + au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H, + AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 | + AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 | + AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_MN); + au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H, + AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_NTSC); + } au8522_writereg(state, AU8522_TVDEC_VCR_DET_LLIM_REG063H, AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS); au8522_writereg(state, AU8522_TVDEC_VCR_DET_HLIM_REG064H, @@ -624,6 +635,21 @@ static int au8522_s_video_routing(struct v4l2_subdev *sd, return 0; } +static int au8522_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct au8522_state *state = to_state(sd); + + if ((std & (V4L2_STD_PAL_M | V4L2_STD_NTSC_M)) == 0) + return -EINVAL; + + state->std = std; + + if (state->operational_mode == AU8522_ANALOG_MODE) + au8522_video_set(state); + + return 0; +} + static int au8522_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -681,6 +707,7 @@ static const struct v4l2_subdev_audio_ops au8522_audio_ops = { static const struct v4l2_subdev_video_ops au8522_video_ops = { .s_routing = au8522_s_video_routing, .s_stream = au8522_s_stream, + .s_std = au8522_s_std, }; static const struct v4l2_subdev_ops au8522_ops = { @@ -763,6 +790,7 @@ static int au8522_probe(struct i2c_client *client, } state->c = client; + state->std = V4L2_STD_NTSC_M; state->vid_input = AU8522_COMPOSITE_CH1; state->aud_input = AU8522_AUDIO_NONE; state->id = 8522; diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h index a781489..b8aca1c 100644 --- a/drivers/media/dvb-frontends/au8522_priv.h +++ b/drivers/media/dvb-frontends/au8522_priv.h @@ -348,6 +348,7 @@ int au8522_led_ctrl(struct au8522_state *state, int led); /* Format control 2 */ #define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_AUTODETECT 0x00 #define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_NTSC 0x01 +#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_PAL_M 0x02 #define AU8522_INPUT_CONTROL_REG081H_ATSC 0xC4 diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 9038194..4aa1d7a 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -1364,9 +1364,11 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) i2c_gate_ctrl(dev, 1); - /* FIXME: when we support something other than NTSC, we are going to - have to make the au0828 bridge adjust the size of its capture - buffer, which is currently hardcoded at 720x480 */ + /* + * FIXME: when we support something other than 60Hz standards, + * we are going to have to make the au0828 bridge adjust the size + * of its capture buffer, which is currently hardcoded at 720x480 + */ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, norm); @@ -1915,7 +1917,7 @@ static const struct video_device au0828_video_template = { .fops = &au0828_v4l_fops, .release = video_device_release, .ioctl_ops = &video_ioctl_ops, - .tvnorms = V4L2_STD_NTSC_M, + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_M, }; /**************************************************************************/ -- cgit v0.10.2 From 64ea37bbd8a5815522706f0099ad3f11c7537e15 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 8 Jun 2014 13:54:57 -0300 Subject: [media] au0828: Only alt setting logic when needed It seems that there's a bug at au0828 hardware/firmware related to alternate setting: when the device is already at alt 5, a further call causes the URBs to receive -ESHUTDOWN. I found two different encarnations of this issue: 1) at qv4l2, it fails the second time we try to open the video screen; 2) at xawtv, when audio underrun occurs, with is very frequent, at least on my test machine. The fix is simple: just check if alt=5 before calling set_usb_interface(). Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 4aa1d7a..85d83ca 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -787,11 +787,27 @@ static int au0828_i2s_init(struct au0828_dev *dev) /* * Auvitek au0828 analog stream enable - * Please set interface0 to AS5 before enable the stream */ static int au0828_analog_stream_enable(struct au0828_dev *d) { + struct usb_interface *iface; + int ret; + dprintk(1, "au0828_analog_stream_enable called\n"); + + iface = usb_ifnum_to_if(d->usbdev, 0); + if (iface && iface->cur_altsetting->desc.bAlternateSetting != 5) { + dprintk(1, "Changing intf#0 to alt 5\n"); + /* set au0828 interface0 to AS5 here again */ + ret = usb_set_interface(d->usbdev, 0, 5); + if (ret < 0) { + printk(KERN_INFO "Au0828 can't set alt setting to 5!\n"); + return -EBUSY; + } + } + + /* FIXME: size should be calculated using d->width, d->height */ + au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00); au0828_writereg(d, 0x106, 0x00); /* set x position */ @@ -1002,15 +1018,6 @@ static int au0828_v4l2_open(struct file *filp) return -ERESTARTSYS; } if (dev->users == 0) { - /* set au0828 interface0 to AS5 here again */ - ret = usb_set_interface(dev->usbdev, 0, 5); - if (ret < 0) { - mutex_unlock(&dev->lock); - printk(KERN_INFO "Au0828 can't set alternate to 5!\n"); - kfree(fh); - return -EBUSY; - } - au0828_analog_stream_enable(dev); au0828_analog_stream_reset(dev); @@ -1252,13 +1259,6 @@ static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, } } - /* set au0828 interface0 to AS5 here again */ - ret = usb_set_interface(dev->usbdev, 0, 5); - if (ret < 0) { - printk(KERN_INFO "Au0828 can't set alt setting to 5!\n"); - return -EBUSY; - } - au0828_analog_stream_enable(dev); return 0; -- cgit v0.10.2 From 1fe3a8fe494463cfe2556a25ae41a1499725c178 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 8 Jun 2014 13:54:58 -0300 Subject: [media] au0828: don't hardcode height/width While this device doesn't have a scaler (or have it disabled), the screen dimentions are a function of the standard. Ok, right now, only 480 lines standards are implemented, although it supports other ones. Yet, let's calculate the size, to make easier to add more standards latter. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 85d83ca..385894a 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -791,7 +791,7 @@ static int au0828_i2s_init(struct au0828_dev *dev) static int au0828_analog_stream_enable(struct au0828_dev *d) { struct usb_interface *iface; - int ret; + int ret, h, w; dprintk(1, "au0828_analog_stream_enable called\n"); @@ -806,20 +806,21 @@ static int au0828_analog_stream_enable(struct au0828_dev *d) } } - /* FIXME: size should be calculated using d->width, d->height */ + h = d->height / 2 + 2; + w = d->width * 2; au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00); au0828_writereg(d, 0x106, 0x00); /* set x position */ au0828_writereg(d, 0x110, 0x00); au0828_writereg(d, 0x111, 0x00); - au0828_writereg(d, 0x114, 0xa0); - au0828_writereg(d, 0x115, 0x05); + au0828_writereg(d, 0x114, w & 0xff); + au0828_writereg(d, 0x115, w >> 8); /* set y position */ au0828_writereg(d, 0x112, 0x00); au0828_writereg(d, 0x113, 0x00); - au0828_writereg(d, 0x116, 0xf2); - au0828_writereg(d, 0x117, 0x00); + au0828_writereg(d, 0x116, h & 0xff); + au0828_writereg(d, 0x117, h >> 8); au0828_writereg(d, AU0828_SENSORCTRL_100, 0xb3); return 0; @@ -1725,6 +1726,7 @@ static int vidioc_streamoff(struct file *file, void *priv, dev->vid_timeout_running = 0; del_timer_sync(&dev->vid_timeout); + au0828_analog_stream_disable(dev); v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); rc = au0828_stream_interrupt(dev); if (rc != 0) @@ -1930,7 +1932,8 @@ int au0828_analog_register(struct au0828_dev *dev, struct usb_endpoint_descriptor *endpoint; int i, ret; - dprintk(1, "au0828_analog_register called!\n"); + dprintk(1, "au0828_analog_register called for intf#%d!\n", + interface->cur_altsetting->desc.bInterfaceNumber); /* set au0828 usb interface0 to as5 */ retval = usb_set_interface(dev->usbdev, @@ -1954,6 +1957,9 @@ int au0828_analog_register(struct au0828_dev *dev, dev->max_pkt_size = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); dev->isoc_in_endpointaddr = endpoint->bEndpointAddress; + dprintk(1, + "Found isoc endpoint 0x%02x, max size = %d\n", + dev->isoc_in_endpointaddr, dev->max_pkt_size); } } if (!(dev->isoc_in_endpointaddr)) { -- cgit v0.10.2 From b5b620584b9c4644b85e932895a742e0c192d66c Mon Sep 17 00:00:00 2001 From: CrazyCat Date: Wed, 16 Apr 2014 17:22:01 -0300 Subject: [media] technisat-sub2: Fix stream curruption on high bitrate Fix stream curruption on high bitrate (>60mbit). Signed-off-by: Evgeny Plehov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c index d947e03..6b0b8b6 100644 --- a/drivers/media/usb/dvb-usb/technisat-usb2.c +++ b/drivers/media/usb/dvb-usb/technisat-usb2.c @@ -710,7 +710,7 @@ static struct dvb_usb_device_properties technisat_usb2_devices = { .isoc = { .framesperurb = 32, .framesize = 2048, - .interval = 3, + .interval = 1, } } }, -- cgit v0.10.2 From 2fe152451df0ead7e1ca14c0c6210b13226feafb Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Wed, 25 Jun 2014 06:27:56 -0300 Subject: [media] gspca: provide a mechanism to select a specific transfer endpoint Currently gspca selects the first ISOC input endpoint as the input transfer endpoint, however some devices can provide streams on endpoints different then the first one, so some subdrivers (e.g. gspca_kinect) may want to select a specific endpoint to use as a transfer endpoint. Add an xfer_ep field to struct gspca_dev, and change alt_xfer() so that it accepts a parameter which represents a specific endpoint address to look for. If a subdriver wants to specify a value for gspca_dev->xfer_ep it can do that in its sd_config() callback. Signed-off-by: Antonio Ospite Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index f3a7ace..f9a75ad 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -603,10 +603,13 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev) } /* - * look for an input transfer endpoint in an alternate setting + * look for an input transfer endpoint in an alternate setting. + * + * If xfer_ep is invalid, return the first valid ep found, otherwise + * look for exactly the ep with address equal to xfer_ep. */ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, - int xfer) + int xfer, int xfer_ep) { struct usb_host_endpoint *ep; int i, attr; @@ -616,7 +619,8 @@ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; if (attr == xfer && ep->desc.wMaxPacketSize != 0 - && usb_endpoint_dir_in(&ep->desc)) + && usb_endpoint_dir_in(&ep->desc) + && (xfer_ep < 0 || ep->desc.bEndpointAddress == xfer_ep)) return ep; } return NULL; @@ -689,7 +693,8 @@ static int build_isoc_ep_tb(struct gspca_dev *gspca_dev, found = 0; for (j = 0; j < nbalt; j++) { ep = alt_xfer(&intf->altsetting[j], - USB_ENDPOINT_XFER_ISOC); + USB_ENDPOINT_XFER_ISOC, + gspca_dev->xfer_ep); if (ep == NULL) continue; if (ep->desc.bInterval == 0) { @@ -862,7 +867,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) /* if bulk or the subdriver forced an altsetting, get the endpoint */ if (gspca_dev->alt != 0) { gspca_dev->alt--; /* (previous version compatibility) */ - ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer); + ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer, + gspca_dev->xfer_ep); if (ep == NULL) { pr_err("bad altsetting %d\n", gspca_dev->alt); return -EIO; @@ -904,7 +910,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) if (!gspca_dev->cam.no_urb_create) { PDEBUG(D_STREAM, "init transfer alt %d", alt); ret = create_urbs(gspca_dev, - alt_xfer(&intf->altsetting[alt], xfer)); + alt_xfer(&intf->altsetting[alt], xfer, + gspca_dev->xfer_ep)); if (ret < 0) { destroy_urbs(gspca_dev); goto out; @@ -2030,6 +2037,7 @@ int gspca_dev_probe2(struct usb_interface *intf, } gspca_dev->dev = dev; gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber; + gspca_dev->xfer_ep = -1; /* check if any audio device */ if (dev->actconfig->desc.bNumInterfaces != 1) { diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h index 300642d..f06253c 100644 --- a/drivers/media/usb/gspca/gspca.h +++ b/drivers/media/usb/gspca/gspca.h @@ -205,6 +205,7 @@ struct gspca_dev { char memory; /* memory type (V4L2_MEMORY_xxx) */ __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ + int xfer_ep; /* USB transfer endpoint address */ u8 audio; /* presence of audio device */ /* (*) These variables are proteced by both usb_lock and queue_lock, -- cgit v0.10.2 From c87c0f079cf8c9c346adbedd367ba53ad0afaded Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Wed, 25 Jun 2014 06:27:57 -0300 Subject: [media] gspca_kinect: add support for the depth stream Add support for the depth stream at 10bpp, for now use a 'depth_mode' command line parameter to switch between video and depth mode. Signed-off-by: Alexander Sosna Signed-off-by: Antonio Ospite Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c index 081f051..45bc1f5 100644 --- a/drivers/media/usb/gspca/kinect.c +++ b/drivers/media/usb/gspca/kinect.c @@ -36,6 +36,8 @@ MODULE_AUTHOR("Antonio Ospite "); MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver"); MODULE_LICENSE("GPL"); +static bool depth_mode; + struct pkt_hdr { uint8_t magic[2]; uint8_t pad; @@ -73,6 +75,14 @@ struct sd { #define FPS_HIGH 0x0100 +static const struct v4l2_pix_format depth_camera_mode[] = { + {640, 480, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE, + .bytesperline = 640 * 10 / 8, + .sizeimage = 640 * 480 * 10 / 8, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = MODE_640x488 | FORMAT_Y10B}, +}; + static const struct v4l2_pix_format video_camera_mode[] = { {640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, .bytesperline = 640, @@ -219,7 +229,7 @@ static int write_register(struct gspca_dev *gspca_dev, uint16_t reg, } /* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, +static int sd_config_video(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; @@ -227,8 +237,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->cam_tag = 0; - /* Only video stream is supported for now, - * which has stream flag = 0x80 */ sd->stream_flag = 0x80; cam = &gspca_dev->cam; @@ -236,6 +244,8 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = video_camera_mode; cam->nmodes = ARRAY_SIZE(video_camera_mode); + gspca_dev->xfer_ep = 0x81; + #if 0 /* Setting those values is not needed for video stream */ cam->npkt = 15; @@ -245,6 +255,26 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } +static int sd_config_depth(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + sd->cam_tag = 0; + + sd->stream_flag = 0x70; + + cam = &gspca_dev->cam; + + cam->cam_mode = depth_camera_mode; + cam->nmodes = ARRAY_SIZE(depth_camera_mode); + + gspca_dev->xfer_ep = 0x82; + + return 0; +} + /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { @@ -253,7 +283,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static int sd_start(struct gspca_dev *gspca_dev) +static int sd_start_video(struct gspca_dev *gspca_dev) { int mode; uint8_t fmt_reg, fmt_val; @@ -325,12 +355,39 @@ static int sd_start(struct gspca_dev *gspca_dev) return 0; } -static void sd_stopN(struct gspca_dev *gspca_dev) +static int sd_start_depth(struct gspca_dev *gspca_dev) +{ + /* turn off IR-reset function */ + write_register(gspca_dev, 0x105, 0x00); + + /* reset depth stream */ + write_register(gspca_dev, 0x06, 0x00); + /* Depth Stream Format 0x03: 11 bit stream | 0x02: 10 bit */ + write_register(gspca_dev, 0x12, 0x02); + /* Depth Stream Resolution 1: standard (640x480) */ + write_register(gspca_dev, 0x13, 0x01); + /* Depth Framerate / 0x1e (30): 30 fps */ + write_register(gspca_dev, 0x14, 0x1e); + /* Depth Stream Control / 2: Open Depth Stream */ + write_register(gspca_dev, 0x06, 0x02); + /* disable depth hflip / LSB = 0: Smoothing Disabled */ + write_register(gspca_dev, 0x17, 0x00); + + return 0; +} + +static void sd_stopN_video(struct gspca_dev *gspca_dev) { /* reset video stream */ write_register(gspca_dev, 0x05, 0x00); } +static void sd_stopN_depth(struct gspca_dev *gspca_dev) +{ + /* reset depth stream */ + write_register(gspca_dev, 0x06, 0x00); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len) { struct sd *sd = (struct sd *) gspca_dev; @@ -366,12 +423,24 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len) } /* sub-driver description */ -static const struct sd_desc sd_desc = { +static const struct sd_desc sd_desc_video = { .name = MODULE_NAME, - .config = sd_config, + .config = sd_config_video, .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, + .start = sd_start_video, + .stopN = sd_stopN_video, + .pkt_scan = sd_pkt_scan, + /* + .get_streamparm = sd_get_streamparm, + .set_streamparm = sd_set_streamparm, + */ +}; +static const struct sd_desc sd_desc_depth = { + .name = MODULE_NAME, + .config = sd_config_depth, + .init = sd_init, + .start = sd_start_depth, + .stopN = sd_stopN_depth, .pkt_scan = sd_pkt_scan, /* .get_streamparm = sd_get_streamparm, @@ -391,8 +460,12 @@ MODULE_DEVICE_TABLE(usb, device_table); /* -- device connect -- */ static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); + if (depth_mode) + return gspca_dev_probe(intf, id, &sd_desc_depth, + sizeof(struct sd), THIS_MODULE); + else + return gspca_dev_probe(intf, id, &sd_desc_video, + sizeof(struct sd), THIS_MODULE); } static struct usb_driver sd_driver = { @@ -408,3 +481,6 @@ static struct usb_driver sd_driver = { }; module_usb_driver(sd_driver); + +module_param(depth_mode, bool, 0644); +MODULE_PARM_DESC(depth_mode, "0=video 1=depth"); -- cgit v0.10.2 From 22f44249d3fc913a8c9c8671e1554f30cdebb885 Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Sun, 8 Jun 2014 15:08:09 -0300 Subject: [media] dt: bindings: Add binding documentation for sunxi IR controller This patch adds documentation for Device-Tree bindings for sunxi IR controller. Signed-off-by: Alexander Bersenev Signed-off-by: Alexsey Shestacov [hdegoede@redhat.com: Changed compatible to sun4i-a10-ir] Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/sunxi-ir.txt b/Documentation/devicetree/bindings/media/sunxi-ir.txt new file mode 100644 index 0000000..23dd5ad --- /dev/null +++ b/Documentation/devicetree/bindings/media/sunxi-ir.txt @@ -0,0 +1,23 @@ +Device-Tree bindings for SUNXI IR controller found in sunXi SoC family + +Required properties: +- compatible : should be "allwinner,sun4i-a10-ir"; +- clocks : list of clock specifiers, corresponding to + entries in clock-names property; +- clock-names : should contain "apb" and "ir" entries; +- interrupts : should contain IR IRQ number; +- reg : should contain IO map address for IR. + +Optional properties: +- linux,rc-map-name : Remote control map name. + +Example: + +ir0: ir@01c21800 { + compatible = "allwinner,sun4i-a10-ir"; + clocks = <&apb0_gates 6>, <&ir0_clk>; + clock-names = "apb", "ir"; + interrupts = <0 5 1>; + reg = <0x01C21800 0x40>; + linux,rc-map-name = "rc-rc6-mce"; +}; -- cgit v0.10.2 From b4e3e59fb59c214c5bcf9d1bf2971f100e0dac4f Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Sun, 8 Jun 2014 15:08:10 -0300 Subject: [media] rc: add sunxi-ir driver This patch adds driver for sunxi IR controller. It is based on Alexsey Shestacov's work based on the original driver supplied by Allwinner. Signed-off-by: Alexander Bersenev Signed-off-by: Alexsey Shestacov [hdegoede@redhat.com: Changed compatible to sun4i-a10-ir] Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 8fbd377..9427fad 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -343,4 +343,14 @@ config RC_ST If you're not sure, select N here. +config IR_SUNXI + tristate "SUNXI IR remote control" + depends on RC_CORE + depends on ARCH_SUNXI + ---help--- + Say Y if you want to use sunXi internal IR Controller + + To compile this driver as a module, choose M here: the module will + be called sunxi-ir. + endif #RC_DEVICES diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index f8b54ff..9ee9ee7 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -32,4 +32,5 @@ obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o obj-$(CONFIG_IR_IGUANA) += iguanair.o obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o obj-$(CONFIG_RC_ST) += st_rc.o +obj-$(CONFIG_IR_SUNXI) += sunxi-cir.o obj-$(CONFIG_IR_IMG) += img-ir/ diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c new file mode 100644 index 0000000..13dcd80 --- /dev/null +++ b/drivers/media/rc/sunxi-cir.c @@ -0,0 +1,318 @@ +/* + * Driver for Allwinner sunXi IR controller + * + * Copyright (C) 2014 Alexsey Shestacov + * Copyright (C) 2014 Alexander Bersenev + * + * Based on sun5i-ir.c: + * Copyright (C) 2007-2012 Daniel Wang + * Allwinner Technology Co., Ltd. + * + * 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 +#include +#include +#include +#include + +#define SUNXI_IR_DEV "sunxi-ir" + +/* Registers */ +/* IR Control */ +#define SUNXI_IR_CTL_REG 0x00 +/* Global Enable */ +#define REG_CTL_GEN BIT(0) +/* RX block enable */ +#define REG_CTL_RXEN BIT(1) +/* CIR mode */ +#define REG_CTL_MD (BIT(4) | BIT(5)) + +/* Rx Config */ +#define SUNXI_IR_RXCTL_REG 0x10 +/* Pulse Polarity Invert flag */ +#define REG_RXCTL_RPPI BIT(2) + +/* Rx Data */ +#define SUNXI_IR_RXFIFO_REG 0x20 + +/* Rx Interrupt Enable */ +#define SUNXI_IR_RXINT_REG 0x2C +/* Rx FIFO Overflow */ +#define REG_RXINT_ROI_EN BIT(0) +/* Rx Packet End */ +#define REG_RXINT_RPEI_EN BIT(1) +/* Rx FIFO Data Available */ +#define REG_RXINT_RAI_EN BIT(4) + +/* Rx FIFO available byte level */ +#define REG_RXINT_RAL(val) (((val) << 8) & (GENMASK(11, 8))) + +/* Rx Interrupt Status */ +#define SUNXI_IR_RXSTA_REG 0x30 +/* RX FIFO Get Available Counter */ +#define REG_RXSTA_GET_AC(val) (((val) >> 8) & (GENMASK(5, 0))) +/* Clear all interrupt status value */ +#define REG_RXSTA_CLEARALL 0xff + +/* IR Sample Config */ +#define SUNXI_IR_CIR_REG 0x34 +/* CIR_REG register noise threshold */ +#define REG_CIR_NTHR(val) (((val) << 2) & (GENMASK(7, 2))) +/* CIR_REG register idle threshold */ +#define REG_CIR_ITHR(val) (((val) << 8) & (GENMASK(15, 8))) + +/* Hardware supported fifo size */ +#define SUNXI_IR_FIFO_SIZE 16 +/* How many messages in FIFO trigger IRQ */ +#define TRIGGER_LEVEL 8 +/* Required frequency for IR0 or IR1 clock in CIR mode */ +#define SUNXI_IR_BASE_CLK 8000000 +/* Frequency after IR internal divider */ +#define SUNXI_IR_CLK (SUNXI_IR_BASE_CLK / 64) +/* Sample period in ns */ +#define SUNXI_IR_SAMPLE (1000000000ul / SUNXI_IR_CLK) +/* Noise threshold in samples */ +#define SUNXI_IR_RXNOISE 1 +/* Idle Threshold in samples */ +#define SUNXI_IR_RXIDLE 20 +/* Time after which device stops sending data in ms */ +#define SUNXI_IR_TIMEOUT 120 + +struct sunxi_ir { + spinlock_t ir_lock; + struct rc_dev *rc; + void __iomem *base; + int irq; + struct clk *clk; + struct clk *apb_clk; + const char *map_name; +}; + +static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) +{ + unsigned long status; + unsigned char dt; + unsigned int cnt, rc; + struct sunxi_ir *ir = dev_id; + DEFINE_IR_RAW_EVENT(rawir); + + spin_lock(&ir->ir_lock); + + status = readl(ir->base + SUNXI_IR_RXSTA_REG); + + /* clean all pending statuses */ + writel(status | REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); + + if (status & REG_RXINT_RAI_EN) { + /* How many messages in fifo */ + rc = REG_RXSTA_GET_AC(status); + /* Sanity check */ + rc = rc > SUNXI_IR_FIFO_SIZE ? SUNXI_IR_FIFO_SIZE : rc; + /* If we have data */ + for (cnt = 0; cnt < rc; cnt++) { + /* for each bit in fifo */ + dt = readb(ir->base + SUNXI_IR_RXFIFO_REG); + rawir.pulse = (dt & 0x80) != 0; + rawir.duration = ((dt & 0x7f) + 1) * SUNXI_IR_SAMPLE; + ir_raw_event_store_with_filter(ir->rc, &rawir); + } + } + + if (status & REG_RXINT_ROI_EN) { + ir_raw_event_reset(ir->rc); + } else if (status & REG_RXINT_RPEI_EN) { + ir_raw_event_set_idle(ir->rc, true); + ir_raw_event_handle(ir->rc); + } + + spin_unlock(&ir->ir_lock); + + return IRQ_HANDLED; +} + +static int sunxi_ir_probe(struct platform_device *pdev) +{ + int ret = 0; + unsigned long tmp = 0; + + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node; + struct resource *res; + struct sunxi_ir *ir; + + ir = devm_kzalloc(dev, sizeof(struct sunxi_ir), GFP_KERNEL); + if (!ir) + return -ENOMEM; + + /* Clock */ + ir->apb_clk = devm_clk_get(dev, "apb"); + if (IS_ERR(ir->apb_clk)) { + dev_err(dev, "failed to get a apb clock.\n"); + return PTR_ERR(ir->apb_clk); + } + ir->clk = devm_clk_get(dev, "ir"); + if (IS_ERR(ir->clk)) { + dev_err(dev, "failed to get a ir clock.\n"); + return PTR_ERR(ir->clk); + } + + ret = clk_set_rate(ir->clk, SUNXI_IR_BASE_CLK); + if (ret) { + dev_err(dev, "set ir base clock failed!\n"); + return ret; + } + + if (clk_prepare_enable(ir->apb_clk)) { + dev_err(dev, "try to enable apb_ir_clk failed\n"); + return -EINVAL; + } + + if (clk_prepare_enable(ir->clk)) { + dev_err(dev, "try to enable ir_clk failed\n"); + ret = -EINVAL; + goto exit_clkdisable_apb_clk; + } + + /* IO */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ir->base = devm_ioremap_resource(dev, res); + if (IS_ERR(ir->base)) { + dev_err(dev, "failed to map registers\n"); + ret = PTR_ERR(ir->base); + goto exit_clkdisable_clk; + } + + ir->rc = rc_allocate_device(); + if (!ir->rc) { + dev_err(dev, "failed to allocate device\n"); + ret = -ENOMEM; + goto exit_clkdisable_clk; + } + + ir->rc->priv = ir; + ir->rc->input_name = SUNXI_IR_DEV; + ir->rc->input_phys = "sunxi-ir/input0"; + ir->rc->input_id.bustype = BUS_HOST; + ir->rc->input_id.vendor = 0x0001; + ir->rc->input_id.product = 0x0001; + ir->rc->input_id.version = 0x0100; + ir->map_name = of_get_property(dn, "linux,rc-map-name", NULL); + ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY; + ir->rc->dev.parent = dev; + ir->rc->driver_type = RC_DRIVER_IR_RAW; + rc_set_allowed_protocols(ir->rc, RC_BIT_ALL); + ir->rc->rx_resolution = SUNXI_IR_SAMPLE; + ir->rc->timeout = MS_TO_NS(SUNXI_IR_TIMEOUT); + ir->rc->driver_name = SUNXI_IR_DEV; + + ret = rc_register_device(ir->rc); + if (ret) { + dev_err(dev, "failed to register rc device\n"); + goto exit_free_dev; + } + + platform_set_drvdata(pdev, ir); + + /* IRQ */ + ir->irq = platform_get_irq(pdev, 0); + if (ir->irq < 0) { + dev_err(dev, "no irq resource\n"); + ret = ir->irq; + goto exit_free_dev; + } + + ret = devm_request_irq(dev, ir->irq, sunxi_ir_irq, 0, SUNXI_IR_DEV, ir); + if (ret) { + dev_err(dev, "failed request irq\n"); + goto exit_free_dev; + } + + /* Enable CIR Mode */ + writel(REG_CTL_MD, ir->base+SUNXI_IR_CTL_REG); + + /* Set noise threshold and idle threshold */ + writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE)|REG_CIR_ITHR(SUNXI_IR_RXIDLE), + ir->base + SUNXI_IR_CIR_REG); + + /* Invert Input Signal */ + writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG); + + /* Clear All Rx Interrupt Status */ + writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); + + /* + * Enable IRQ on overflow, packet end, FIFO available with trigger + * level + */ + writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN | + REG_RXINT_RAI_EN | REG_RXINT_RAL(TRIGGER_LEVEL - 1), + ir->base + SUNXI_IR_RXINT_REG); + + /* Enable IR Module */ + tmp = readl(ir->base + SUNXI_IR_CTL_REG); + writel(tmp | REG_CTL_GEN | REG_CTL_RXEN, ir->base + SUNXI_IR_CTL_REG); + + dev_info(dev, "initialized sunXi IR driver\n"); + return 0; + +exit_free_dev: + rc_free_device(ir->rc); +exit_clkdisable_clk: + clk_disable_unprepare(ir->clk); +exit_clkdisable_apb_clk: + clk_disable_unprepare(ir->apb_clk); + + return ret; +} + +static int sunxi_ir_remove(struct platform_device *pdev) +{ + unsigned long flags; + struct sunxi_ir *ir = platform_get_drvdata(pdev); + + clk_disable_unprepare(ir->clk); + clk_disable_unprepare(ir->apb_clk); + + spin_lock_irqsave(&ir->ir_lock, flags); + /* disable IR IRQ */ + writel(0, ir->base + SUNXI_IR_RXINT_REG); + /* clear All Rx Interrupt Status */ + writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); + /* disable IR */ + writel(0, ir->base + SUNXI_IR_CTL_REG); + spin_unlock_irqrestore(&ir->ir_lock, flags); + + rc_unregister_device(ir->rc); + return 0; +} + +static const struct of_device_id sunxi_ir_match[] = { + { .compatible = "allwinner,sun4i-a10-ir", }, + {}, +}; + +static struct platform_driver sunxi_ir_driver = { + .probe = sunxi_ir_probe, + .remove = sunxi_ir_remove, + .driver = { + .name = SUNXI_IR_DEV, + .owner = THIS_MODULE, + .of_match_table = sunxi_ir_match, + }, +}; + +module_platform_driver(sunxi_ir_driver); + +MODULE_DESCRIPTION("Allwinner sunXi IR controller driver"); +MODULE_AUTHOR("Alexsey Shestacov "); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 690130fe0b4c1a72c931e9aafbec8f21dbeed20c Mon Sep 17 00:00:00 2001 From: Alan Date: Mon, 20 Jan 2014 15:03:04 -0300 Subject: [media] dvb-frontends: Add static Add static to tda m_* variables in the header. They don't need to be global. With some cleanup they could probably even be marked const. Resolves-bug: https://bugzilla.kernel.org/show_bug.cgi?id=68191 Reported-by: Christian Schneider Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/tda18271c2dd_maps.h b/drivers/media/dvb-frontends/tda18271c2dd_maps.h index b87661b..f3bca5c 100644 --- a/drivers/media/dvb-frontends/tda18271c2dd_maps.h +++ b/drivers/media/dvb-frontends/tda18271c2dd_maps.h @@ -5,7 +5,7 @@ enum HF_S { HF_DVBC_8MHZ, HF_DVBC }; -struct SStandardParam m_StandardTable[] = { +static struct SStandardParam m_StandardTable[] = { { 0, 0, 0x00, 0x00 }, /* HF_None */ { 6000000, 7000000, 0x1D, 0x2C }, /* HF_B, */ { 6900000, 8000000, 0x1E, 0x2C }, /* HF_DK, */ @@ -27,7 +27,7 @@ struct SStandardParam m_StandardTable[] = { { 0, 0, 0x00, 0x00 }, /* HF_DVBC (Unused) */ }; -struct SMap m_BP_Filter_Map[] = { +static struct SMap m_BP_Filter_Map[] = { { 62000000, 0x00 }, { 84000000, 0x01 }, { 100000000, 0x02 }, @@ -799,14 +799,14 @@ static struct SRFBandMap m_RF_Band_Map[7] = { { 865000000, 489500000, 697500000, 842000000}, }; -u8 m_Thermometer_Map_1[16] = { +static u8 m_Thermometer_Map_1[16] = { 60, 62, 66, 64, 74, 72, 68, 70, 90, 88, 84, 86, 76, 78, 82, 80, }; -u8 m_Thermometer_Map_2[16] = { +static u8 m_Thermometer_Map_2[16] = { 92, 94, 98, 96, 106, 104, 100, 102, 122, 120, 116, 118, diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 96ccfeb..a0db64f 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1545,7 +1545,7 @@ static int r820t_imr_cross(struct r820t_priv *priv, cross[i].value = rc; if (cross[i].value < tmp.value) - memcpy(&tmp, &cross[i], sizeof(tmp)); + tmp = cross[i]; } if ((tmp.phase_y & 0x1f) == 1) { /* y-direction */ -- cgit v0.10.2 From 20f63150a21598c38e2f32cedb235319b99436f8 Mon Sep 17 00:00:00 2001 From: Benoit Taine Date: Mon, 26 May 2014 12:21:26 -0300 Subject: [media] drx-j: Use kmemdup instead of kmalloc + memcpy This issue was reported by coccicheck using the semantic patch at scripts/coccinelle/api/memdup.cocci Signed-off-by: Benoit Taine Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 9482954..3795f65 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -12272,22 +12272,20 @@ struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c) if (demod == NULL) goto error; - demod_addr = kmalloc(sizeof(struct i2c_device_addr), GFP_KERNEL); + demod_addr = kmemdup(&drxj_default_addr_g, + sizeof(struct i2c_device_addr), GFP_KERNEL); if (demod_addr == NULL) goto error; - memcpy(demod_addr, &drxj_default_addr_g, - sizeof(struct i2c_device_addr)); - demod_comm_attr = kmalloc(sizeof(struct drx_common_attr), GFP_KERNEL); + demod_comm_attr = kmemdup(&drxj_default_comm_attr_g, + sizeof(struct drx_common_attr), GFP_KERNEL); if (demod_comm_attr == NULL) goto error; - memcpy(demod_comm_attr, &drxj_default_comm_attr_g, - sizeof(struct drx_common_attr)); - demod_ext_attr = kmalloc(sizeof(struct drxj_data), GFP_KERNEL); + demod_ext_attr = kmemdup(&drxj_data_g, sizeof(struct drxj_data), + GFP_KERNEL); if (demod_ext_attr == NULL) goto error; - memcpy(demod_ext_attr, &drxj_data_g, sizeof(struct drxj_data)); /* setup the state */ state->i2c = i2c; -- cgit v0.10.2 From e22eca78574b52ef8320aacdaa5987ed8bca778d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 27 May 2014 04:12:19 -0300 Subject: [media] em28xx: add MSI Digivox Trio support Add support for the MSI Digivox Trio device. Signed-off-by: P. van Gaans Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 15ad470..9da812b 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2280,6 +2280,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2875), .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2885), /* MSI Digivox Trio */ + .driver_info = EM2884_BOARD_TERRATEC_H5 }, { USB_DEVICE(0xeb1a, 0xe300), .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U }, { USB_DEVICE(0xeb1a, 0xe303), -- cgit v0.10.2 From 931387fa941ec0da2f4a6999bec69bc608af311e Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 31 May 2014 10:14:08 -0300 Subject: [media] drivers/media/usb/usbvision/usbvision-core.c: Remove useless return variables This patch remove variables that are initialized with a constant, are never updated, and are only used as parameter of return. Return the constant instead of using a variable. Verified by compilation only. The coccinelle script that find and fixes this issue is: // @@ type T; constant C; identifier ret; @@ - T ret = C; ... when != ret when strict return - ret + C ; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index 816b1cf..302aa07 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -1463,8 +1463,6 @@ static int usbvision_write_reg_irq(struct usb_usbvision *usbvision, int address, static int usbvision_init_compression(struct usb_usbvision *usbvision) { - int err_code = 0; - usbvision->last_isoc_frame_num = -1; usbvision->isoc_data_count = 0; usbvision->isoc_packet_count = 0; @@ -1475,7 +1473,7 @@ static int usbvision_init_compression(struct usb_usbvision *usbvision) usbvision->request_intra = 1; usbvision->isoc_measure_bandwidth_count = 0; - return err_code; + return 0; } /* this function measures the used bandwidth since last call @@ -1484,11 +1482,9 @@ static int usbvision_init_compression(struct usb_usbvision *usbvision) */ static int usbvision_measure_bandwidth(struct usb_usbvision *usbvision) { - int err_code = 0; - if (usbvision->isoc_measure_bandwidth_count < 2) { /* this gives an average bandwidth of 3 frames */ usbvision->isoc_measure_bandwidth_count++; - return err_code; + return 0; } if ((usbvision->isoc_packet_size > 0) && (usbvision->isoc_packet_count > 0)) { usbvision->used_bandwidth = usbvision->isoc_data_count / @@ -1499,7 +1495,7 @@ static int usbvision_measure_bandwidth(struct usb_usbvision *usbvision) usbvision->isoc_data_count = 0; usbvision->isoc_packet_count = 0; usbvision->isoc_skip_count = 0; - return err_code; + return 0; } static int usbvision_adjust_compression(struct usb_usbvision *usbvision) @@ -1546,26 +1542,24 @@ static int usbvision_adjust_compression(struct usb_usbvision *usbvision) static int usbvision_request_intra(struct usb_usbvision *usbvision) { - int err_code = 0; unsigned char buffer[1]; PDEBUG(DBG_IRQ, ""); usbvision->request_intra = 1; buffer[0] = 1; usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1); - return err_code; + return 0; } static int usbvision_unrequest_intra(struct usb_usbvision *usbvision) { - int err_code = 0; unsigned char buffer[1]; PDEBUG(DBG_IRQ, ""); usbvision->request_intra = 0; buffer[0] = 0; usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1); - return err_code; + return 0; } /******************************* -- cgit v0.10.2 From 4df16f702c38221f611006bda45377278b2ec325 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 31 May 2014 13:30:52 -0300 Subject: [media] drivers/media: Remove useless return variables This patch remove variables that are initialized with a constant, are never updated, and are only used as parameter of return. Return the constant instead of using a variable. Verified by compilation only. The coccinelle script that find and fixes this issue is: // @@ type T; constant C; identifier ret; @@ - T ret = C; ... when != ret when strict return - ret + C ; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c index 970e833..826228c 100644 --- a/drivers/media/pci/ngene/ngene-core.c +++ b/drivers/media/pci/ngene/ngene-core.c @@ -910,7 +910,6 @@ static int AllocateRingBuffers(struct pci_dev *pci_dev, { dma_addr_t tmp; u32 i, j; - int status = 0; u32 SCListMemSize = pRingBuffer->NumBuffers * ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) : NUM_SCATTER_GATHER_ENTRIES) @@ -1010,14 +1009,12 @@ static int AllocateRingBuffers(struct pci_dev *pci_dev, } - return status; + return 0; } static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer, struct SRingBufferDescriptor *pRingBuffer) { - int status = 0; - /* Copy pointer to scatter gather list in TSRingbuffer structure for buffer 2 Load number of buffer @@ -1038,7 +1035,7 @@ static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer, pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1; Cur = Cur->Next; } - return status; + return 0; } static u32 RingBufferSizes[MAX_STREAM] = { diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 1f87513..cba7fea 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -208,7 +208,7 @@ static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q, static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) { struct cx231xx_dmaqueue *dma_q = urb->context; - int i, rc = 1; + int i; unsigned char *p_buffer; u32 bytes_parsed = 0, buffer_size = 0; u8 sav_eav = 0; @@ -299,13 +299,12 @@ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) bytes_parsed = 0; } - return rc; + return 1; } static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) { struct cx231xx_dmaqueue *dma_q = urb->context; - int rc = 1; unsigned char *p_buffer; u32 bytes_parsed = 0, buffer_size = 0; u8 sav_eav = 0; @@ -379,7 +378,7 @@ static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) bytes_parsed = 0; } - return rc; + return 1; } @@ -1620,7 +1619,7 @@ static int radio_s_tuner(struct file *file, void *priv, const struct v4l2_tuner */ static int cx231xx_v4l2_open(struct file *filp) { - int errCode = 0, radio = 0; + int radio = 0; struct video_device *vdev = video_devdata(filp); struct cx231xx *dev = video_drvdata(filp); struct cx231xx_fh *fh; @@ -1718,7 +1717,7 @@ static int cx231xx_v4l2_open(struct file *filp) mutex_unlock(&dev->lock); v4l2_fh_add(&fh->fh); - return errCode; + return 0; } /* -- cgit v0.10.2 From e5a89998d4c43cd36397e1d9e061bfdab0bcd094 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 1 Jun 2014 09:29:19 -0300 Subject: [media] media: usb: dvb-usb-v2: mxl111sf.c: Cleaning up uninitialized variables There is a risk that the variable will be used without being initialized. This was largely found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c index c7304fa..b8a707e 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -129,7 +129,7 @@ int mxl111sf_write_reg_mask(struct mxl111sf_state *state, u8 addr, u8 mask, u8 data) { int ret; - u8 val; + u8 val = 0; if (mask != 0xff) { ret = mxl111sf_read_reg(state, addr, &val); -- cgit v0.10.2 From 8effbff4e2b666645c7e1db18631323501c70196 Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Mon, 9 Jun 2014 12:55:25 -0300 Subject: [media] update reference, kerneltrap.org no longer works kerneltrap.org no longer works, update to a working reference Signed-off-by: Pranith Kumar Acked-by: Alexey Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index a360227..f476071 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -32,7 +32,7 @@ * achievements (specifications given). * Also, Faidon Liambotis wrote nice driver for this radio * in 2007. He allowed to use his driver to improve current mr800 radio driver. - * http://kerneltrap.org/mailarchive/linux-usb-devel/2007/10/11/342492 + * http://www.spinics.net/lists/linux-usb-devel/msg10109.html * * Version 0.01: First working version. * It's required to blacklist AverMedia USB Radio -- cgit v0.10.2 From eeff336ccf6b812821e7bf534e554986efc3e96f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 16 Jun 2014 05:13:55 -0300 Subject: [media] DocBook media: fix small typo know -> known Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index a086a5d..8c4ee74 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -1066,7 +1066,7 @@ state, in the application domain so to say. Drivers set or clear this flag when calling the VIDIOC_DQBUF ioctl. It may be set by video capture devices when the buffer contains a compressed image which is a -key frame (or field), &ie; can be decompressed on its own. Also know as +key frame (or field), &ie; can be decompressed on its own. Also known as an I-frame. Applications can set this bit when type refers to an output stream. -- cgit v0.10.2 From e7d40c37215b2048685e72a0bb0101578c2b0a62 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Fri, 30 May 2014 19:20:11 -0300 Subject: [media] tlg2300: fix leak at failure path in poseidon_probe() Error handling code in poseidon_probe() misses usb_put_intf() and usb_put_dev(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/tlg2300/pd-main.c b/drivers/media/usb/tlg2300/pd-main.c index 3316caa..b31f479 100644 --- a/drivers/media/usb/tlg2300/pd-main.c +++ b/drivers/media/usb/tlg2300/pd-main.c @@ -476,6 +476,8 @@ err_audio: err_video: v4l2_device_unregister(&pd->v4l2_dev); err_v4l2: + usb_put_intf(pd->interface); + usb_put_dev(pd->udev); kfree(pd); return ret; } -- cgit v0.10.2 From 85594960f54aebcf2bd3ef4d57d77fa1bfc7e1e2 Mon Sep 17 00:00:00 2001 From: Anthony DeStefano Date: Wed, 9 Apr 2014 23:08:49 -0300 Subject: [media] staging: rtl2832_sdr: fixup checkpatch/style issues rtl2832_sdr.c: fixup checkpatch issues about long lines Aligned stuff under the ( for this version. Signed-off-by: Anthony DeStefano Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c index 093df6b..d09a596 100644 --- a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c +++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c @@ -935,7 +935,8 @@ static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s) /* * bandwidth (Hz) */ - bandwidth_auto = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); + bandwidth_auto = v4l2_ctrl_find(&s->hdl, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); if (v4l2_ctrl_g_ctrl(bandwidth_auto)) { c->bandwidth_hz = s->f_adc; @@ -1329,9 +1330,11 @@ static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) /* Round towards the closest legal value */ s32 val = s->f_adc + s->bandwidth->step / 2; u32 offset; - val = clamp(val, s->bandwidth->minimum, s->bandwidth->maximum); + val = clamp(val, s->bandwidth->minimum, + s->bandwidth->maximum); offset = val - s->bandwidth->minimum; - offset = s->bandwidth->step * (offset / s->bandwidth->step); + offset = s->bandwidth->step * + (offset / s->bandwidth->step); s->bandwidth->val = s->bandwidth->minimum + offset; } @@ -1420,15 +1423,24 @@ struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, break; case RTL2832_TUNER_R820T: v4l2_ctrl_handler_init(&s->hdl, 2); - s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1); - s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH, 0, 8000000, 100000, 0); + s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, + 0, 1, 1, 1); + s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH, + 0, 8000000, 100000, 0); v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); break; case RTL2832_TUNER_FC0012: case RTL2832_TUNER_FC0013: v4l2_ctrl_handler_init(&s->hdl, 2); - s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1); - s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH, 6000000, 8000000, 1000000, 6000000); + s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, + 0, 1, 1, 1); + s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH, + 6000000, 8000000, 1000000, + 6000000); v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); break; default: -- cgit v0.10.2 From 2abfeef620171db94ef59b85f7a81561019eda20 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Thu, 22 May 2014 16:42:13 -0300 Subject: [media] dm644x_ccdc: remove check for CONFIG_DM644X_VIDEO_PORT_ENABLE A check for CONFIG_DM644X_VIDEO_PORT_ENABLE was added in v2.6.32. The related Kconfig symbol was never added so this check has always evaluated to false. Remove that check. Signed-off-by: Paul Bolle Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c index 30fa084..07e98df 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc.c +++ b/drivers/media/platform/davinci/dm644x_ccdc.c @@ -581,13 +581,8 @@ void ccdc_config_raw(void) config_params->alaw.enable) syn_mode |= CCDC_DATA_PACK_ENABLE; -#ifdef CONFIG_DM644X_VIDEO_PORT_ENABLE - /* enable video port */ - val = CCDC_ENABLE_VIDEO_PORT; -#else /* disable video port */ val = CCDC_DISABLE_VIDEO_PORT; -#endif if (config_params->data_sz == CCDC_DATA_8BITS) val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK) -- cgit v0.10.2 From 941a8204d0bb92a20542c6a88ec9f2acb4b72d83 Mon Sep 17 00:00:00 2001 From: Ovidiu Toader Date: Mon, 2 Jun 2014 21:38:06 -0300 Subject: [media] staging/media/rtl2832u_sdr: fix coding style problems by adding blank lines This minor patch fixes all WARNING:SPACING style warnings in rtl2832_sdr.c The new version of the file pleases checkpatch.pl when run with "--ignore LONG_LINE". Signed-off-by: Ovidiu Toader Acked-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c index d09a596..4d2e30f 100644 --- a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c +++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c @@ -348,6 +348,7 @@ static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s, /* convert u8 to u16 */ unsigned int i; u16 *u16dst = dst; + for (i = 0; i < src_len; i++) *u16dst++ = (src[i] << 8) | (src[i] >> 0); dst_len = 2 * src_len; @@ -359,6 +360,7 @@ static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s, if (unlikely(time_is_before_jiffies(s->jiffies_next))) { #define MSECS 10000UL unsigned int samples = s->sample - s->sample_measured; + s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); s->sample_measured = s->sample; dev_dbg(&s->udev->dev, @@ -560,11 +562,13 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s) static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s) { unsigned long flags = 0; + dev_dbg(&s->udev->dev, "%s:\n", __func__); spin_lock_irqsave(&s->queued_bufs_lock, flags); while (!list_empty(&s->queued_bufs)) { struct rtl2832_sdr_frame_buf *buf; + buf = list_entry(s->queued_bufs.next, struct rtl2832_sdr_frame_buf, list); list_del(&buf->list); @@ -577,6 +581,7 @@ static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s) static void rtl2832_sdr_release_sec(struct dvb_frontend *fe) { struct rtl2832_sdr_state *s = fe->sec_priv; + dev_dbg(&s->udev->dev, "%s:\n", __func__); mutex_lock(&s->vb_queue_lock); @@ -598,6 +603,7 @@ static int rtl2832_sdr_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { struct rtl2832_sdr_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s:\n", __func__); strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); @@ -615,6 +621,7 @@ static int rtl2832_sdr_queue_setup(struct vb2_queue *vq, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); + dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); /* Need at least 8 buffers */ @@ -665,6 +672,7 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s) u8 buf[4], u8tmp1, u8tmp2; u64 u64tmp; u32 u32tmp; + dev_dbg(&s->udev->dev, "%s: f_adc=%u\n", __func__, s->f_adc); if (!test_bit(POWER_ON, &s->flags)) @@ -988,6 +996,7 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) { struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); int ret; + dev_dbg(&s->udev->dev, "%s:\n", __func__); if (!s->udev) @@ -1036,6 +1045,7 @@ err: static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) { struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); + dev_dbg(&s->udev->dev, "%s:\n", __func__); mutex_lock(&s->v4l2_lock); @@ -1069,6 +1079,7 @@ static int rtl2832_sdr_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { struct rtl2832_sdr_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s: index=%d type=%d\n", __func__, v->index, v->type); @@ -1095,6 +1106,7 @@ static int rtl2832_sdr_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *v) { struct rtl2832_sdr_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s:\n", __func__); if (v->index > 1) @@ -1106,6 +1118,7 @@ static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv, struct v4l2_frequency_band *band) { struct rtl2832_sdr_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", __func__, band->tuner, band->type, band->index); @@ -1131,6 +1144,7 @@ static int rtl2832_sdr_g_frequency(struct file *file, void *priv, { struct rtl2832_sdr_state *s = video_drvdata(file); int ret = 0; + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", __func__, f->tuner, f->type); @@ -1194,6 +1208,7 @@ static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct rtl2832_sdr_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s:\n", __func__); if (f->index >= NUM_FORMATS) @@ -1209,6 +1224,7 @@ static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_format *f) { struct rtl2832_sdr_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s:\n", __func__); f->fmt.sdr.pixelformat = s->pixelformat; @@ -1223,6 +1239,7 @@ static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv, struct rtl2832_sdr_state *s = video_drvdata(file); struct vb2_queue *q = &s->vb_queue; int i; + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, (char *)&f->fmt.sdr.pixelformat); @@ -1248,6 +1265,7 @@ static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv, { struct rtl2832_sdr_state *s = video_drvdata(file); int i; + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, (char *)&f->fmt.sdr.pixelformat); @@ -1317,6 +1335,7 @@ static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) struct dvb_frontend *fe = s->fe; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; + dev_dbg(&s->udev->dev, "%s: id=%d name=%s val=%d min=%d max=%d step=%d\n", __func__, ctrl->id, ctrl->name, ctrl->val, @@ -1330,6 +1349,7 @@ static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) /* Round towards the closest legal value */ s32 val = s->f_adc + s->bandwidth->step / 2; u32 offset; + val = clamp(val, s->bandwidth->minimum, s->bandwidth->maximum); offset = val - s->bandwidth->minimum; -- cgit v0.10.2 From 7979fcaaf3bacfa4796fbbf03a5916ff6fed7267 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 9 Jun 2014 12:21:08 -0300 Subject: [media] cx18: remove duplicate CX18_ALSA_DBGFLG_WARN define The CX18_ALSA_DBGFLG_WARN is cut and pasted twice and we can delete the second instance. Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx18/cx18-alsa.h b/drivers/media/pci/cx18/cx18-alsa.h index 447da37..2718be2 100644 --- a/drivers/media/pci/cx18/cx18-alsa.h +++ b/drivers/media/pci/cx18/cx18-alsa.h @@ -49,7 +49,6 @@ static inline void snd_cx18_unlock(struct snd_cx18_card *cxsc) } #define CX18_ALSA_DBGFLG_WARN (1 << 0) -#define CX18_ALSA_DBGFLG_WARN (1 << 0) #define CX18_ALSA_DBGFLG_INFO (1 << 1) #define CX18_ALSA_DEBUG(x, type, fmt, args...) \ -- cgit v0.10.2 From c0e11a2a24db782341f456877d19b9390c5998a9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 16 Jun 2014 04:47:58 -0300 Subject: [media] sn9c102: remove deprecated driver During the media summit meeting in Edinburgh it was decided to move this driver to staging as the first step to removing it altogether. Most webcams covered by this driver are now supported by gspca. Nobody has the hardware or is willing to convert the remaining devices to gspca. This driver needs a major overhaul to have it conform to the latest frameworks and compliancy tests. Without hardware, however, this is next to impossible. Given the fact that this driver seems to be pretty much unused (it has been removed from Fedora several versions ago and nobody complained about that), we decided to drop this driver. Signed-off-by: Hans Verkuil Cc: Luca Risolia Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 134483f..50f8134 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9499,15 +9499,6 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/usb/smsc95xx.* -USB SN9C1xx DRIVER -M: Luca Risolia -L: linux-usb@vger.kernel.org -L: linux-media@vger.kernel.org -T: git git://linuxtv.org/media_tree.git -W: http://www.linux-projects.org -S: Maintained -F: drivers/staging/media/sn9c102/ - USB SUBSYSTEM M: Greg Kroah-Hartman L: linux-usb@vger.kernel.org diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index a9f2e63..cd2af376 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -35,8 +35,6 @@ source "drivers/staging/media/msi3101/Kconfig" source "drivers/staging/media/omap24xx/Kconfig" -source "drivers/staging/media/sn9c102/Kconfig" - source "drivers/staging/media/solo6x10/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 8e2c5d2..2766a3e 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_USB_MSI3101) += msi3101/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ -obj-$(CONFIG_USB_SN9C102) += sn9c102/ obj-$(CONFIG_VIDEO_OMAP2) += omap24xx/ obj-$(CONFIG_VIDEO_TCM825X) += omap24xx/ obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832u_sdr/ diff --git a/drivers/staging/media/sn9c102/Kconfig b/drivers/staging/media/sn9c102/Kconfig deleted file mode 100644 index c9aba59..0000000 --- a/drivers/staging/media/sn9c102/Kconfig +++ /dev/null @@ -1,17 +0,0 @@ -config USB_SN9C102 - tristate "USB SN9C1xx PC Camera Controller support (DEPRECATED)" - depends on VIDEO_V4L2 && MEDIA_USB_SUPPORT - ---help--- - This driver is DEPRECATED, please use the gspca sonixb and - sonixj modules instead. - - Say Y here if you want support for cameras based on SONiX SN9C101, - SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers. - - See for more info. - - If you have webcams that are only supported by this driver and not by - the gspca driver, then contact the linux-media mailinglist. - - To compile this driver as a module, choose M here: the - module will be called sn9c102. diff --git a/drivers/staging/media/sn9c102/Makefile b/drivers/staging/media/sn9c102/Makefile deleted file mode 100644 index 7ecd5a9..0000000 --- a/drivers/staging/media/sn9c102/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -sn9c102-objs := sn9c102_core.o \ - sn9c102_hv7131d.o \ - sn9c102_hv7131r.o \ - sn9c102_mi0343.o \ - sn9c102_mi0360.o \ - sn9c102_mt9v111.o \ - sn9c102_ov7630.o \ - sn9c102_ov7660.o \ - sn9c102_pas106b.o \ - sn9c102_pas202bcb.o \ - sn9c102_tas5110c1b.o \ - sn9c102_tas5110d.o \ - sn9c102_tas5130d1b.o - -obj-$(CONFIG_USB_SN9C102) += sn9c102.o diff --git a/drivers/staging/media/sn9c102/sn9c102.h b/drivers/staging/media/sn9c102/sn9c102.h deleted file mode 100644 index 37ca722..0000000 --- a/drivers/staging/media/sn9c102/sn9c102.h +++ /dev/null @@ -1,214 +0,0 @@ -/*************************************************************************** - * V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2006 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#ifndef _SN9C102_H_ -#define _SN9C102_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sn9c102_config.h" -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -enum sn9c102_frame_state { - F_UNUSED, - F_QUEUED, - F_GRABBING, - F_DONE, - F_ERROR, -}; - -struct sn9c102_frame_t { - void *bufmem; - struct v4l2_buffer buf; - enum sn9c102_frame_state state; - struct list_head frame; - unsigned long vma_use_count; -}; - -enum sn9c102_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -enum sn9c102_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - -enum sn9c102_stream_state { - STREAM_OFF, - STREAM_INTERRUPT, - STREAM_ON, -}; - -typedef char sn9c102_sof_header_t[62]; - -struct sn9c102_sof_t { - sn9c102_sof_header_t header; - u16 bytesread; -}; - -struct sn9c102_sysfs_attr { - u16 reg, i2c_reg; - sn9c102_sof_header_t frame_header; -}; - -struct sn9c102_module_param { - u8 force_munmap; - u16 frame_timeout; -}; - -static DEFINE_MUTEX(sn9c102_sysfs_lock); -static DECLARE_RWSEM(sn9c102_dev_lock); - -struct sn9c102_device { - struct video_device *v4ldev; - - struct v4l2_device v4l2_dev; - - enum sn9c102_bridge bridge; - struct sn9c102_sensor sensor; - - struct usb_device *usbdev; - struct urb *urb[SN9C102_URBS]; - void *transfer_buffer[SN9C102_URBS]; - u8 *control_buffer; - - struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES]; - struct list_head inqueue, outqueue; - u32 frame_count, nbuffers, nreadbuffers; - - enum sn9c102_io_method io; - enum sn9c102_stream_state stream; - - struct v4l2_jpegcompression compression; - - struct sn9c102_sysfs_attr sysfs; - struct sn9c102_sof_t sof; - u16 reg[384]; - - struct sn9c102_module_param module_param; - - struct kref kref; - enum sn9c102_dev_state state; - u8 users; - - struct completion probe; - struct mutex open_mutex, fileop_mutex; - spinlock_t queue_lock; - wait_queue_head_t wait_open, wait_frame, wait_stream; -}; - -/*****************************************************************************/ - -struct sn9c102_device* -sn9c102_match_id(struct sn9c102_device *cam, const struct usb_device_id *id) -{ - return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; -} - - -void -sn9c102_attach_sensor(struct sn9c102_device *cam, - const struct sn9c102_sensor *sensor) -{ - memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor)); -} - - -enum sn9c102_bridge -sn9c102_get_bridge(struct sn9c102_device *cam) -{ - return cam->bridge; -} - - -struct sn9c102_sensor *sn9c102_get_sensor(struct sn9c102_device *cam) -{ - return &cam->sensor; -} - -/*****************************************************************************/ - -#undef DBG -#undef KDBG -#ifdef SN9C102_DEBUG -# define DBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1) \ - dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) == 2) \ - dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) >= 3) \ - dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args); \ - } \ -} while (0) -# define V4LDBG(level, name, cmd) \ -do { \ - if (debug >= (level)) \ - v4l_printk_ioctl(name, cmd); \ -} while (0) -# define KDBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1 || (level) == 2) \ - pr_info("sn9c102: " fmt "\n", ## args); \ - else if ((level) == 3) \ - pr_debug("sn9c102: [%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args); \ - } \ -} while (0) -#else -# define DBG(level, fmt, args...) do { ; } while (0) -# define V4LDBG(level, name, cmd) do { ; } while (0) -# define KDBG(level, fmt, args...) do { ; } while (0) -#endif - -#undef PDBG -#define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ - __LINE__ , ## args) - -#undef PDBGG -#define PDBGG(fmt, args...) do { ; } while (0) /* placeholder */ - -#endif /* _SN9C102_H_ */ diff --git a/drivers/staging/media/sn9c102/sn9c102.txt b/drivers/staging/media/sn9c102/sn9c102.txt deleted file mode 100644 index b4f6704..0000000 --- a/drivers/staging/media/sn9c102/sn9c102.txt +++ /dev/null @@ -1,592 +0,0 @@ - - SN9C1xx PC Camera Controllers - Driver for Linux - ============================= - - - Documentation - - - -Index -===== -1. Copyright -2. Disclaimer -3. License -4. Overview and features -5. Module dependencies -6. Module loading -7. Module parameters -8. Optional device control through "sysfs" -9. Supported devices -10. Notes for V4L2 application developers -11. Video frame formats -12. Contact information -13. Credits - - -1. Copyright -============ -Copyright (C) 2004-2007 by Luca Risolia - - -2. Disclaimer -============= -SONiX is a trademark of SONiX Technology Company Limited, inc. -This software is not sponsored or developed by SONiX. - - -3. License -========== -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. - -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. - - -4. Overview and features -======================== -This driver attempts to support the video interface of the devices assembling -the SONiX SN9C101, SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers -("SN9C1xx" from now on). - -The driver relies on the Video4Linux2 and USB core modules. It has been -designed to run properly on SMP systems as well. - -The latest version of the SN9C1xx driver can be found at the following URL: -http://www.linux-projects.org/ - -Some of the features of the driver are: - -- full compliance with the Video4Linux2 API (see also "Notes for V4L2 - application developers" paragraph); -- available mmap or read/poll methods for video streaming through isochronous - data transfers; -- automatic detection of image sensor; -- support for built-in microphone interface; -- support for any window resolutions and optional panning within the maximum - pixel area of image sensor; -- image downscaling with arbitrary scaling factors from 1, 2 and 4 in both - directions (see "Notes for V4L2 application developers" paragraph); -- two different video formats for uncompressed or compressed data in low or - high compression quality (see also "Notes for V4L2 application developers" - and "Video frame formats" paragraphs); -- full support for the capabilities of many of the possible image sensors that - can be connected to the SN9C1xx bridges, including, for instance, red, green, - blue and global gain adjustments and exposure (see "Supported devices" - paragraph for details); -- use of default color settings for sunlight conditions; -- dynamic I/O interface for both SN9C1xx and image sensor control and - monitoring (see "Optional device control through 'sysfs'" paragraph); -- dynamic driver control thanks to various module parameters (see "Module - parameters" paragraph); -- up to 64 cameras can be handled at the same time; they can be connected and - disconnected from the host many times without turning off the computer, if - the system supports hotplugging; -- no known bugs. - - -5. Module dependencies -====================== -For it to work properly, the driver needs kernel support for Video4Linux and -USB. - -The following options of the kernel configuration file must be enabled and -corresponding modules must be compiled: - - # Multimedia devices - # - CONFIG_VIDEO_DEV=m - -To enable advanced debugging functionality on the device through /sysfs: - - # Multimedia devices - # - CONFIG_VIDEO_ADV_DEBUG=y - - # USB support - # - CONFIG_USB=m - -In addition, depending on the hardware being used, the modules below are -necessary: - - # USB Host Controller Drivers - # - CONFIG_USB_EHCI_HCD=m - CONFIG_USB_UHCI_HCD=m - CONFIG_USB_OHCI_HCD=m - -The SN9C103, SN9c105 and SN9C120 controllers also provide a built-in microphone -interface. It is supported by the USB Audio driver thanks to the ALSA API: - - # Sound - # - CONFIG_SOUND=y - - # Advanced Linux Sound Architecture - # - CONFIG_SND=m - - # USB devices - # - CONFIG_SND_USB_AUDIO=m - -And finally: - - # USB Multimedia devices - # - CONFIG_USB_SN9C102=m - - -6. Module loading -================= -To use the driver, it is necessary to load the "sn9c102" module into memory -after every other module required: "videodev", "v4l2_common", "compat_ioctl32", -"usbcore" and, depending on the USB host controller you have, "ehci-hcd", -"uhci-hcd" or "ohci-hcd". - -Loading can be done as shown below: - - [root@localhost home]# modprobe sn9c102 - -Note that the module is called "sn9c102" for historic reasons, although it -does not just support the SN9C102. - -At this point all the devices supported by the driver and connected to the USB -ports should be recognized. You can invoke "dmesg" to analyze kernel messages -and verify that the loading process has gone well: - - [user@localhost home]$ dmesg - -or, to isolate all the kernel messages generated by the driver: - - [user@localhost home]$ dmesg | grep sn9c102 - - -7. Module parameters -==================== -Module parameters are listed below: -------------------------------------------------------------------------------- -Name: video_nr -Type: short array (min = 0, max = 64) -Syntax: <-1|n[,...]> -Description: Specify V4L2 minor mode number: - -1 = use next available - n = use minor number n - You can specify up to 64 cameras this way. - For example: - video_nr=-1,2,-1 would assign minor number 2 to the second - recognized camera and use auto for the first one and for every - other camera. -Default: -1 -------------------------------------------------------------------------------- -Name: force_munmap -Type: bool array (min = 0, max = 64) -Syntax: <0|1[,...]> -Description: Force the application to unmap previously mapped buffer memory - before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not - all the applications support this feature. This parameter is - specific for each detected camera. - 0 = do not force memory unmapping - 1 = force memory unmapping (save memory) -Default: 0 -------------------------------------------------------------------------------- -Name: frame_timeout -Type: uint array (min = 0, max = 64) -Syntax: <0|n[,...]> -Description: Timeout for a video frame in seconds before returning an I/O - error; 0 for infinity. This parameter is specific for each - detected camera and can be changed at runtime thanks to the - /sys filesystem interface. -Default: 2 -------------------------------------------------------------------------------- -Name: debug -Type: ushort -Syntax: -Description: Debugging information level, from 0 to 3: - 0 = none (use carefully) - 1 = critical errors - 2 = significant information - 3 = more verbose messages - Level 3 is useful for testing only. It also shows some more - information about the hardware being detected. - This parameter can be changed at runtime thanks to the /sys - filesystem interface. -Default: 2 -------------------------------------------------------------------------------- - - -8. Optional device control through "sysfs" [1] -========================================== -If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled, -it is possible to read and write both the SN9C1xx and the image sensor -registers by using the "sysfs" filesystem interface. - -Every time a supported device is recognized, a write-only file named "green" is -created in the /sys/class/video4linux/videoX directory. You can set the green -channel's gain by writing the desired value to it. The value may range from 0 -to 15 for the SN9C101 or SN9C102 bridges, from 0 to 127 for the SN9C103, -SN9C105 and SN9C120 bridges. -Similarly, only for the SN9C103, SN9C105 and SN9C120 controllers, blue and red -gain control files are available in the same directory, for which accepted -values may range from 0 to 127. - -There are other four entries in the directory above for each registered camera: -"reg", "val", "i2c_reg" and "i2c_val". The first two files control the -SN9C1xx bridge, while the other two control the sensor chip. "reg" and -"i2c_reg" hold the values of the current register index where the following -reading/writing operations are addressed at through "val" and "i2c_val". Their -use is not intended for end-users. Note that "i2c_reg" and "i2c_val" will not -be created if the sensor does not actually support the standard I2C protocol or -its registers are not 8-bit long. Also, remember that you must be logged in as -root before writing to them. - -As an example, suppose we were to want to read the value contained in the -register number 1 of the sensor register table - which is usually the product -identifier - of the camera registered as "/dev/video0": - - [root@localhost #] cd /sys/class/video4linux/video0 - [root@localhost #] echo 1 > i2c_reg - [root@localhost #] cat i2c_val - -Note that "cat" will fail if sensor registers cannot be read. - -Now let's set the green gain's register of the SN9C101 or SN9C102 chips to 2: - - [root@localhost #] echo 0x11 > reg - [root@localhost #] echo 2 > val - -Note that the SN9C1xx always returns 0 when some of its registers are read. -To avoid race conditions, all the I/O accesses to the above files are -serialized. -The sysfs interface also provides the "frame_header" entry, which exports the -frame header of the most recent requested and captured video frame. The header -is always 18-bytes long and is appended to every video frame by the SN9C1xx -controllers. As an example, this additional information can be used by the user -application for implementing auto-exposure features via software. - -The following table describes the frame header exported by the SN9C101 and -SN9C102: - -Byte # Value or bits Description ------- ------------- ----------- -0x00 0xFF Frame synchronisation pattern -0x01 0xFF Frame synchronisation pattern -0x02 0x00 Frame synchronisation pattern -0x03 0xC4 Frame synchronisation pattern -0x04 0xC4 Frame synchronisation pattern -0x05 0x96 Frame synchronisation pattern -0x06 [3:0] Read channel gain control = (1+R_GAIN/8) - [7:4] Blue channel gain control = (1+B_GAIN/8) -0x07 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled - [2:1] Maximum scale factor for compression - [ 3 ] 1 = USB fifo(2K bytes) is full - [ 4 ] 1 = Digital gain is finish - [ 5 ] 1 = Exposure is finish - [7:6] Frame index -0x08 [7:0] Y sum inside Auto-Exposure area (low-byte) -0x09 [7:0] Y sum inside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 32 -0x0A [7:0] Y sum outside Auto-Exposure area (low-byte) -0x0B [7:0] Y sum outside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 128 -0x0C 0xXX Not used -0x0D 0xXX Not used -0x0E 0xXX Not used -0x0F 0xXX Not used -0x10 0xXX Not used -0x11 0xXX Not used - -The following table describes the frame header exported by the SN9C103: - -Byte # Value or bits Description ------- ------------- ----------- -0x00 0xFF Frame synchronisation pattern -0x01 0xFF Frame synchronisation pattern -0x02 0x00 Frame synchronisation pattern -0x03 0xC4 Frame synchronisation pattern -0x04 0xC4 Frame synchronisation pattern -0x05 0x96 Frame synchronisation pattern -0x06 [6:0] Read channel gain control = (1/2+R_GAIN/64) -0x07 [6:0] Blue channel gain control = (1/2+B_GAIN/64) - [7:4] -0x08 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled - [2:1] Maximum scale factor for compression - [ 3 ] 1 = USB fifo(2K bytes) is full - [ 4 ] 1 = Digital gain is finish - [ 5 ] 1 = Exposure is finish - [7:6] Frame index -0x09 [7:0] Y sum inside Auto-Exposure area (low-byte) -0x0A [7:0] Y sum inside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 32 -0x0B [7:0] Y sum outside Auto-Exposure area (low-byte) -0x0C [7:0] Y sum outside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 128 -0x0D [1:0] Audio frame number - [ 2 ] 1 = Audio is recording -0x0E [7:0] Audio summation (low-byte) -0x0F [7:0] Audio summation (high-byte) -0x10 [7:0] Audio sample count -0x11 [7:0] Audio peak data in audio frame - -The AE area (sx, sy, ex, ey) in the active window can be set by programming the -registers 0x1c, 0x1d, 0x1e and 0x1f of the SN9C1xx controllers, where one unit -corresponds to 32 pixels. - -[1] The frame headers exported by the SN9C105 and SN9C120 are not described. - - -9. Supported devices -==================== -None of the names of the companies as well as their products will be mentioned -here. They have never collaborated with the author, so no advertising. - -From the point of view of a driver, what unambiguously identify a device are -its vendor and product USB identifiers. Below is a list of known identifiers of -devices assembling the SN9C1xx PC camera controllers: - -Vendor ID Product ID ---------- ---------- -0x0458 0x7025 -0x045e 0x00f5 -0x045e 0x00f7 -0x0471 0x0327 -0x0471 0x0328 -0x0c45 0x6001 -0x0c45 0x6005 -0x0c45 0x6007 -0x0c45 0x6009 -0x0c45 0x600d -0x0c45 0x6011 -0x0c45 0x6019 -0x0c45 0x6024 -0x0c45 0x6025 -0x0c45 0x6028 -0x0c45 0x6029 -0x0c45 0x602a -0x0c45 0x602b -0x0c45 0x602c -0x0c45 0x602d -0x0c45 0x602e -0x0c45 0x6030 -0x0c45 0x603f -0x0c45 0x6080 -0x0c45 0x6082 -0x0c45 0x6083 -0x0c45 0x6088 -0x0c45 0x608a -0x0c45 0x608b -0x0c45 0x608c -0x0c45 0x608e -0x0c45 0x608f -0x0c45 0x60a0 -0x0c45 0x60a2 -0x0c45 0x60a3 -0x0c45 0x60a8 -0x0c45 0x60aa -0x0c45 0x60ab -0x0c45 0x60ac -0x0c45 0x60ae -0x0c45 0x60af -0x0c45 0x60b0 -0x0c45 0x60b2 -0x0c45 0x60b3 -0x0c45 0x60b8 -0x0c45 0x60ba -0x0c45 0x60bb -0x0c45 0x60bc -0x0c45 0x60be -0x0c45 0x60c0 -0x0c45 0x60c2 -0x0c45 0x60c8 -0x0c45 0x60cc -0x0c45 0x60ea -0x0c45 0x60ec -0x0c45 0x60ef -0x0c45 0x60fa -0x0c45 0x60fb -0x0c45 0x60fc -0x0c45 0x60fe -0x0c45 0x6102 -0x0c45 0x6108 -0x0c45 0x610f -0x0c45 0x6130 -0x0c45 0x6138 -0x0c45 0x613a -0x0c45 0x613b -0x0c45 0x613c -0x0c45 0x613e - -The list above does not imply that all those devices work with this driver: up -until now only the ones that assemble the following pairs of SN9C1xx bridges -and image sensors are supported; kernel messages will always tell you whether -this is the case (see "Module loading" paragraph): - -Image sensor / SN9C1xx bridge | SN9C10[12] SN9C103 SN9C105 SN9C120 -------------------------------------------------------------------------------- -HV7131D Hynix Semiconductor | Yes No No No -HV7131R Hynix Semiconductor | No Yes Yes Yes -MI-0343 Micron Technology | Yes No No No -MI-0360 Micron Technology | No Yes Yes Yes -OV7630 OmniVision Technologies | Yes Yes Yes Yes -OV7660 OmniVision Technologies | No No Yes Yes -PAS106B PixArt Imaging | Yes No No No -PAS202B PixArt Imaging | Yes Yes No No -TAS5110C1B Taiwan Advanced Sensor | Yes No No No -TAS5110D Taiwan Advanced Sensor | Yes No No No -TAS5130D1B Taiwan Advanced Sensor | Yes No No No - -"Yes" means that the pair is supported by the driver, while "No" means that the -pair does not exist or is not supported by the driver. - -Only some of the available control settings of each image sensor are supported -through the V4L2 interface. - -Donations of new models for further testing and support would be much -appreciated. Non-available hardware will not be supported by the author of this -driver. - - -10. Notes for V4L2 application developers -========================================= -This driver follows the V4L2 API specifications. In particular, it enforces two -rules: - -- exactly one I/O method, either "mmap" or "read", is associated with each -file descriptor. Once it is selected, the application must close and reopen the -device to switch to the other I/O method; - -- although it is not mandatory, previously mapped buffer memory should always -be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's. -The same number of buffers as before will be allocated again to match the size -of the new video frames, so you have to map the buffers again before any I/O -attempts on them. - -Consistently with the hardware limits, this driver also supports image -downscaling with arbitrary scaling factors from 1, 2 and 4 in both directions. -However, the V4L2 API specifications don't correctly define how the scaling -factor can be chosen arbitrarily by the "negotiation" of the "source" and -"target" rectangles. To work around this flaw, we have added the convention -that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the -scaling factor is restored to 1. - -This driver supports two different video formats: the first one is the "8-bit -Sequential Bayer" format and can be used to obtain uncompressed video data -from the device through the current I/O method, while the second one provides -either "raw" compressed video data (without frame headers not related to the -compressed data) or standard JPEG (with frame headers). The compression quality -may vary from 0 to 1 and can be selected or queried thanks to the -VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP V4L2 ioctl's. For maximum flexibility, -both the default active video format and the default compression quality -depend on how the image sensor being used is initialized. - - -11. Video frame formats [1] -======================= -The SN9C1xx PC Camera Controllers can send images in two possible video -formats over the USB: either native "Sequential RGB Bayer" or compressed. -The compression is used to achieve high frame rates. With regard to the -SN9C101, SN9C102 and SN9C103, the compression is based on the Huffman encoding -algorithm described below, while with regard to the SN9C105 and SN9C120 the -compression is based on the JPEG standard. -The current video format may be selected or queried from the user application -by calling the VIDIOC_S_FMT or VIDIOC_G_FMT ioctl's, as described in the V4L2 -API specifications. - -The name "Sequential Bayer" indicates the organization of the red, green and -blue pixels in one video frame. Each pixel is associated with a 8-bit long -value and is disposed in memory according to the pattern shown below: - -B[0] G[1] B[2] G[3] ... B[m-2] G[m-1] -G[m] R[m+1] G[m+2] R[m+2] ... G[2m-2] R[2m-1] -... -... B[(n-1)(m-2)] G[(n-1)(m-1)] -... G[n(m-2)] R[n(m-1)] - -The above matrix also represents the sequential or progressive read-out mode of -the (n, m) Bayer color filter array used in many CCD or CMOS image sensors. - -The Huffman compressed video frame consists of a bitstream that encodes for -every R, G, or B pixel the difference between the value of the pixel itself and -some reference pixel value. Pixels are organised in the Bayer pattern and the -Bayer sub-pixels are tracked individually and alternatingly. For example, in -the first line values for the B and G1 pixels are alternatingly encoded, while -in the second line values for the G2 and R pixels are alternatingly encoded. - -The pixel reference value is calculated as follows: -- the 4 top left pixels are encoded in raw uncompressed 8-bit format; -- the value in the top two rows is the value of the pixel left of the current - pixel; -- the value in the left column is the value of the pixel above the current - pixel; -- for all other pixels, the reference value is the average of the value of the - pixel on the left and the value of the pixel above the current pixel; -- there is one code in the bitstream that specifies the value of a pixel - directly (in 4-bit resolution); -- pixel values need to be clamped inside the range [0..255] for proper - decoding. - -The algorithm purely describes the conversion from compressed Bayer code used -in the SN9C101, SN9C102 and SN9C103 chips to uncompressed Bayer. Additional -steps are required to convert this to a color image (i.e. a color interpolation -algorithm). - -The following Huffman codes have been found: -0: +0 (relative to reference pixel value) -100: +4 -101: -4? -1110xxxx: set absolute value to xxxx.0000 -1101: +11 -1111: -11 -11001: +20 -110000: -20 -110001: ??? - these codes are apparently not used - -[1] The Huffman compression algorithm has been reverse-engineered and - documented by Bertrik Sikken. - - -12. Contact information -======================= -The author may be contacted by e-mail at . - -GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is -'FCE635A4'; the public 1024-bit key should be available at any keyserver; -the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. - - -13. Credits -=========== -Many thanks to following persons for their contribute (listed in alphabetical -order): - -- David Anderson for the donation of a webcam; -- Luca Capello for the donation of a webcam; -- Philippe Coval for having helped testing the PAS202BCA image sensor; -- Joao Rodrigo Fuzaro, Joao Limirio, Claudio Filho and Caio Begotti for the - donation of a webcam; -- Dennis Heitmann for the donation of a webcam; -- Jon Hollstrom for the donation of a webcam; -- Nick McGill for the donation of a webcam; -- Carlos Eduardo Medaglia Dyonisio, who added the support for the PAS202BCB - image sensor; -- Stefano Mozzi, who donated 45 EU; -- Andrew Pearce for the donation of a webcam; -- John Pullan for the donation of a webcam; -- Bertrik Sikken, who reverse-engineered and documented the Huffman compression - algorithm used in the SN9C101, SN9C102 and SN9C103 controllers and - implemented the first decoder; -- Ronny Standke for the donation of a webcam; -- Mizuno Takafumi for the donation of a webcam; -- an "anonymous" donator (who didn't want his name to be revealed) for the - donation of a webcam. -- an anonymous donator for the donation of four webcams and two boards with ten - image sensors. diff --git a/drivers/staging/media/sn9c102/sn9c102_config.h b/drivers/staging/media/sn9c102/sn9c102_config.h deleted file mode 100644 index 0f4e037..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_config.h +++ /dev/null @@ -1,86 +0,0 @@ -/*************************************************************************** - * Global parameters for the V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#ifndef _SN9C102_CONFIG_H_ -#define _SN9C102_CONFIG_H_ - -#include -#include - -#define SN9C102_DEBUG -#define SN9C102_DEBUG_LEVEL 2 -#define SN9C102_MAX_DEVICES 64 -#define SN9C102_PRESERVE_IMGSCALE 0 -#define SN9C102_FORCE_MUNMAP 0 -#define SN9C102_MAX_FRAMES 32 -#define SN9C102_URBS 2 -#define SN9C102_ISO_PACKETS 7 -#define SN9C102_ALTERNATE_SETTING 8 -#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) -#define SN9C102_CTRL_TIMEOUT 300 -#define SN9C102_FRAME_TIMEOUT 0 - -/*****************************************************************************/ - -static const u8 SN9C102_Y_QTABLE0[64] = { - 8, 5, 5, 8, 12, 20, 25, 30, - 6, 6, 7, 9, 13, 29, 30, 27, - 7, 6, 8, 12, 20, 28, 34, 28, - 7, 8, 11, 14, 25, 43, 40, 31, - 9, 11, 18, 28, 34, 54, 51, 38, - 12, 17, 27, 32, 40, 52, 56, 46, - 24, 32, 39, 43, 51, 60, 60, 50, - 36, 46, 47, 49, 56, 50, 51, 49 -}; - -static const u8 SN9C102_UV_QTABLE0[64] = { - 8, 9, 12, 23, 49, 49, 49, 49, - 9, 10, 13, 33, 49, 49, 49, 49, - 12, 13, 28, 49, 49, 49, 49, 49, - 23, 33, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49 -}; - -static const u8 SN9C102_Y_QTABLE1[64] = { - 16, 11, 10, 16, 24, 40, 51, 61, - 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, - 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 56, 68, 109, 103, 77, - 24, 35, 55, 64, 81, 104, 113, 92, - 49, 64, 78, 87, 103, 121, 120, 101, - 72, 92, 95, 98, 112, 100, 103, 99 -}; - -static const u8 SN9C102_UV_QTABLE1[64] = { - 17, 18, 24, 47, 99, 99, 99, 99, - 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 -}; - -#endif /* _SN9C102_CONFIG_H_ */ diff --git a/drivers/staging/media/sn9c102/sn9c102_core.c b/drivers/staging/media/sn9c102/sn9c102_core.c deleted file mode 100644 index 98b3057..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_core.c +++ /dev/null @@ -1,3465 +0,0 @@ -/*************************************************************************** - * V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sn9c102.h" - -/*****************************************************************************/ - -#define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers" -#define SN9C102_MODULE_ALIAS "sn9c1xx" -#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia" -#define SN9C102_AUTHOR_EMAIL "" -#define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.48" - -/*****************************************************************************/ - -MODULE_DEVICE_TABLE(usb, sn9c102_id_table); - -MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); -MODULE_DESCRIPTION(SN9C102_MODULE_NAME); -MODULE_ALIAS(SN9C102_MODULE_ALIAS); -MODULE_VERSION(SN9C102_MODULE_VERSION); -MODULE_LICENSE(SN9C102_MODULE_LICENSE); - -static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1}; -module_param_array(video_nr, short, NULL, 0444); -MODULE_PARM_DESC(video_nr, - " <-1|n[,...]>" - "\nSpecify V4L2 minor mode number." - "\n-1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" - "\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES) - " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second camera and use auto for the first" - "\none and for every other camera." - "\n"); - -static bool force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = - SN9C102_FORCE_MUNMAP}; -module_param_array(force_munmap, bool, NULL, 0444); -MODULE_PARM_DESC(force_munmap, - " <0|1[,...]>" - "\nForce the application to unmap previously" - "\nmapped buffer memory before calling any VIDIOC_S_CROP or" - "\nVIDIOC_S_FMT ioctl's. Not all the applications support" - "\nthis feature. This parameter is specific for each" - "\ndetected camera." - "\n0 = do not force memory unmapping" - "\n1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." - "\n"); - -static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = - SN9C102_FRAME_TIMEOUT}; -module_param_array(frame_timeout, uint, NULL, 0644); -MODULE_PARM_DESC(frame_timeout, - " <0|n[,...]>" - "\nTimeout for a video frame in seconds before" - "\nreturning an I/O error; 0 for infinity." - "\nThis parameter is specific for each detected camera." - "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." - "\n"); - -#ifdef SN9C102_DEBUG -static unsigned short debug = SN9C102_DEBUG_LEVEL; -module_param(debug, ushort, 0644); -MODULE_PARM_DESC(debug, - " " - "\nDebugging information level, from 0 to 3:" - "\n0 = none (use carefully)" - "\n1 = critical errors" - "\n2 = significant informations" - "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only." - "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"." - "\n"); -#endif - -/* - Add the probe entries to this table. Be sure to add the entry in the right - place, since, on failure, the next probing routine is called according to - the order of the list below, from top to bottom. -*/ -static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = { - &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ - &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ - &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ -}; - -/*****************************************************************************/ - -static u32 -sn9c102_request_buffers(struct sn9c102_device *cam, u32 count, - enum sn9c102_io_method io) -{ - struct v4l2_pix_format *p = &(cam->sensor.pix_format); - struct v4l2_rect *r = &(cam->sensor.cropcap.bounds); - size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; - void *buff = NULL; - u32 i; - - if (count > SN9C102_MAX_FRAMES) - count = SN9C102_MAX_FRAMES; - - if (cam->bridge == BRIDGE_SN9C105 || cam->bridge == BRIDGE_SN9C120) - imagesize += 589 + 2; /* length of JPEG header + EOI marker */ - - cam->nbuffers = count; - while (cam->nbuffers > 0) { - buff = vmalloc_32_user(cam->nbuffers * PAGE_ALIGN(imagesize)); - if (buff) - break; - cam->nbuffers--; - } - - for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.index = i; - cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.length = imagesize; - cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cam->frame[i].buf.sequence = 0; - cam->frame[i].buf.field = V4L2_FIELD_NONE; - cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - } - - return cam->nbuffers; -} - - -static void sn9c102_release_buffers(struct sn9c102_device *cam) -{ - if (cam->nbuffers) { - vfree(cam->frame[0].bufmem); - cam->nbuffers = 0; - } - cam->frame_current = NULL; -} - - -static void sn9c102_empty_framequeues(struct sn9c102_device *cam) -{ - u32 i; - - INIT_LIST_HEAD(&cam->inqueue); - INIT_LIST_HEAD(&cam->outqueue); - - for (i = 0; i < SN9C102_MAX_FRAMES; i++) { - cam->frame[i].state = F_UNUSED; - cam->frame[i].buf.bytesused = 0; - } -} - - -static void sn9c102_requeue_outqueue(struct sn9c102_device *cam) -{ - struct sn9c102_frame_t *i; - - list_for_each_entry(i, &cam->outqueue, frame) { - i->state = F_QUEUED; - list_add(&i->frame, &cam->inqueue); - } - - INIT_LIST_HEAD(&cam->outqueue); -} - - -static void sn9c102_queue_unusedframes(struct sn9c102_device *cam) -{ - unsigned long lock_flags; - u32 i; - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].state == F_UNUSED) { - cam->frame[i].state = F_QUEUED; - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[i].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - } -} - -/*****************************************************************************/ - -/* - Write a sequence of count value/register pairs. Returns -1 after the first - failed write, or 0 for no errors. -*/ -int sn9c102_write_regs(struct sn9c102_device *cam, const u8 valreg[][2], - int count) -{ - struct usb_device *udev = cam->usbdev; - u8 *buff = cam->control_buffer; - int i, res; - - for (i = 0; i < count; i++) { - u8 index = valreg[i][1]; - - /* - index is a u8, so it must be <256 and can't be out of range. - If we put in a check anyway, gcc annoys us with a warning - hat our check is useless. People get all uppity when they - see warnings in the kernel compile. - */ - - *buff = valreg[i][0]; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, - 0x41, index, 0, buff, 1, - SN9C102_CTRL_TIMEOUT); - - if (res < 0) { - DBG(3, "Failed to write a register (value 0x%02X, " - "index 0x%02X, error %d)", *buff, index, res); - return -1; - } - - cam->reg[index] = *buff; - } - - return 0; -} - - -int sn9c102_write_reg(struct sn9c102_device *cam, u8 value, u16 index) -{ - struct usb_device *udev = cam->usbdev; - u8 *buff = cam->control_buffer; - int res; - - if (index >= ARRAY_SIZE(cam->reg)) - return -1; - - *buff = value; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); - if (res < 0) { - DBG(3, "Failed to write a register (value 0x%02X, index " - "0x%02X, error %d)", value, index, res); - return -1; - } - - cam->reg[index] = value; - - return 0; -} - - -/* NOTE: with the SN9C10[123] reading some registers always returns 0 */ -int sn9c102_read_reg(struct sn9c102_device *cam, u16 index) -{ - struct usb_device *udev = cam->usbdev; - u8 *buff = cam->control_buffer; - int res; - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); - if (res < 0) - DBG(3, "Failed to read a register (index 0x%02X, error %d)", - index, res); - - return (res >= 0) ? (int)(*buff) : -1; -} - - -int sn9c102_pread_reg(struct sn9c102_device *cam, u16 index) -{ - if (index >= ARRAY_SIZE(cam->reg)) - return -1; - - return cam->reg[index]; -} - - -static int -sn9c102_i2c_wait(struct sn9c102_device *cam, - const struct sn9c102_sensor *sensor) -{ - int i, r; - - for (i = 1; i <= 5; i++) { - r = sn9c102_read_reg(cam, 0x08); - if (r < 0) - return -EIO; - if (r & 0x04) - return 0; - if (sensor->frequency & SN9C102_I2C_400KHZ) - udelay(5*16); - else - udelay(16*16); - } - return -EBUSY; -} - - -static int -sn9c102_i2c_detect_read_error(struct sn9c102_device *cam, - const struct sn9c102_sensor *sensor) -{ - int r , err = 0; - - r = sn9c102_read_reg(cam, 0x08); - if (r < 0) - err += r; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - if (!(r & 0x08)) - err += -1; - } else { - if (r & 0x08) - err += -1; - } - - return err ? -EIO : 0; -} - - -static int -sn9c102_i2c_detect_write_error(struct sn9c102_device *cam, - const struct sn9c102_sensor *sensor) -{ - int r; - - r = sn9c102_read_reg(cam, 0x08); - return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0; -} - - -int -sn9c102_i2c_try_raw_read(struct sn9c102_device *cam, - const struct sn9c102_sensor *sensor, u8 data0, - u8 data1, u8 n, u8 buffer[]) -{ - struct usb_device *udev = cam->usbdev; - u8 *data = cam->control_buffer; - int i = 0, err = 0, res; - - /* Write cycle */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10; - data[1] = data0; /* I2C slave id */ - data[2] = data1; /* address */ - data[7] = 0x10; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - - /* Read cycle - n bytes */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | - (n << 4) | 0x02; - data[1] = data0; - data[7] = 0x10; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - - /* The first read byte will be placed in data[4] */ - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_detect_read_error(cam, sensor); - - PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1, - data[4]); - - if (err) { - DBG(3, "I2C read failed for %s image sensor", sensor->name); - return -1; - } - - if (buffer) - for (i = 0; i < n && i < 5; i++) - buffer[n-i-1] = data[4-i]; - - return (int)data[4]; -} - - -int -sn9c102_i2c_try_raw_write(struct sn9c102_device *cam, - const struct sn9c102_sensor *sensor, u8 n, u8 data0, - u8 data1, u8 data2, u8 data3, u8 data4, u8 data5) -{ - struct usb_device *udev = cam->usbdev; - u8 *data = cam->control_buffer; - int err = 0, res; - - /* Write cycle. It usually is address + value */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) - | ((n - 1) << 4); - data[1] = data0; - data[2] = data1; - data[3] = data2; - data[4] = data3; - data[5] = data4; - data[6] = data5; - data[7] = 0x17; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - err += sn9c102_i2c_detect_write_error(cam, sensor); - - if (err) - DBG(3, "I2C write failed for %s image sensor", sensor->name); - - PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " - "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X", - n, data0, data1, data2, data3, data4, data5); - - return err ? -1 : 0; -} - - -int -sn9c102_i2c_try_read(struct sn9c102_device *cam, - const struct sn9c102_sensor *sensor, u8 address) -{ - return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id, - address, 1, NULL); -} - - -static int sn9c102_i2c_try_write(struct sn9c102_device *cam, - const struct sn9c102_sensor *sensor, - u8 address, u8 value) -{ - return sn9c102_i2c_try_raw_write(cam, sensor, 3, - sensor->i2c_slave_id, address, - value, 0, 0, 0); -} - - -int sn9c102_i2c_read(struct sn9c102_device *cam, u8 address) -{ - return sn9c102_i2c_try_read(cam, &cam->sensor, address); -} - - -int sn9c102_i2c_write(struct sn9c102_device *cam, u8 address, u8 value) -{ - return sn9c102_i2c_try_write(cam, &cam->sensor, address, value); -} - -/*****************************************************************************/ - -static size_t sn9c102_sof_length(struct sn9c102_device *cam) -{ - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - return 12; - case BRIDGE_SN9C103: - return 18; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - return 62; - } - - return 0; -} - - -static void* -sn9c102_find_sof_header(struct sn9c102_device *cam, void *mem, size_t len) -{ - static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; - const char *m = mem; - size_t soflen = 0, i, j; - - soflen = sn9c102_sof_length(cam); - - for (i = 0; i < len; i++) { - size_t b; - - /* Read the variable part of the header */ - if (unlikely(cam->sof.bytesread >= sizeof(marker))) { - cam->sof.header[cam->sof.bytesread] = *(m+i); - if (++cam->sof.bytesread == soflen) { - cam->sof.bytesread = 0; - return mem + i; - } - continue; - } - - /* Search for the SOF marker (fixed part) in the header */ - for (j = 0, b = cam->sof.bytesread; j+b < sizeof(marker); j++) { - if (unlikely(i+j == len)) - return NULL; - if (*(m+i+j) == marker[cam->sof.bytesread]) { - cam->sof.header[cam->sof.bytesread] = *(m+i+j); - if (++cam->sof.bytesread == sizeof(marker)) { - PDBGG("Bytes to analyze: %zd. SOF " - "starts at byte #%zd", len, i); - i += j+1; - break; - } - } else { - cam->sof.bytesread = 0; - break; - } - } - } - - return NULL; -} - - -static void* -sn9c102_find_eof_header(struct sn9c102_device *cam, void *mem, size_t len) -{ - static const u8 eof_header[4][4] = { - {0x00, 0x00, 0x00, 0x00}, - {0x40, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00}, - {0xc0, 0x00, 0x00, 0x00}, - }; - size_t i, j; - - /* The EOF header does not exist in compressed data */ - if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - return NULL; - - /* - The EOF header might cross the packet boundary, but this is not a - problem, since the end of a frame is determined by checking its size - in the first place. - */ - for (i = 0; (len >= 4) && (i <= len - 4); i++) - for (j = 0; j < ARRAY_SIZE(eof_header); j++) - if (!memcmp(mem + i, eof_header[j], 4)) - return mem + i; - - return NULL; -} - - -static void -sn9c102_write_jpegheader(struct sn9c102_device *cam, struct sn9c102_frame_t *f) -{ - static const u8 jpeg_header[589] = { - 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05, - 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06, - 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, - 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16, - 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16, - 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29, - 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29, - 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a, - 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2, - 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, - 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, - 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, - 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, - 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, - 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, - 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, - 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, - 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, - 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, - 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, - 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, - 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, - 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, - 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, - 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, - 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, - 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, - 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, - 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11, - 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02, - 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, - 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 - }; - u8 *pos = f->bufmem; - - memcpy(pos, jpeg_header, sizeof(jpeg_header)); - *(pos + 6) = 0x00; - *(pos + 7 + 64) = 0x01; - if (cam->compression.quality == 0) { - memcpy(pos + 7, SN9C102_Y_QTABLE0, 64); - memcpy(pos + 8 + 64, SN9C102_UV_QTABLE0, 64); - } else if (cam->compression.quality == 1) { - memcpy(pos + 7, SN9C102_Y_QTABLE1, 64); - memcpy(pos + 8 + 64, SN9C102_UV_QTABLE1, 64); - } - *(pos + 564) = cam->sensor.pix_format.width & 0xFF; - *(pos + 563) = (cam->sensor.pix_format.width >> 8) & 0xFF; - *(pos + 562) = cam->sensor.pix_format.height & 0xFF; - *(pos + 561) = (cam->sensor.pix_format.height >> 8) & 0xFF; - *(pos + 567) = 0x21; - - f->buf.bytesused += sizeof(jpeg_header); -} - - -static void sn9c102_urb_complete(struct urb *urb) -{ - struct sn9c102_device *cam = urb->context; - struct sn9c102_frame_t **f; - size_t imagesize, soflen; - u8 i; - int err = 0; - - if (urb->status == -ENOENT) - return; - - f = &cam->frame_current; - - if (cam->stream == STREAM_INTERRUPT) { - cam->stream = STREAM_OFF; - if ((*f)) - (*f)->state = F_QUEUED; - cam->sof.bytesread = 0; - DBG(3, "Stream interrupted by application"); - wake_up(&cam->wait_stream); - } - - if (cam->state & DEV_DISCONNECTED) - return; - - if (cam->state & DEV_MISCONFIGURED) { - wake_up_interruptible(&cam->wait_frame); - return; - } - - if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) - goto resubmit_urb; - - if (!(*f)) - (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t, - frame); - - imagesize = (cam->sensor.pix_format.width * - cam->sensor.pix_format.height * - cam->sensor.pix_format.priv) / 8; - if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - imagesize += 589; /* length of jpeg header */ - soflen = sn9c102_sof_length(cam); - - for (i = 0; i < urb->number_of_packets; i++) { - unsigned int img, len, status; - void *pos, *sof, *eof; - - len = urb->iso_frame_desc[i].actual_length; - status = urb->iso_frame_desc[i].status; - pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; - - if (status) { - DBG(3, "Error in isochronous frame"); - (*f)->state = F_ERROR; - cam->sof.bytesread = 0; - continue; - } - - PDBGG("Isochrnous frame: length %u, #%u i", len, i); - -redo: - sof = sn9c102_find_sof_header(cam, pos, len); - if (likely(!sof)) { - eof = sn9c102_find_eof_header(cam, pos, len); - if ((*f)->state == F_GRABBING) { -end_of_frame: - img = len; - - if (eof) - img = (eof > pos) ? eof - pos - 1 : 0; - - if ((*f)->buf.bytesused + img > imagesize) { - u32 b; - b = (*f)->buf.bytesused + img - - imagesize; - img = imagesize - (*f)->buf.bytesused; - PDBGG("Expected EOF not found: video " - "frame cut"); - if (eof) - DBG(3, "Exceeded limit: +%u " - "bytes", (unsigned)(b)); - } - - memcpy((*f)->bufmem + (*f)->buf.bytesused, pos, - img); - - if ((*f)->buf.bytesused == 0) - v4l2_get_timestamp( - &(*f)->buf.timestamp); - - (*f)->buf.bytesused += img; - - if ((*f)->buf.bytesused == imagesize || - ((cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) && eof)) { - u32 b; - - b = (*f)->buf.bytesused; - (*f)->state = F_DONE; - (*f)->buf.sequence = ++cam->frame_count; - - spin_lock(&cam->queue_lock); - list_move_tail(&(*f)->frame, - &cam->outqueue); - if (!list_empty(&cam->inqueue)) - (*f) = list_entry( - cam->inqueue.next, - struct sn9c102_frame_t, - frame); - else - (*f) = NULL; - spin_unlock(&cam->queue_lock); - - memcpy(cam->sysfs.frame_header, - cam->sof.header, soflen); - - DBG(3, "Video frame captured: %lu " - "bytes", (unsigned long)(b)); - - if (!(*f)) - goto resubmit_urb; - - } else if (eof) { - (*f)->state = F_ERROR; - DBG(3, "Not expected EOF after %lu " - "bytes of image data", - (unsigned long) - ((*f)->buf.bytesused)); - } - - if (sof) /* (1) */ - goto start_of_frame; - - } else if (eof) { - DBG(3, "EOF without SOF"); - continue; - - } else { - PDBGG("Ignoring pointless isochronous frame"); - continue; - } - - } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) { -start_of_frame: - (*f)->state = F_GRABBING; - (*f)->buf.bytesused = 0; - len -= (sof - pos); - pos = sof; - if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) - sn9c102_write_jpegheader(cam, (*f)); - DBG(3, "SOF detected: new video frame"); - if (len) - goto redo; - - } else if ((*f)->state == F_GRABBING) { - eof = sn9c102_find_eof_header(cam, pos, len); - if (eof && eof < sof) - goto end_of_frame; /* (1) */ - else { - if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) { - if (sof - pos >= soflen) { - eof = sof - soflen; - } else { /* remove header */ - eof = pos; - (*f)->buf.bytesused -= - (soflen - (sof - pos)); - } - goto end_of_frame; - } else { - DBG(3, "SOF before expected EOF after " - "%lu bytes of image data", - (unsigned long) - ((*f)->buf.bytesused)); - goto start_of_frame; - } - } - } - } - -resubmit_urb: - urb->dev = cam->usbdev; - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -EPERM) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "usb_submit_urb() failed"); - } - - wake_up_interruptible(&cam->wait_frame); -} - - -static int sn9c102_start_transfer(struct sn9c102_device *cam) -{ - struct usb_device *udev = cam->usbdev; - struct urb *urb; - struct usb_host_interface *altsetting = usb_altnum_to_altsetting( - usb_ifnum_to_if(udev, 0), - SN9C102_ALTERNATE_SETTING); - const unsigned int psz = le16_to_cpu(altsetting-> - endpoint[0].desc.wMaxPacketSize); - s8 i, j; - int err = 0; - - for (i = 0; i < SN9C102_URBS; i++) { - cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz, - GFP_KERNEL); - if (!cam->transfer_buffer[i]) { - err = -ENOMEM; - DBG(1, "Not enough memory"); - goto free_buffers; - } - } - - for (i = 0; i < SN9C102_URBS; i++) { - urb = usb_alloc_urb(SN9C102_ISO_PACKETS, GFP_KERNEL); - cam->urb[i] = urb; - if (!urb) { - err = -ENOMEM; - DBG(1, "usb_alloc_urb() failed"); - goto free_urbs; - } - urb->dev = udev; - urb->context = cam; - urb->pipe = usb_rcvisocpipe(udev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = SN9C102_ISO_PACKETS; - urb->complete = sn9c102_urb_complete; - urb->transfer_buffer = cam->transfer_buffer[i]; - urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS; - urb->interval = 1; - for (j = 0; j < SN9C102_ISO_PACKETS; j++) { - urb->iso_frame_desc[j].offset = psz * j; - urb->iso_frame_desc[j].length = psz; - } - } - - /* Enable video */ - if (!(cam->reg[0x01] & 0x04)) { - err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01); - if (err) { - err = -EIO; - DBG(1, "I/O hardware error"); - goto free_urbs; - } - } - - err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING); - if (err) { - DBG(1, "usb_set_interface() failed"); - goto free_urbs; - } - - cam->frame_current = NULL; - cam->sof.bytesread = 0; - - for (i = 0; i < SN9C102_URBS; i++) { - err = usb_submit_urb(cam->urb[i], GFP_KERNEL); - if (err) { - for (j = i-1; j >= 0; j--) - usb_kill_urb(cam->urb[j]); - DBG(1, "usb_submit_urb() failed, error %d", err); - goto free_urbs; - } - } - - return 0; - -free_urbs: - for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++) - usb_free_urb(cam->urb[i]); - -free_buffers: - for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++) - kfree(cam->transfer_buffer[i]); - - return err; -} - - -static int sn9c102_stop_transfer(struct sn9c102_device *cam) -{ - struct usb_device *udev = cam->usbdev; - s8 i; - int err = 0; - - if (cam->state & DEV_DISCONNECTED) - return 0; - - for (i = SN9C102_URBS-1; i >= 0; i--) { - usb_kill_urb(cam->urb[i]); - usb_free_urb(cam->urb[i]); - kfree(cam->transfer_buffer[i]); - } - - err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ - if (err) - DBG(3, "usb_set_interface() failed"); - - return err; -} - - -static int sn9c102_stream_interrupt(struct sn9c102_device *cam) -{ - cam->stream = STREAM_INTERRUPT; - wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - SN9C102_URB_TIMEOUT); - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - else if (cam->stream != STREAM_OFF) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "URB timeout reached. The camera is misconfigured. " - "To use it, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - return 0; -} - -/*****************************************************************************/ - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static u16 sn9c102_strtou16(const char *buff, size_t len, ssize_t *count) -{ - char str[7]; - char *endp; - unsigned long val; - - if (len < 6) { - strncpy(str, buff, len); - str[len] = '\0'; - } else { - strncpy(str, buff, 6); - str[6] = '\0'; - } - - val = simple_strtoul(str, &endp, 0); - - *count = 0; - if (val <= 0xffff) - *count = (ssize_t)(endp - str); - if ((*count) && (len == *count+1) && (buff[*count] == '\n')) - *count += 1; - - return (u16)val; -} - -/* - NOTE 1: being inside one of the following methods implies that the v4l - device exists for sure (see kobjects and reference counters) - NOTE 2: buffers are PAGE_SIZE long -*/ - -static ssize_t sn9c102_show_reg(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct sn9c102_device *cam; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.reg); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_reg(struct device *cd, struct device_attribute *attr, - const char *buf, size_t len) -{ - struct sn9c102_device *cam; - u16 index; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - index = sn9c102_strtou16(buf, len, &count); - if (index >= ARRAY_SIZE(cam->reg) || !count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.reg = index; - - DBG(2, "Moved SN9C1XX register index to 0x%02X", cam->sysfs.reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_val(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct sn9c102_device *cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - val = sn9c102_read_reg(cam, cam->sysfs.reg); - if (val < 0) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd, value: %d", count, val); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_val(struct device *cd, struct device_attribute *attr, - const char *buf, size_t len) -{ - struct sn9c102_device *cam; - u16 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - value = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - err = sn9c102_write_reg(cam, value, cam->sysfs.reg); - if (err) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - DBG(2, "Written SN9C1XX reg. 0x%02X, val. 0x%02X", - cam->sysfs.reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_i2c_reg(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct sn9c102_device *cam; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_i2c_reg(struct device *cd, struct device_attribute *attr, - const char *buf, size_t len) -{ - struct sn9c102_device *cam; - u16 index; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - index = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.i2c_reg = index; - - DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_i2c_val(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct sn9c102_device *cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENOSYS; - } - - val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg); - if (val < 0) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd, value: %d", count, val); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_i2c_val(struct device *cd, struct device_attribute *attr, - const char *buf, size_t len) -{ - struct sn9c102_device *cam; - u16 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENOSYS; - } - - value = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value); - if (err) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", - cam->sysfs.i2c_reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_green(struct device *cd, struct device_attribute *attr, - const char *buf, size_t len) -{ - struct sn9c102_device *cam; - enum sn9c102_bridge bridge; - ssize_t res = 0; - u16 value; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - bridge = cam->bridge; - - mutex_unlock(&sn9c102_sysfs_lock); - - value = sn9c102_strtou16(buf, len, &count); - if (!count) - return -EINVAL; - - switch (bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - if (value > 0x0f) - return -EINVAL; - res = sn9c102_store_reg(cd, attr, "0x11", 4); - if (res >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - break; - case BRIDGE_SN9C103: - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (value > 0x7f) - return -EINVAL; - res = sn9c102_store_reg(cd, attr, "0x07", 4); - if (res >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - break; - } - - return res; -} - - -static ssize_t -sn9c102_store_blue(struct device *cd, struct device_attribute *attr, - const char *buf, size_t len) -{ - ssize_t res = 0; - u16 value; - ssize_t count; - - value = sn9c102_strtou16(buf, len, &count); - if (!count || value > 0x7f) - return -EINVAL; - - res = sn9c102_store_reg(cd, attr, "0x06", 4); - if (res >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - - return res; -} - - -static ssize_t -sn9c102_store_red(struct device *cd, struct device_attribute *attr, - const char *buf, size_t len) -{ - ssize_t res = 0; - u16 value; - ssize_t count; - - value = sn9c102_strtou16(buf, len, &count); - if (!count || value > 0x7f) - return -EINVAL; - res = sn9c102_store_reg(cd, attr, "0x05", 4); - if (res >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - - return res; -} - - -static ssize_t sn9c102_show_frame_header(struct device *cd, - struct device_attribute *attr, - char *buf) -{ - struct sn9c102_device *cam; - ssize_t count; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) - return -ENODEV; - - count = sizeof(cam->sysfs.frame_header); - memcpy(buf, cam->sysfs.frame_header, count); - - DBG(3, "Frame header, read bytes: %zd", count); - - return count; -} - - -static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, sn9c102_show_reg, sn9c102_store_reg); -static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, sn9c102_show_val, sn9c102_store_val); -static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, - sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); -static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, - sn9c102_show_i2c_val, sn9c102_store_i2c_val); -static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green); -static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue); -static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red); -static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL); - - -static int sn9c102_create_sysfs(struct sn9c102_device *cam) -{ - struct device *dev = &(cam->v4ldev->dev); - int err = 0; - - err = device_create_file(dev, &dev_attr_reg); - if (err) - goto err_out; - err = device_create_file(dev, &dev_attr_val); - if (err) - goto err_reg; - err = device_create_file(dev, &dev_attr_frame_header); - if (err) - goto err_val; - - if (cam->sensor.sysfs_ops) { - err = device_create_file(dev, &dev_attr_i2c_reg); - if (err) - goto err_frame_header; - err = device_create_file(dev, &dev_attr_i2c_val); - if (err) - goto err_i2c_reg; - } - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - err = device_create_file(dev, &dev_attr_green); - if (err) - goto err_i2c_val; - } else { - err = device_create_file(dev, &dev_attr_blue); - if (err) - goto err_i2c_val; - err = device_create_file(dev, &dev_attr_red); - if (err) - goto err_blue; - } - - return 0; - -err_blue: - device_remove_file(dev, &dev_attr_blue); -err_i2c_val: - if (cam->sensor.sysfs_ops) - device_remove_file(dev, &dev_attr_i2c_val); -err_i2c_reg: - if (cam->sensor.sysfs_ops) - device_remove_file(dev, &dev_attr_i2c_reg); -err_frame_header: - device_remove_file(dev, &dev_attr_frame_header); -err_val: - device_remove_file(dev, &dev_attr_val); -err_reg: - device_remove_file(dev, &dev_attr_reg); -err_out: - return err; -} -#endif /* CONFIG_VIDEO_ADV_DEBUG */ - -/*****************************************************************************/ - -static int -sn9c102_set_pix_format(struct sn9c102_device *cam, struct v4l2_pix_format *pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || - pix->pixelformat == V4L2_PIX_FMT_JPEG) { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, - 0x18); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, - 0x18); - break; - } - } else { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, - 0x18); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, - 0x18); - break; - } - } - - return err ? -EIO : 0; -} - - -static int -sn9c102_set_compression(struct sn9c102_device *cam, - struct v4l2_jpegcompression *compression) -{ - int i, err = 0; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (compression->quality == 0) - err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, - 0x17); - else if (compression->quality == 1) - err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, - 0x17); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (compression->quality == 0) { - for (i = 0; i <= 63; i++) { - err += sn9c102_write_reg(cam, - SN9C102_Y_QTABLE1[i], - 0x100 + i); - err += sn9c102_write_reg(cam, - SN9C102_UV_QTABLE1[i], - 0x140 + i); - } - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf, - 0x18); - } else if (compression->quality == 1) { - for (i = 0; i <= 63; i++) { - err += sn9c102_write_reg(cam, - SN9C102_Y_QTABLE1[i], - 0x100 + i); - err += sn9c102_write_reg(cam, - SN9C102_UV_QTABLE1[i], - 0x140 + i); - } - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x40, - 0x18); - } - break; - } - - return err ? -EIO : 0; -} - - -static int sn9c102_set_scale(struct sn9c102_device *cam, u8 scale) -{ - u8 r = 0; - int err = 0; - - if (scale == 1) - r = cam->reg[0x18] & 0xcf; - else if (scale == 2) { - r = cam->reg[0x18] & 0xcf; - r |= 0x10; - } else if (scale == 4) - r = cam->reg[0x18] | 0x20; - - err += sn9c102_write_reg(cam, r, 0x18); - if (err) - return -EIO; - - PDBGG("Scaling factor: %u", scale); - - return 0; -} - - -static int sn9c102_set_crop(struct sn9c102_device *cam, struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = &cam->sensor; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), - v_start = (u8)(rect->top - s->cropcap.bounds.top), - h_size = (u8)(rect->width / 16), - v_size = (u8)(rect->height / 16); - int err = 0; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - err += sn9c102_write_reg(cam, h_size, 0x15); - err += sn9c102_write_reg(cam, v_size, 0x16); - if (err) - return -EIO; - - PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " - "%u %u %u %u", h_start, v_start, h_size, v_size); - - return 0; -} - - -static int sn9c102_init(struct sn9c102_device *cam) -{ - struct sn9c102_sensor *s = &cam->sensor; - struct v4l2_control ctrl; - struct v4l2_queryctrl *qctrl; - struct v4l2_rect *rect; - u8 i = 0; - int err = 0; - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->open_mutex); - init_waitqueue_head(&cam->wait_open); - qctrl = s->qctrl; - rect = &(s->cropcap.defrect); - } else { /* use current values */ - qctrl = s->_qctrl; - rect = &(s->_rect); - } - - err += sn9c102_set_scale(cam, rect->width / s->pix_format.width); - err += sn9c102_set_crop(cam, rect); - if (err) - return err; - - if (s->init) { - err = s->init(cam); - if (err) { - DBG(3, "Sensor initialization failed"); - return err; - } - } - - if (!(cam->state & DEV_INITIALIZED)) - if (cam->bridge == BRIDGE_SN9C101 || - cam->bridge == BRIDGE_SN9C102 || - cam->bridge == BRIDGE_SN9C103) { - if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - s->pix_format.pixelformat = V4L2_PIX_FMT_SBGGR8; - cam->compression.quality = cam->reg[0x17] & 0x01 ? - 0 : 1; - } else { - if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) - s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG; - cam->compression.quality = cam->reg[0x18] & 0x40 ? - 0 : 1; - err += sn9c102_set_compression(cam, &cam->compression); - } - else - err += sn9c102_set_compression(cam, &cam->compression); - err += sn9c102_set_pix_format(cam, &s->pix_format); - if (s->set_pix_format) - err += s->set_pix_format(cam, &s->pix_format); - if (err) - return err; - - if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || - s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - DBG(3, "Compressed video format is active, quality %d", - cam->compression.quality); - else - DBG(3, "Uncompressed video format is active"); - - if (s->set_crop) { - err = s->set_crop(cam, rect); - if (err) { - DBG(3, "set_crop() failed"); - return err; - } - } - - if (s->set_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (s->qctrl[i].id != 0 && - !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { - ctrl.id = s->qctrl[i].id; - ctrl.value = qctrl[i].default_value; - err = s->set_ctrl(cam, &ctrl); - if (err) { - DBG(3, "Set %s control failed", - s->qctrl[i].name); - return err; - } - DBG(3, "Image sensor supports '%s' control", - s->qctrl[i].name); - } - } - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->fileop_mutex); - spin_lock_init(&cam->queue_lock); - init_waitqueue_head(&cam->wait_frame); - init_waitqueue_head(&cam->wait_stream); - cam->nreadbuffers = 2; - memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); - memcpy(&(s->_rect), &(s->cropcap.defrect), - sizeof(struct v4l2_rect)); - cam->state |= DEV_INITIALIZED; - } - - DBG(2, "Initialization succeeded"); - return 0; -} - -/*****************************************************************************/ - -static void sn9c102_release_resources(struct kref *kref) -{ - struct sn9c102_device *cam; - - mutex_lock(&sn9c102_sysfs_lock); - - cam = container_of(kref, struct sn9c102_device, kref); - - DBG(2, "V4L2 device %s deregistered", - video_device_node_name(cam->v4ldev)); - video_set_drvdata(cam->v4ldev, NULL); - video_unregister_device(cam->v4ldev); - v4l2_device_unregister(&cam->v4l2_dev); - usb_put_dev(cam->usbdev); - kfree(cam->control_buffer); - kfree(cam); - - mutex_unlock(&sn9c102_sysfs_lock); - -} - - -static int sn9c102_open(struct file *filp) -{ - struct sn9c102_device *cam; - int err = 0; - - /* - A read_trylock() in open() is the only safe way to prevent race - conditions with disconnect(), one close() and multiple (not - necessarily simultaneous) attempts to open(). For example, it - prevents from waiting for a second access, while the device - structure is being deallocated, after a possible disconnect() and - during a following close() holding the write lock: given that, after - this deallocation, no access will be possible anymore, using the - non-trylock version would have let open() gain the access to the - device structure improperly. - For this reason the lock must also not be per-device. - */ - if (!down_read_trylock(&sn9c102_dev_lock)) - return -ERESTARTSYS; - - cam = video_drvdata(filp); - - if (wait_for_completion_interruptible(&cam->probe)) { - up_read(&sn9c102_dev_lock); - return -ERESTARTSYS; - } - - kref_get(&cam->kref); - - /* - Make sure to isolate all the simultaneous opens. - */ - if (mutex_lock_interruptible(&cam->open_mutex)) { - kref_put(&cam->kref, sn9c102_release_resources); - up_read(&sn9c102_dev_lock); - return -ERESTARTSYS; - } - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - err = -ENODEV; - goto out; - } - - if (cam->users) { - DBG(2, "Device %s is already in use", - video_device_node_name(cam->v4ldev)); - DBG(3, "Simultaneous opens are not supported"); - /* - open() must follow the open flags and should block - eventually while the device is in use. - */ - if ((filp->f_flags & O_NONBLOCK) || - (filp->f_flags & O_NDELAY)) { - err = -EWOULDBLOCK; - goto out; - } - DBG(2, "A blocking open() has been requested. Wait for the " - "device to be released..."); - up_read(&sn9c102_dev_lock); - /* - We will not release the "open_mutex" lock, so that only one - process can be in the wait queue below. This way the process - will be sleeping while holding the lock, without losing its - priority after any wake_up(). - */ - err = wait_event_interruptible_exclusive(cam->wait_open, - (cam->state & DEV_DISCONNECTED) - || !cam->users); - down_read(&sn9c102_dev_lock); - if (err) - goto out; - if (cam->state & DEV_DISCONNECTED) { - err = -ENODEV; - goto out; - } - } - - if (cam->state & DEV_MISCONFIGURED) { - err = sn9c102_init(cam); - if (err) { - DBG(1, "Initialization failed again. " - "I will retry on next open()."); - goto out; - } - cam->state &= ~DEV_MISCONFIGURED; - } - - err = sn9c102_start_transfer(cam); - if (err) - goto out; - - filp->private_data = cam; - cam->users++; - cam->io = IO_NONE; - cam->stream = STREAM_OFF; - cam->nbuffers = 0; - cam->frame_count = 0; - sn9c102_empty_framequeues(cam); - - DBG(3, "Video device %s is open", video_device_node_name(cam->v4ldev)); - -out: - mutex_unlock(&cam->open_mutex); - if (err) - kref_put(&cam->kref, sn9c102_release_resources); - - up_read(&sn9c102_dev_lock); - return err; -} - - -static int sn9c102_release(struct file *filp) -{ - struct sn9c102_device *cam; - - down_write(&sn9c102_dev_lock); - - cam = video_drvdata(filp); - - sn9c102_stop_transfer(cam); - sn9c102_release_buffers(cam); - cam->users--; - wake_up_interruptible_nr(&cam->wait_open, 1); - - DBG(3, "Video device %s closed", video_device_node_name(cam->v4ldev)); - - kref_put(&cam->kref, sn9c102_release_resources); - - up_write(&sn9c102_dev_lock); - - return 0; -} - - -static ssize_t -sn9c102_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) -{ - struct sn9c102_device *cam = video_drvdata(filp); - struct sn9c102_frame_t *f, *i; - unsigned long lock_flags; - long timeout; - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (cam->io == IO_MMAP) { - DBG(3, "Close and open the device again to choose " - "the read method"); - mutex_unlock(&cam->fileop_mutex); - return -EBUSY; - } - - if (cam->io == IO_NONE) { - if (!sn9c102_request_buffers(cam, cam->nreadbuffers, IO_READ)) { - DBG(1, "read() failed, not enough memory"); - mutex_unlock(&cam->fileop_mutex); - return -ENOMEM; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (list_empty(&cam->inqueue)) { - if (!list_empty(&cam->outqueue)) - sn9c102_empty_framequeues(cam); - sn9c102_queue_unusedframes(cam); - } - - if (!count) { - mutex_unlock(&cam->fileop_mutex); - return 0; - } - - if (list_empty(&cam->outqueue)) { - if (filp->f_flags & O_NONBLOCK) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - if (!cam->module_param.frame_timeout) { - err = wait_event_interruptible - (cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED)); - if (err) { - mutex_unlock(&cam->fileop_mutex); - return err; - } - } else { - timeout = wait_event_interruptible_timeout - (cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - msecs_to_jiffies( - cam->module_param.frame_timeout * 1000 - ) - ); - if (timeout < 0) { - mutex_unlock(&cam->fileop_mutex); - return timeout; - } else if (timeout == 0 && - !(cam->state & DEV_DISCONNECTED)) { - DBG(1, "Video frame timeout elapsed"); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - if (cam->state & DEV_DISCONNECTED) { - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - if (cam->state & DEV_MISCONFIGURED) { - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - - f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame); - - if (count > f->buf.bytesused) - count = f->buf.bytesused; - - if (copy_to_user(buf, f->bufmem, count)) { - err = -EFAULT; - goto exit; - } - *f_pos += count; - -exit: - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(i, &cam->outqueue, frame) - i->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - sn9c102_queue_unusedframes(cam); - - PDBGG("Frame #%lu, bytes read: %zu", - (unsigned long)f->buf.index, count); - - mutex_unlock(&cam->fileop_mutex); - - return count; -} - - -static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) -{ - struct sn9c102_device *cam = video_drvdata(filp); - struct sn9c102_frame_t *f; - unsigned long lock_flags; - unsigned int mask = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return POLLERR; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - goto error; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - goto error; - } - - if (cam->io == IO_NONE) { - if (!sn9c102_request_buffers(cam, cam->nreadbuffers, - IO_READ)) { - DBG(1, "poll() failed, not enough memory"); - goto error; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (cam->io == IO_READ) { - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(f, &cam->outqueue, frame) - f->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - sn9c102_queue_unusedframes(cam); - } - - poll_wait(filp, &cam->wait_frame, wait); - - if (!list_empty(&cam->outqueue)) - mask |= POLLIN | POLLRDNORM; - - mutex_unlock(&cam->fileop_mutex); - - return mask; - -error: - mutex_unlock(&cam->fileop_mutex); - return POLLERR; -} - - -static void sn9c102_vm_open(struct vm_area_struct *vma) -{ - struct sn9c102_frame_t *f = vma->vm_private_data; - f->vma_use_count++; -} - - -static void sn9c102_vm_close(struct vm_area_struct *vma) -{ - /* NOTE: buffers are not freed here */ - struct sn9c102_frame_t *f = vma->vm_private_data; - f->vma_use_count--; -} - - -static const struct vm_operations_struct sn9c102_vm_ops = { - .open = sn9c102_vm_open, - .close = sn9c102_vm_close, -}; - - -static int sn9c102_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct sn9c102_device *cam = video_drvdata(filp); - unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; - void *pos; - u32 i; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { - mutex_unlock(&cam->fileop_mutex); - return -EACCES; - } - - if (cam->io != IO_MMAP || - size != PAGE_ALIGN(cam->frame[0].buf.length)) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - for (i = 0; i < cam->nbuffers; i++) { - if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) - break; - } - if (i == cam->nbuffers) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; - - pos = cam->frame[i].bufmem; - while (size > 0) { /* size is page-aligned */ - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &sn9c102_vm_ops; - vma->vm_private_data = &cam->frame[i]; - sn9c102_vm_open(vma); - - mutex_unlock(&cam->fileop_mutex); - - return 0; -} - -/*****************************************************************************/ - -static int -sn9c102_vidioc_querycap(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_capability cap = { - .driver = "sn9c102", - .version = LINUX_VERSION_CODE, - .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, - }; - - strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); - if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), - sizeof(cap.bus_info)); - - if (copy_to_user(arg, &cap, sizeof(cap))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_enuminput(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_input i; - - if (copy_from_user(&i, arg, sizeof(i))) - return -EFAULT; - - if (i.index) - return -EINVAL; - - memset(&i, 0, sizeof(i)); - strcpy(i.name, "Camera"); - i.type = V4L2_INPUT_TYPE_CAMERA; - i.capabilities = V4L2_IN_CAP_STD; - - if (copy_to_user(arg, &i, sizeof(i))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_input(struct sn9c102_device *cam, void __user *arg) -{ - int index = 0; - - if (copy_to_user(arg, &index, sizeof(index))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_input(struct sn9c102_device *cam, void __user *arg) -{ - int index; - - if (copy_from_user(&index, arg, sizeof(index))) - return -EFAULT; - - if (index != 0) - return -EINVAL; - - return 0; -} - - -static int -sn9c102_vidioc_query_ctrl(struct sn9c102_device *cam, void __user *arg) -{ - struct sn9c102_sensor *s = &cam->sensor; - struct v4l2_queryctrl qc; - u8 i; - - if (copy_from_user(&qc, arg, sizeof(qc))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (qc.id && qc.id == s->qctrl[i].id) { - memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); - if (copy_to_user(arg, &qc, sizeof(qc))) - return -EFAULT; - return 0; - } - - return -EINVAL; -} - - -static int -sn9c102_vidioc_g_ctrl(struct sn9c102_device *cam, void __user *arg) -{ - struct sn9c102_sensor *s = &cam->sensor; - struct v4l2_control ctrl; - int err = 0; - u8 i; - - if (!s->get_ctrl && !s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - if (!s->get_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (ctrl.id && ctrl.id == s->qctrl[i].id) { - ctrl.value = s->_qctrl[i].default_value; - goto exit; - } - return -EINVAL; - } else - err = s->get_ctrl(cam, &ctrl); - -exit: - if (copy_to_user(arg, &ctrl, sizeof(ctrl))) - return -EFAULT; - - PDBGG("VIDIOC_G_CTRL: id %lu, value %lu", - (unsigned long)ctrl.id, (unsigned long)ctrl.value); - - return err; -} - - -static int -sn9c102_vidioc_s_ctrl(struct sn9c102_device *cam, void __user *arg) -{ - struct sn9c102_sensor *s = &cam->sensor; - struct v4l2_control ctrl; - u8 i; - int err = 0; - - if (!s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { - if (ctrl.id == s->qctrl[i].id) { - if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) - return -EINVAL; - if (ctrl.value < s->qctrl[i].minimum || - ctrl.value > s->qctrl[i].maximum) - return -ERANGE; - ctrl.value -= ctrl.value % s->qctrl[i].step; - break; - } - } - if (i == ARRAY_SIZE(s->qctrl)) - return -EINVAL; - err = s->set_ctrl(cam, &ctrl); - if (err) - return err; - - s->_qctrl[i].default_value = ctrl.value; - - PDBGG("VIDIOC_S_CTRL: id %lu, value %lu", - (unsigned long)ctrl.id, (unsigned long)ctrl.value); - - return 0; -} - - -static int -sn9c102_vidioc_cropcap(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_cropcap *cc = &(cam->sensor.cropcap); - - cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cc->pixelaspect.numerator = 1; - cc->pixelaspect.denominator = 1; - - if (copy_to_user(arg, cc, sizeof(*cc))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_crop(struct sn9c102_device *cam, void __user *arg) -{ - struct sn9c102_sensor *s = &cam->sensor; - struct v4l2_crop crop = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - }; - - memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); - - if (copy_to_user(arg, &crop, sizeof(crop))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_crop(struct sn9c102_device *cam, void __user *arg) -{ - struct sn9c102_sensor *s = &cam->sensor; - struct v4l2_crop crop; - struct v4l2_rect *rect; - struct v4l2_rect *bounds = &(s->cropcap.bounds); - struct v4l2_pix_format *pix_format = &(s->pix_format); - u8 scale; - const enum sn9c102_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&crop, arg, sizeof(crop))) - return -EFAULT; - - rect = &(crop.c); - - if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_CROP failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - /* Preserve R,G or B origin */ - rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; - rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; - - if (rect->width < 16) - rect->width = 16; - if (rect->height < 16) - rect->height = 16; - if (rect->width > bounds->width) - rect->width = bounds->width; - if (rect->height > bounds->height) - rect->height = bounds->height; - if (rect->left < bounds->left) - rect->left = bounds->left; - if (rect->top < bounds->top) - rect->top = bounds->top; - if (rect->left + rect->width > bounds->left + bounds->width) - rect->left = bounds->left+bounds->width - rect->width; - if (rect->top + rect->height > bounds->top + bounds->height) - rect->top = bounds->top+bounds->height - rect->height; - - rect->width &= ~15L; - rect->height &= ~15L; - - if (SN9C102_PRESERVE_IMGSCALE) { - /* Calculate the actual scaling factor */ - u32 a, b; - a = rect->width * rect->height; - b = pix_format->width * pix_format->height; - scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; - } else - scale = 1; - - if (cam->stream == STREAM_ON) { - err = sn9c102_stream_interrupt(cam); - if (err) - return err; - } - - if (copy_to_user(arg, &crop, sizeof(crop))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - sn9c102_release_buffers(cam); - - err = sn9c102_set_crop(cam, rect); - if (s->set_crop) - err += s->set_crop(cam, rect); - err += sn9c102_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - s->pix_format.width = rect->width/scale; - s->pix_format.height = rect->height/scale; - memcpy(&(s->_rect), rect, sizeof(*rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - sn9c102_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - sn9c102_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -sn9c102_vidioc_enum_framesizes(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_frmsizeenum frmsize; - - if (copy_from_user(&frmsize, arg, sizeof(frmsize))) - return -EFAULT; - - if (frmsize.index != 0) - return -EINVAL; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (frmsize.pixel_format != V4L2_PIX_FMT_SN9C10X && - frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) - return -EINVAL; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG && - frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) - return -EINVAL; - break; - } - - frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE; - frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16; - frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16; - frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width; - frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height; - memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); - - if (copy_to_user(arg, &frmsize, sizeof(frmsize))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_enum_fmt(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_fmtdesc fmtd; - - if (copy_from_user(&fmtd, arg, sizeof(fmtd))) - return -EFAULT; - - if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (fmtd.index == 0) { - strcpy(fmtd.description, "bayer rgb"); - fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; - } else if (fmtd.index == 1) { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - strcpy(fmtd.description, "compressed"); - fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - strcpy(fmtd.description, "JPEG"); - fmtd.pixelformat = V4L2_PIX_FMT_JPEG; - break; - } - fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; - } else - return -EINVAL; - - fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); - - if (copy_to_user(arg, &fmtd, sizeof(fmtd))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_fmt(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_format format; - struct v4l2_pix_format *pfmt = &(cam->sensor.pix_format); - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ? - V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; - pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X || - pfmt->pixelformat == V4L2_PIX_FMT_JPEG) - ? 0 : (pfmt->width * pfmt->priv) / 8; - pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); - pfmt->field = V4L2_FIELD_NONE; - memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); - - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_try_s_fmt(struct sn9c102_device *cam, unsigned int cmd, - void __user *arg) -{ - struct sn9c102_sensor *s = &cam->sensor; - struct v4l2_format format; - struct v4l2_pix_format *pix; - struct v4l2_pix_format *pfmt = &(s->pix_format); - struct v4l2_rect *bounds = &(s->cropcap.bounds); - struct v4l2_rect rect; - u8 scale; - const enum sn9c102_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - pix = &(format.fmt.pix); - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memcpy(&rect, &(s->_rect), sizeof(rect)); - - { /* calculate the actual scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; - } - - rect.width = scale * pix->width; - rect.height = scale * pix->height; - - if (rect.width < 16) - rect.width = 16; - if (rect.height < 16) - rect.height = 16; - if (rect.width > bounds->left + bounds->width - rect.left) - rect.width = bounds->left + bounds->width - rect.left; - if (rect.height > bounds->top + bounds->height - rect.top) - rect.height = bounds->top + bounds->height - rect.top; - - rect.width &= ~15L; - rect.height &= ~15L; - - { /* adjust the scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; - } - - pix->width = rect.width / scale; - pix->height = rect.height / scale; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat != V4L2_PIX_FMT_JPEG && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; - break; - } - pix->priv = pfmt->priv; /* bpp */ - pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ? - V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; - pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || - pix->pixelformat == V4L2_PIX_FMT_JPEG) - ? 0 : (pix->width * pix->priv) / 8; - pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); - pix->field = V4L2_FIELD_NONE; - - if (cmd == VIDIOC_TRY_FMT) { - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - return 0; - } - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_FMT failed. Unmap the " - "buffers first."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) { - err = sn9c102_stream_interrupt(cam); - if (err) - return err; - } - - if (copy_to_user(arg, &format, sizeof(format))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - sn9c102_release_buffers(cam); - - err += sn9c102_set_pix_format(cam, pix); - err += sn9c102_set_crop(cam, &rect); - if (s->set_pix_format) - err += s->set_pix_format(cam, pix); - if (s->set_crop) - err += s->set_crop(cam, &rect); - err += sn9c102_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - memcpy(pfmt, pix, sizeof(*pix)); - memcpy(&(s->_rect), &rect, sizeof(rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - sn9c102_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - sn9c102_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -sn9c102_vidioc_g_jpegcomp(struct sn9c102_device *cam, void __user *arg) -{ - if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_jpegcomp(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_jpegcompression jc; - const enum sn9c102_stream_state stream = cam->stream; - int err = 0; - - if (copy_from_user(&jc, arg, sizeof(jc))) - return -EFAULT; - - if (jc.quality != 0 && jc.quality != 1) - return -EINVAL; - - if (cam->stream == STREAM_ON) { - err = sn9c102_stream_interrupt(cam); - if (err) - return err; - } - - err += sn9c102_set_compression(cam, &jc); - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware problems. " - "To use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - cam->compression.quality = jc.quality; - - cam->stream = stream; - - return 0; -} - - -static int -sn9c102_vidioc_reqbufs(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_requestbuffers rb; - u32 i; - int err; - - if (copy_from_user(&rb, arg, sizeof(rb))) - return -EFAULT; - - if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - rb.memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (cam->io == IO_READ) { - DBG(3, "Close and open the device again to choose the mmap " - "I/O method"); - return -EBUSY; - } - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are " - "still mapped."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) { - err = sn9c102_stream_interrupt(cam); - if (err) - return err; - } - - sn9c102_empty_framequeues(cam); - - sn9c102_release_buffers(cam); - if (rb.count) - rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP); - - if (copy_to_user(arg, &rb, sizeof(rb))) { - sn9c102_release_buffers(cam); - cam->io = IO_NONE; - return -EFAULT; - } - - cam->io = rb.count ? IO_MMAP : IO_NONE; - - return 0; -} - - -static int -sn9c102_vidioc_querybuf(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_buffer b; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - b = cam->frame[b.index].buf; - - if (cam->frame[b.index].vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (cam->frame[b.index].state == F_DONE) - b.flags |= V4L2_BUF_FLAG_DONE; - else if (cam->frame[b.index].state != F_UNUSED) - b.flags |= V4L2_BUF_FLAG_QUEUED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_qbuf(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_buffer b; - unsigned long lock_flags; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->frame[b.index].state != F_UNUSED) - return -EINVAL; - - cam->frame[b.index].state = F_QUEUED; - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - PDBGG("Frame #%lu queued", (unsigned long)b.index); - - return 0; -} - - -static int -sn9c102_vidioc_dqbuf(struct sn9c102_device *cam, struct file *filp, - void __user *arg) -{ - struct v4l2_buffer b; - struct sn9c102_frame_t *f; - unsigned long lock_flags; - long timeout; - int err = 0; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - if (list_empty(&cam->outqueue)) { - if (cam->stream == STREAM_OFF) - return -EINVAL; - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - if (!cam->module_param.frame_timeout) { - err = wait_event_interruptible - (cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED)); - if (err) - return err; - } else { - timeout = wait_event_interruptible_timeout - (cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1)); - if (timeout < 0) - return timeout; - else if (timeout == 0 && - !(cam->state & DEV_DISCONNECTED)) { - DBG(1, "Video frame timeout elapsed"); - return -EIO; - } - } - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - if (cam->state & DEV_MISCONFIGURED) - return -EIO; - } - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame); - list_del(cam->outqueue.next); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - f->state = F_UNUSED; - - b = f->buf; - if (f->vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); - - return 0; -} - - -static int -sn9c102_vidioc_streamon(struct sn9c102_device *cam, void __user *arg) -{ - int type; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - cam->stream = STREAM_ON; - - DBG(3, "Stream on"); - - return 0; -} - - -static int -sn9c102_vidioc_streamoff(struct sn9c102_device *cam, void __user *arg) -{ - int type, err; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->stream == STREAM_ON) { - err = sn9c102_stream_interrupt(cam); - if (err) - return err; - } - - sn9c102_empty_framequeues(cam); - - DBG(3, "Stream off"); - - return 0; -} - - -static int -sn9c102_vidioc_g_parm(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_parm(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - - if (sp.parm.capture.readbuffers == 0) - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES) - sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - cam->nreadbuffers = sp.parm.capture.readbuffers; - - return 0; -} - - -static int -sn9c102_vidioc_enumaudio(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - if (audio.index != 0) - return -EINVAL; - - strcpy(audio.name, "Microphone"); - audio.capability = 0; - audio.mode = 0; - - if (copy_to_user(arg, &audio, sizeof(audio))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_audio(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - memset(&audio, 0, sizeof(audio)); - strcpy(audio.name, "Microphone"); - - if (copy_to_user(arg, &audio, sizeof(audio))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_audio(struct sn9c102_device *cam, void __user *arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - if (audio.index != 0) - return -EINVAL; - - return 0; -} - - -static long sn9c102_ioctl_v4l2(struct file *filp, - unsigned int cmd, void __user *arg) -{ - struct sn9c102_device *cam = video_drvdata(filp); - - switch (cmd) { - - case VIDIOC_QUERYCAP: - return sn9c102_vidioc_querycap(cam, arg); - - case VIDIOC_ENUMINPUT: - return sn9c102_vidioc_enuminput(cam, arg); - - case VIDIOC_G_INPUT: - return sn9c102_vidioc_g_input(cam, arg); - - case VIDIOC_S_INPUT: - return sn9c102_vidioc_s_input(cam, arg); - - case VIDIOC_QUERYCTRL: - return sn9c102_vidioc_query_ctrl(cam, arg); - - case VIDIOC_G_CTRL: - return sn9c102_vidioc_g_ctrl(cam, arg); - - case VIDIOC_S_CTRL: - return sn9c102_vidioc_s_ctrl(cam, arg); - - case VIDIOC_CROPCAP: - return sn9c102_vidioc_cropcap(cam, arg); - - case VIDIOC_G_CROP: - return sn9c102_vidioc_g_crop(cam, arg); - - case VIDIOC_S_CROP: - return sn9c102_vidioc_s_crop(cam, arg); - - case VIDIOC_ENUM_FRAMESIZES: - return sn9c102_vidioc_enum_framesizes(cam, arg); - - case VIDIOC_ENUM_FMT: - return sn9c102_vidioc_enum_fmt(cam, arg); - - case VIDIOC_G_FMT: - return sn9c102_vidioc_g_fmt(cam, arg); - - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - return sn9c102_vidioc_try_s_fmt(cam, cmd, arg); - - case VIDIOC_G_JPEGCOMP: - return sn9c102_vidioc_g_jpegcomp(cam, arg); - - case VIDIOC_S_JPEGCOMP: - return sn9c102_vidioc_s_jpegcomp(cam, arg); - - case VIDIOC_REQBUFS: - return sn9c102_vidioc_reqbufs(cam, arg); - - case VIDIOC_QUERYBUF: - return sn9c102_vidioc_querybuf(cam, arg); - - case VIDIOC_QBUF: - return sn9c102_vidioc_qbuf(cam, arg); - - case VIDIOC_DQBUF: - return sn9c102_vidioc_dqbuf(cam, filp, arg); - - case VIDIOC_STREAMON: - return sn9c102_vidioc_streamon(cam, arg); - - case VIDIOC_STREAMOFF: - return sn9c102_vidioc_streamoff(cam, arg); - - case VIDIOC_G_PARM: - return sn9c102_vidioc_g_parm(cam, arg); - - case VIDIOC_S_PARM: - return sn9c102_vidioc_s_parm(cam, arg); - - case VIDIOC_ENUMAUDIO: - return sn9c102_vidioc_enumaudio(cam, arg); - - case VIDIOC_G_AUDIO: - return sn9c102_vidioc_g_audio(cam, arg); - - case VIDIOC_S_AUDIO: - return sn9c102_vidioc_s_audio(cam, arg); - - default: - return -ENOTTY; - - } -} - - -static long sn9c102_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct sn9c102_device *cam = video_drvdata(filp); - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - V4LDBG(3, "sn9c102", cmd); - - err = sn9c102_ioctl_v4l2(filp, cmd, (void __user *)arg); - - mutex_unlock(&cam->fileop_mutex); - - return err; -} - -/*****************************************************************************/ - -static const struct v4l2_file_operations sn9c102_fops = { - .owner = THIS_MODULE, - .open = sn9c102_open, - .release = sn9c102_release, - .unlocked_ioctl = sn9c102_ioctl, - .read = sn9c102_read, - .poll = sn9c102_poll, - .mmap = sn9c102_mmap, -}; - -/*****************************************************************************/ - -/* It exists a single interface only. We do not need to validate anything. */ -static int -sn9c102_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct sn9c102_device *cam; - static unsigned int dev_nr; - unsigned int i; - int err = 0, r; - - cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL); - if (!cam) - return -ENOMEM; - - cam->usbdev = udev; - - /* register v4l2_device early so it can be used for printks */ - if (v4l2_device_register(&intf->dev, &cam->v4l2_dev)) { - dev_err(&intf->dev, "v4l2_device_register failed\n"); - err = -ENOMEM; - goto fail; - } - - cam->control_buffer = kzalloc(8, GFP_KERNEL); - if (!cam->control_buffer) { - DBG(1, "kzalloc() failed"); - err = -ENOMEM; - goto fail; - } - - cam->v4ldev = video_device_alloc(); - if (!cam->v4ldev) { - DBG(1, "video_device_alloc() failed"); - err = -ENOMEM; - goto fail; - } - - r = sn9c102_read_reg(cam, 0x00); - if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) { - DBG(1, "Sorry, this is not a SN9C1xx-based camera " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - err = -ENODEV; - goto fail; - } - - cam->bridge = id->driver_info; - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - DBG(2, "SN9C10[12] PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C103: - DBG(2, "SN9C103 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C105: - DBG(2, "SN9C105 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C120: - DBG(2, "SN9C120 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - } - - for (i = 0; i < ARRAY_SIZE(sn9c102_sensor_table); i++) { - err = sn9c102_sensor_table[i](cam); - if (!err) - break; - } - - if (!err) { - DBG(2, "%s image sensor detected", cam->sensor.name); - DBG(3, "Support for %s maintained by %s", - cam->sensor.name, cam->sensor.maintainer); - } else { - DBG(1, "No supported image sensor detected for this bridge"); - err = -ENODEV; - goto fail; - } - - if (!(cam->bridge & cam->sensor.supported_bridge)) { - DBG(1, "Bridge not supported"); - err = -ENODEV; - goto fail; - } - - if (sn9c102_init(cam)) { - DBG(1, "Initialization failed. I will retry on open()."); - cam->state |= DEV_MISCONFIGURED; - } - - strcpy(cam->v4ldev->name, "SN9C1xx PC Camera"); - cam->v4ldev->fops = &sn9c102_fops; - cam->v4ldev->release = video_device_release; - cam->v4ldev->v4l2_dev = &cam->v4l2_dev; - - init_completion(&cam->probe); - - err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, - video_nr[dev_nr]); - if (err) { - DBG(1, "V4L2 device registration failed"); - if (err == -ENFILE && video_nr[dev_nr] == -1) - DBG(1, "Free /dev/videoX node not found"); - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; - complete_all(&cam->probe); - goto fail; - } - - DBG(2, "V4L2 device registered as %s", - video_device_node_name(cam->v4ldev)); - - video_set_drvdata(cam->v4ldev, cam); - cam->module_param.force_munmap = force_munmap[dev_nr]; - cam->module_param.frame_timeout = frame_timeout[dev_nr]; - - dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; - -#ifdef CONFIG_VIDEO_ADV_DEBUG - err = sn9c102_create_sysfs(cam); - if (!err) - DBG(2, "Optional device control through 'sysfs' " - "interface ready"); - else - DBG(2, "Failed to create optional 'sysfs' interface for " - "device controlling. Error #%d", err); -#else - DBG(2, "Optional device control through 'sysfs' interface disabled"); - DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' " - "configuration option to enable it."); -#endif - - usb_set_intfdata(intf, cam); - kref_init(&cam->kref); - usb_get_dev(cam->usbdev); - - complete_all(&cam->probe); - - return 0; - -fail: - if (cam) { - kfree(cam->control_buffer); - if (cam->v4ldev) - video_device_release(cam->v4ldev); - v4l2_device_unregister(&cam->v4l2_dev); - kfree(cam); - } - return err; -} - - -static void sn9c102_usb_disconnect(struct usb_interface *intf) -{ - struct sn9c102_device *cam; - - down_write(&sn9c102_dev_lock); - - cam = usb_get_intfdata(intf); - - DBG(2, "Disconnecting %s...", cam->v4ldev->name); - - if (cam->users) { - DBG(2, "Device %s is open! Deregistration and memory " - "deallocation are deferred.", - video_device_node_name(cam->v4ldev)); - cam->state |= DEV_MISCONFIGURED; - sn9c102_stop_transfer(cam); - cam->state |= DEV_DISCONNECTED; - wake_up_interruptible(&cam->wait_frame); - wake_up(&cam->wait_stream); - } else - cam->state |= DEV_DISCONNECTED; - - wake_up_interruptible_all(&cam->wait_open); - - v4l2_device_disconnect(&cam->v4l2_dev); - - kref_put(&cam->kref, sn9c102_release_resources); - - up_write(&sn9c102_dev_lock); -} - - -static struct usb_driver sn9c102_usb_driver = { - .name = "sn9c102", - .id_table = sn9c102_id_table, - .probe = sn9c102_usb_probe, - .disconnect = sn9c102_usb_disconnect, -}; - -module_usb_driver(sn9c102_usb_driver); diff --git a/drivers/staging/media/sn9c102/sn9c102_devtable.h b/drivers/staging/media/sn9c102/sn9c102_devtable.h deleted file mode 100644 index b187a8a..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_devtable.h +++ /dev/null @@ -1,145 +0,0 @@ -/*************************************************************************** - * Table of device identifiers of the SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#ifndef _SN9C102_DEVTABLE_H_ -#define _SN9C102_DEVTABLE_H_ - -#include - -struct sn9c102_device; - -/* - Each SN9C1xx camera has proper PID/VID identifiers. - SN9C103, SN9C105, SN9C120 support multiple interfaces, but we only have to - handle the video class interface. -*/ -#define SN9C102_USB_DEVICE(vend, prod, bridge) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ - .idVendor = (vend), \ - .idProduct = (prod), \ - .bInterfaceClass = 0xff, \ - .driver_info = (bridge) - -static const struct usb_device_id sn9c102_id_table[] = { - /* SN9C101 and SN9C102 */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ - { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, /* not in sonixb */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ - { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, /* not in sonixb */ - /* SN9C103 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, /* not in sonixb */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE -/* { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, HY7131D/E */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ - { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, PAS106 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, TAS5130 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, TAS5110, non existent */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ba, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, non existent ? */ -#endif - /* SN9C105 */ -#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE - { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, PO1030 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, OM6801 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, HV7131GP */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, MO4000 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, ICM105C */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, OV7648 */ - { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, - /* SN9C120 */ - { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, po2030 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, om6801 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, S5K53BEB */ - { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ - { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, -#endif - { } -}; - -/* - Probing functions: on success, you must attach the sensor to the camera - by calling sn9c102_attach_sensor(). - To enable the I2C communication, you might need to perform a really basic - initialization of the SN9C1XX chip. - Functions must return 0 on success, the appropriate error otherwise. -*/ -extern int sn9c102_probe_hv7131d(struct sn9c102_device *cam); -extern int sn9c102_probe_hv7131r(struct sn9c102_device *cam); -extern int sn9c102_probe_mi0343(struct sn9c102_device *cam); -extern int sn9c102_probe_mi0360(struct sn9c102_device *cam); -extern int sn9c102_probe_mt9v111(struct sn9c102_device *cam); -extern int sn9c102_probe_ov7630(struct sn9c102_device *cam); -extern int sn9c102_probe_ov7660(struct sn9c102_device *cam); -extern int sn9c102_probe_pas106b(struct sn9c102_device *cam); -extern int sn9c102_probe_pas202bcb(struct sn9c102_device *cam); -extern int sn9c102_probe_tas5110c1b(struct sn9c102_device *cam); -extern int sn9c102_probe_tas5110d(struct sn9c102_device *cam); -extern int sn9c102_probe_tas5130d1b(struct sn9c102_device *cam); - -#endif /* _SN9C102_DEVTABLE_H_ */ diff --git a/drivers/staging/media/sn9c102/sn9c102_hv7131d.c b/drivers/staging/media/sn9c102/sn9c102_hv7131d.c deleted file mode 100644 index f1d94f0..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_hv7131d.c +++ /dev/null @@ -1,269 +0,0 @@ -/*************************************************************************** - * Plug-in for HV7131D image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int hv7131d_init(struct sn9c102_device *cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x60, 0x17}, - {0x0e, 0x18}, {0xf2, 0x19}); - - err += sn9c102_i2c_write(cam, 0x01, 0x04); - err += sn9c102_i2c_write(cam, 0x02, 0x00); - err += sn9c102_i2c_write(cam, 0x28, 0x00); - - return err; -} - - -static int hv7131d_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x26), - r2 = sn9c102_i2c_read(cam, 0x27); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 8) | (r2 & 0xff); - } - return 0; - case V4L2_CID_RED_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x31); - if (ctrl->value < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x33); - if (ctrl->value < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x32); - if (ctrl->value < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case SN9C102_V4L2_CID_RESET_LEVEL: - ctrl->value = sn9c102_i2c_read(cam, 0x30); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x3f; - return 0; - case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: - ctrl->value = sn9c102_i2c_read(cam, 0x34); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x07; - return 0; - default: - return -EINVAL; - } -} - - -static int hv7131d_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x26, ctrl->value >> 8); - err += sn9c102_i2c_write(cam, 0x27, ctrl->value & 0xff); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x31, 0x3f - ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x33, 0x3f - ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x32, 0x3f - ctrl->value); - break; - case SN9C102_V4L2_CID_RESET_LEVEL: - err += sn9c102_i2c_write(cam, 0x30, ctrl->value); - break; - case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: - err += sn9c102_i2c_write(cam, 0x34, ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int hv7131d_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int hv7131d_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x42, 0x19); - else - err += sn9c102_write_reg(cam, 0xf2, 0x19); - - return err; -} - - -static const struct sn9c102_sensor hv7131d = { - .name = "HV7131D", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x11, - .init = &hv7131d_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x0250, - .maximum = 0xffff, - .step = 0x0001, - .default_value = 0x0250, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x1e, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_RESET_LEVEL, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "reset level", - .minimum = 0x19, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x30, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "pixel bias voltage", - .minimum = 0x00, - .maximum = 0x07, - .step = 0x01, - .default_value = 0x02, - .flags = 0, - }, - }, - .get_ctrl = &hv7131d_get_ctrl, - .set_ctrl = &hv7131d_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &hv7131d_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &hv7131d_set_pix_format -}; - - -int sn9c102_probe_hv7131d(struct sn9c102_device *cam) -{ - int r0 = 0, r1 = 0, err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - - r0 = sn9c102_i2c_try_read(cam, &hv7131d, 0x00); - r1 = sn9c102_i2c_try_read(cam, &hv7131d, 0x01); - if (err || r0 < 0 || r1 < 0) - return -EIO; - - if ((r0 != 0x00 && r0 != 0x01) || r1 != 0x04) - return -ENODEV; - - sn9c102_attach_sensor(cam, &hv7131d); - - return 0; -} diff --git a/drivers/staging/media/sn9c102/sn9c102_hv7131r.c b/drivers/staging/media/sn9c102/sn9c102_hv7131r.c deleted file mode 100644 index 51b24e0..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_hv7131r.c +++ /dev/null @@ -1,369 +0,0 @@ -/*************************************************************************** - * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int hv7131r_init(struct sn9c102_device *cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x03}, {0x1a, 0x04}, - {0x20, 0x05}, {0x20, 0x06}, - {0x03, 0x10}, {0x00, 0x14}, - {0x60, 0x17}, {0x0a, 0x18}, - {0xf0, 0x19}, {0x1d, 0x1a}, - {0x10, 0x1b}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x44, 0x05}, {0x3e, 0x06}, - {0x1a, 0x07}, {0x03, 0x10}, - {0x08, 0x14}, {0xa3, 0x17}, - {0x4b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3F}, - {0xC7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xC7, 0x48}, {0x01, 0x49}, - {0xC7, 0x4A}, {0x01, 0x4B}, - {0xC7, 0x4C}, {0x01, 0x4D}, - {0x44, 0x4E}, {0x00, 0x4F}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xC7, 0x54}, {0x01, 0x55}, - {0xC7, 0x56}, {0x01, 0x57}, - {0xC7, 0x58}, {0x01, 0x59}, - {0x44, 0x5A}, {0x00, 0x5B}, - {0x44, 0x5C}, {0x00, 0x5D}, - {0x44, 0x5E}, {0x00, 0x5F}, - {0xC7, 0x60}, {0x01, 0x61}, - {0xC7, 0x62}, {0x01, 0x63}, - {0xC7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6A}, {0x00, 0x6B}, - {0xC7, 0x6C}, {0x01, 0x6D}, - {0xC7, 0x6E}, {0x01, 0x6F}, - {0xC7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xC7, 0x78}, {0x01, 0x79}, - {0xC7, 0x7A}, {0x01, 0x7B}, - {0xC7, 0x7C}, {0x01, 0x7D}, - {0x44, 0x7E}, {0x00, 0x7F}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xEC, 0x8A}, {0x0f, 0x8B}, - {0xD8, 0x8C}, {0x0f, 0x8D}, - {0x3D, 0x8E}, {0x00, 0x8F}, - {0x3D, 0x90}, {0x00, 0x91}, - {0xCD, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0C, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9A}, {0x00, 0x9B}, - {0x04, 0x9C}, {0x00, 0x9D}, - {0x08, 0x9E}, {0x00, 0x9F}, - {0x2D, 0xC0}, {0x2D, 0xC1}, - {0x3A, 0xC2}, {0x05, 0xC3}, - {0x04, 0xC4}, {0x3F, 0xC5}, - {0x00, 0xC6}, {0x00, 0xC7}, - {0x50, 0xC8}, {0x3C, 0xC9}, - {0x28, 0xCA}, {0xD8, 0xCB}, - {0x14, 0xCC}, {0xEC, 0xCD}, - {0x32, 0xCE}, {0xDD, 0xCF}, - {0x32, 0xD0}, {0xDD, 0xD1}, - {0x6A, 0xD2}, {0x50, 0xD3}, - {0x00, 0xD4}, {0x00, 0xD5}, - {0x00, 0xD6}); - break; - default: - break; - } - - err += sn9c102_i2c_write(cam, 0x20, 0x00); - err += sn9c102_i2c_write(cam, 0x21, 0xd6); - err += sn9c102_i2c_write(cam, 0x25, 0x06); - - return err; -} - - -static int hv7131r_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_GAIN: - ctrl->value = sn9c102_i2c_read(cam, 0x30); - if (ctrl->value < 0) - return -EIO; - return 0; - case V4L2_CID_RED_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x31); - if (ctrl->value < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x33); - if (ctrl->value < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x32); - if (ctrl->value < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case V4L2_CID_BLACK_LEVEL: - ctrl->value = sn9c102_i2c_read(cam, 0x01); - if (ctrl->value < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x08) ? 1 : 0; - return 0; - default: - return -EINVAL; - } -} - - -static int hv7131r_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x30, ctrl->value); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x31, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x33, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x32, ctrl->value); - break; - case V4L2_CID_BLACK_LEVEL: - { - int r = sn9c102_i2c_read(cam, 0x01); - - if (r < 0) - return -EIO; - err += sn9c102_i2c_write(cam, 0x01, - (ctrl->value<<3) | (r&0xf7)); - } - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int hv7131r_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int hv7131r_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xa0, 0x19); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } else { - err += sn9c102_write_reg(cam, 0x30, 0x19); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xa5, 0x17); - err += sn9c102_i2c_write(cam, 0x01, 0x24); - } else { - err += sn9c102_write_reg(cam, 0xa3, 0x17); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } - break; - default: - break; - } - - return err; -} - - -static const struct sn9c102_sensor hv7131r = { - .name = "HV7131R", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x11, - .init = &hv7131r_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x40, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x08, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x1a, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x2f, - .flags = 0, - }, - { - .id = V4L2_CID_BLACK_LEVEL, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto black level compensation", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &hv7131r_get_ctrl, - .set_ctrl = &hv7131r_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &hv7131r_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &hv7131r_set_pix_format -}; - - -int sn9c102_probe_hv7131r(struct sn9c102_device *cam) -{ - int devid, err; - - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x02}, - {0x34, 0x01}, {0x20, 0x17}, - {0x34, 0x01}, {0x46, 0x01}); - - devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00); - if (err || devid < 0) - return -EIO; - - if (devid != 0x02) - return -ENODEV; - - sn9c102_attach_sensor(cam, &hv7131r); - - return 0; -} diff --git a/drivers/staging/media/sn9c102/sn9c102_mi0343.c b/drivers/staging/media/sn9c102/sn9c102_mi0343.c deleted file mode 100644 index b20fdb6..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_mi0343.c +++ /dev/null @@ -1,352 +0,0 @@ -/*************************************************************************** - * Plug-in for MI-0343 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mi0343_init(struct sn9c102_device *cam) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x40, 0x01}, - {0x20, 0x17}, {0x07, 0x18}, - {0xa0, 0x19}); - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe1, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x81, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x17, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x11, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, - 0x04, 0x9a, 0, 0); - - return err; -} - - -static int mi0343_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - u8 data[2]; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, - data) < 0) - return -EIO; - ctrl->value = data[0]; - return 0; - case V4L2_CID_GAIN: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, - data) < 0) - return -EIO; - break; - case V4L2_CID_HFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x20 ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - case V4L2_CID_RED_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, - data) < 0) - return -EIO; - break; - case V4L2_CID_BLUE_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, - data) < 0) - return -EIO; - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, - data) < 0) - return -EIO; - break; - default: - return -EINVAL; - } - - switch (ctrl->id) { - case V4L2_CID_GAIN: - case V4L2_CID_RED_BALANCE: - case V4L2_CID_BLUE_BALANCE: - case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = data[1] | (data[0] << 8); - if (ctrl->value >= 0x10 && ctrl->value <= 0x3f) - ctrl->value -= 0x10; - else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f) - ctrl->value -= 0x60; - else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff) - ctrl->value -= 0xe0; - } - - return 0; -} - - -static int mi0343_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - u16 reg = 0; - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - case V4L2_CID_RED_BALANCE: - case V4L2_CID_BLUE_BALANCE: - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (ctrl->value <= (0x3f-0x10)) - reg = 0x10 + ctrl->value; - else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60))) - reg = 0x60 + (ctrl->value - (0x3f-0x10)); - else - reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60)); - break; - } - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x09, ctrl->value, 0x00, - 0, 0); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x35, reg >> 8, reg & 0xff, - 0, 0); - break; - case V4L2_CID_HFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x40:0x00, - ctrl->value ? 0x20:0x00, - 0, 0); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x80:0x00, - ctrl->value ? 0x80:0x00, - 0, 0); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2d, reg >> 8, reg & 0xff, - 0, 0); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2c, reg >> 8, reg & 0xff, - 0, 0); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2b, reg >> 8, reg & 0xff, - 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2e, reg >> 8, reg & 0xff, - 0, 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int mi0343_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int mi0343_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x03, 0, 0); - err += sn9c102_write_reg(cam, 0x20, 0x19); - } else { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x05, 0, 0); - err += sn9c102_write_reg(cam, 0xa0, 0x19); - } - - return err; -} - - -static const struct sn9c102_sensor mi0343 = { - .name = "MI-0343", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5d, - .init = &mi0343_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x06, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/ - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &mi0343_get_ctrl, - .set_ctrl = &mi0343_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mi0343_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mi0343_set_pix_format -}; - - -int sn9c102_probe_mi0343(struct sn9c102_device *cam) -{ - u8 data[2]; - - if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00, - 2, data) < 0) - return -EIO; - - if (data[1] != 0x42 || data[0] != 0xe3) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mi0343); - - return 0; -} diff --git a/drivers/staging/media/sn9c102/sn9c102_mi0360.c b/drivers/staging/media/sn9c102/sn9c102_mi0360.c deleted file mode 100644 index 5f21d1b..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_mi0360.c +++ /dev/null @@ -1,453 +0,0 @@ -/*************************************************************************** - * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mi0360_init(struct sn9c102_device *cam) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x40, 0x01}, - {0x20, 0x17}, {0x07, 0x18}, - {0xa0, 0x19}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x50, 0x05}, {0x20, 0x06}, - {0x10, 0x07}, {0x03, 0x10}, - {0x08, 0x14}, {0xa2, 0x17}, - {0x47, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3F}, - {0xC7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xC7, 0x48}, {0x01, 0x49}, - {0xC7, 0x4A}, {0x01, 0x4B}, - {0xC7, 0x4C}, {0x01, 0x4D}, - {0x44, 0x4E}, {0x00, 0x4F}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xC7, 0x54}, {0x01, 0x55}, - {0xC7, 0x56}, {0x01, 0x57}, - {0xC7, 0x58}, {0x01, 0x59}, - {0x44, 0x5A}, {0x00, 0x5B}, - {0x44, 0x5C}, {0x00, 0x5D}, - {0x44, 0x5E}, {0x00, 0x5F}, - {0xC7, 0x60}, {0x01, 0x61}, - {0xC7, 0x62}, {0x01, 0x63}, - {0xC7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6A}, {0x00, 0x6B}, - {0xC7, 0x6C}, {0x01, 0x6D}, - {0xC7, 0x6E}, {0x01, 0x6F}, - {0xC7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xC7, 0x78}, {0x01, 0x79}, - {0xC7, 0x7A}, {0x01, 0x7B}, - {0xC7, 0x7C}, {0x01, 0x7D}, - {0x44, 0x7E}, {0x00, 0x7F}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xEC, 0x8A}, {0x0f, 0x8B}, - {0xD8, 0x8C}, {0x0f, 0x8D}, - {0x3D, 0x8E}, {0x00, 0x8F}, - {0x3D, 0x90}, {0x00, 0x91}, - {0xCD, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0C, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9A}, {0x00, 0x9B}, - {0x04, 0x9C}, {0x00, 0x9D}, - {0x08, 0x9E}, {0x00, 0x9F}, - {0x2D, 0xC0}, {0x2D, 0xC1}, - {0x3A, 0xC2}, {0x05, 0xC3}, - {0x04, 0xC4}, {0x3F, 0xC5}, - {0x00, 0xC6}, {0x00, 0xC7}, - {0x50, 0xC8}, {0x3C, 0xC9}, - {0x28, 0xCA}, {0xD8, 0xCB}, - {0x14, 0xCC}, {0xEC, 0xCD}, - {0x32, 0xCE}, {0xDD, 0xCF}, - {0x32, 0xD0}, {0xDD, 0xD1}, - {0x6A, 0xD2}, {0x50, 0xD3}, - {0x00, 0xD4}, {0x00, 0xD5}, - {0x00, 0xD6}); - break; - default: - break; - } - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe1, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x81, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x17, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x11, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, - 0x04, 0x9a, 0, 0); - - return err; -} - - -static int mi0360_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - u8 data[2]; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, - data) < 0) - return -EIO; - ctrl->value = data[0]; - return 0; - case V4L2_CID_GAIN: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_RED_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_BLUE_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_HFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x20 ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - default: - return -EINVAL; - } - - return 0; -} - - -static int mi0360_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x09, ctrl->value, 0x00, - 0, 0); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x35, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2c, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2d, 0x03, ctrl->value, - 0, 0); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2b, 0x03, ctrl->value, - 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2e, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_HFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x40:0x00, - ctrl->value ? 0x20:0x00, - 0, 0); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x80:0x00, - ctrl->value ? 0x80:0x00, - 0, 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int mi0360_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int mi0360_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x05, 0, 0); - err += sn9c102_write_reg(cam, 0x60, 0x19); - if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || - sn9c102_get_bridge(cam) == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, 0xa6, 0x17); - } else { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x02, 0, 0); - err += sn9c102_write_reg(cam, 0x20, 0x19); - if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || - sn9c102_get_bridge(cam) == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, 0xa2, 0x17); - } - - return err; -} - - -static const struct sn9c102_sensor mi0360 = { - .name = "MI-0360", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5d, - .init = &mi0360_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x25, - .flags = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x0f, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x32, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x25, - .flags = 0, - }, - }, - .get_ctrl = &mi0360_get_ctrl, - .set_ctrl = &mi0360_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mi0360_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mi0360_set_pix_format -}; - - -int sn9c102_probe_mi0360(struct sn9c102_device *cam) -{ - - u8 data[2]; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - break; - default: - break; - } - - if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00, - 2, data) < 0) - return -EIO; - - if (data[0] != 0x82 || data[1] != 0x43) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mi0360); - - return 0; -} diff --git a/drivers/staging/media/sn9c102/sn9c102_mt9v111.c b/drivers/staging/media/sn9c102/sn9c102_mt9v111.c deleted file mode 100644 index 95986eb..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_mt9v111.c +++ /dev/null @@ -1,260 +0,0 @@ -/*************************************************************************** - * Plug-in for MT9V111 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mt9v111_init(struct sn9c102_device *cam) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x1f, 0x05}, {0x20, 0x06}, - {0x1f, 0x07}, {0x81, 0x08}, - {0x5c, 0x09}, {0x00, 0x0a}, - {0x00, 0x0b}, {0x00, 0x0c}, - {0x00, 0x0d}, {0x00, 0x0e}, - {0x00, 0x0f}, {0x03, 0x10}, - {0x00, 0x11}, {0x00, 0x12}, - {0x02, 0x13}, {0x14, 0x14}, - {0x28, 0x15}, {0x1e, 0x16}, - {0xe2, 0x17}, {0x06, 0x18}, - {0x00, 0x19}, {0x00, 0x1a}, - {0x00, 0x1b}, {0x08, 0x20}, - {0x39, 0x21}, {0x51, 0x22}, - {0x63, 0x23}, {0x73, 0x24}, - {0x82, 0x25}, {0x8f, 0x26}, - {0x9b, 0x27}, {0xa7, 0x28}, - {0xb1, 0x29}, {0xbc, 0x2a}, - {0xc6, 0x2b}, {0xcf, 0x2c}, - {0xd8, 0x2d}, {0xe1, 0x2e}, - {0xea, 0x2f}, {0xf2, 0x30}, - {0x13, 0x84}, {0x00, 0x85}, - {0x25, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xee, 0x8a}, {0x0f, 0x8b}, - {0xe5, 0x8c}, {0x0f, 0x8d}, - {0x2e, 0x8e}, {0x00, 0x8f}, - {0x30, 0x90}, {0x00, 0x91}, - {0xd4, 0x92}, {0x0f, 0x93}, - {0xfc, 0x94}, {0x0f, 0x95}, - {0x14, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x60, 0x99}, - {0x07, 0x9a}, {0x40, 0x9b}, - {0x20, 0x9c}, {0x00, 0x9d}, - {0x00, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x05, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3c, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x2d, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x60, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, - 0x04, 0x80, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x04, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, - 0x00, 0x08, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x02, - 0x00, 0x16, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe7, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x87, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x40, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x09, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x07, - 0x30, 0x02, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0c, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x12, - 0x00, 0xb0, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x13, - 0x00, 0x7c, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x1e, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x04, 0, 0); - - return err; -} - -static int mt9v111_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - u8 data[2]; - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - -static int mt9v111_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, - ctrl->value ? 0x80 : 0x00, - ctrl->value ? 0x80 : 0x00, 0, - 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - -static int mt9v111_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 v_start = (u8) (rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - -static int mt9v111_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xb4, 0x17); - } else { - err += sn9c102_write_reg(cam, 0xe2, 0x17); - } - - return err; -} - - -static const struct sn9c102_sensor mt9v111 = { - .name = "MT9V111", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5c, - .init = &mt9v111_init, - .qctrl = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - }, - .get_ctrl = &mt9v111_get_ctrl, - .set_ctrl = &mt9v111_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mt9v111_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mt9v111_set_pix_format -}; - - -int sn9c102_probe_mt9v111(struct sn9c102_device *cam) -{ - u8 data[2]; - int err = 0; - - err += sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x29, 0x01}, {0x42, 0x17}, - {0x62, 0x17}, {0x08, 0x01}); - err += sn9c102_i2c_try_raw_write(cam, &mt9v111, 4, - mt9v111.i2c_slave_id, 0x01, 0x00, - 0x04, 0, 0); - if (err || sn9c102_i2c_try_raw_read(cam, &mt9v111, - mt9v111.i2c_slave_id, 0x36, 2, - data) < 0) - return -EIO; - - if (data[0] != 0x82 || data[1] != 0x3a) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mt9v111); - - return 0; -} diff --git a/drivers/staging/media/sn9c102/sn9c102_ov7630.c b/drivers/staging/media/sn9c102/sn9c102_ov7630.c deleted file mode 100644 index 9ec304d..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_ov7630.c +++ /dev/null @@ -1,634 +0,0 @@ -/*************************************************************************** - * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2006-2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int ov7630_init(struct sn9c102_device *cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, - {0x0f, 0x18}, {0x50, 0x19}); - - err += sn9c102_i2c_write(cam, 0x12, 0x8d); - err += sn9c102_i2c_write(cam, 0x12, 0x0d); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - err += sn9c102_i2c_write(cam, 0x15, 0x35); - err += sn9c102_i2c_write(cam, 0x16, 0x03); - err += sn9c102_i2c_write(cam, 0x17, 0x1c); - err += sn9c102_i2c_write(cam, 0x18, 0xbd); - err += sn9c102_i2c_write(cam, 0x19, 0x06); - err += sn9c102_i2c_write(cam, 0x1a, 0xf6); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x20, 0x44); - err += sn9c102_i2c_write(cam, 0x23, 0xee); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0x9a); - err += sn9c102_i2c_write(cam, 0x28, 0x20); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2f, 0x3d); - err += sn9c102_i2c_write(cam, 0x30, 0x24); - err += sn9c102_i2c_write(cam, 0x32, 0x86); - err += sn9c102_i2c_write(cam, 0x60, 0xa9); - err += sn9c102_i2c_write(cam, 0x61, 0x42); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x88); - err += sn9c102_i2c_write(cam, 0x70, 0x0b); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x74, 0x21); - err += sn9c102_i2c_write(cam, 0x7d, 0xf7); - break; - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x20, 0x05}, - {0x20, 0x06}, {0x20, 0x07}, - {0x03, 0x10}, {0x0a, 0x14}, - {0x60, 0x17}, {0x0f, 0x18}, - {0x50, 0x19}, {0x1d, 0x1a}, - {0x10, 0x1b}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - - err += sn9c102_i2c_write(cam, 0x12, 0x8d); - err += sn9c102_i2c_write(cam, 0x12, 0x0d); - err += sn9c102_i2c_write(cam, 0x15, 0x34); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x20, 0x44); - err += sn9c102_i2c_write(cam, 0x23, 0xee); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0x9a); - err += sn9c102_i2c_write(cam, 0x28, 0x20); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2f, 0x3d); - err += sn9c102_i2c_write(cam, 0x30, 0x24); - err += sn9c102_i2c_write(cam, 0x32, 0x86); - err += sn9c102_i2c_write(cam, 0x60, 0xa9); - err += sn9c102_i2c_write(cam, 0x61, 0x42); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x88); - err += sn9c102_i2c_write(cam, 0x70, 0x0b); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x74, 0x21); - err += sn9c102_i2c_write(cam, 0x7d, 0xf7); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x03, 0x10}, - {0x0a, 0x14}, {0xe2, 0x17}, - {0x0b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x24, 0x21}, - {0x3b, 0x22}, {0x47, 0x23}, - {0x60, 0x24}, {0x71, 0x25}, - {0x80, 0x26}, {0x8f, 0x27}, - {0x9d, 0x28}, {0xaa, 0x29}, - {0xb8, 0x2a}, {0xc4, 0x2b}, - {0xd1, 0x2c}, {0xdd, 0x2d}, - {0xe8, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3f}, - {0xc7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xc7, 0x48}, {0x01, 0x49}, - {0xc7, 0x4a}, {0x01, 0x4b}, - {0xc7, 0x4c}, {0x01, 0x4d}, - {0x44, 0x4e}, {0x00, 0x4f}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xc7, 0x54}, {0x01, 0x55}, - {0xc7, 0x56}, {0x01, 0x57}, - {0xc7, 0x58}, {0x01, 0x59}, - {0x44, 0x5a}, {0x00, 0x5b}, - {0x44, 0x5c}, {0x00, 0x5d}, - {0x44, 0x5e}, {0x00, 0x5f}, - {0xc7, 0x60}, {0x01, 0x61}, - {0xc7, 0x62}, {0x01, 0x63}, - {0xc7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6a}, {0x00, 0x6b}, - {0xc7, 0x6c}, {0x01, 0x6d}, - {0xc7, 0x6e}, {0x01, 0x6f}, - {0xc7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xc7, 0x78}, {0x01, 0x79}, - {0xc7, 0x7a}, {0x01, 0x7b}, - {0xc7, 0x7c}, {0x01, 0x7d}, - {0x44, 0x7e}, {0x00, 0x7f}, - {0x17, 0x84}, {0x00, 0x85}, - {0x2e, 0x86}, {0x00, 0x87}, - {0x09, 0x88}, {0x00, 0x89}, - {0xe8, 0x8a}, {0x0f, 0x8b}, - {0xda, 0x8c}, {0x0f, 0x8d}, - {0x40, 0x8e}, {0x00, 0x8f}, - {0x37, 0x90}, {0x00, 0x91}, - {0xcf, 0x92}, {0x0f, 0x93}, - {0xfa, 0x94}, {0x0f, 0x95}, - {0x00, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x00, 0x9a}, {0x40, 0x9b}, - {0x20, 0x9c}, {0x00, 0x9d}, - {0x00, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x00, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3c, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x32, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x60, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_write(cam, 0x12, 0x80); - err += sn9c102_i2c_write(cam, 0x12, 0x48); - err += sn9c102_i2c_write(cam, 0x01, 0x80); - err += sn9c102_i2c_write(cam, 0x02, 0x80); - err += sn9c102_i2c_write(cam, 0x03, 0x80); - err += sn9c102_i2c_write(cam, 0x04, 0x10); - err += sn9c102_i2c_write(cam, 0x05, 0x20); - err += sn9c102_i2c_write(cam, 0x06, 0x80); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - err += sn9c102_i2c_write(cam, 0x0c, 0x20); - err += sn9c102_i2c_write(cam, 0x0d, 0x20); - err += sn9c102_i2c_write(cam, 0x15, 0x80); - err += sn9c102_i2c_write(cam, 0x16, 0x03); - err += sn9c102_i2c_write(cam, 0x17, 0x1b); - err += sn9c102_i2c_write(cam, 0x18, 0xbd); - err += sn9c102_i2c_write(cam, 0x19, 0x05); - err += sn9c102_i2c_write(cam, 0x1a, 0xf6); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x21, 0x1b); - err += sn9c102_i2c_write(cam, 0x22, 0x00); - err += sn9c102_i2c_write(cam, 0x23, 0xde); - err += sn9c102_i2c_write(cam, 0x24, 0x10); - err += sn9c102_i2c_write(cam, 0x25, 0x8a); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0xca); - err += sn9c102_i2c_write(cam, 0x28, 0xa2); - err += sn9c102_i2c_write(cam, 0x29, 0x74); - err += sn9c102_i2c_write(cam, 0x2a, 0x88); - err += sn9c102_i2c_write(cam, 0x2b, 0x34); - err += sn9c102_i2c_write(cam, 0x2c, 0x88); - err += sn9c102_i2c_write(cam, 0x2e, 0x00); - err += sn9c102_i2c_write(cam, 0x2f, 0x00); - err += sn9c102_i2c_write(cam, 0x30, 0x00); - err += sn9c102_i2c_write(cam, 0x32, 0xc2); - err += sn9c102_i2c_write(cam, 0x33, 0x08); - err += sn9c102_i2c_write(cam, 0x4c, 0x40); - err += sn9c102_i2c_write(cam, 0x4d, 0xf3); - err += sn9c102_i2c_write(cam, 0x60, 0x05); - err += sn9c102_i2c_write(cam, 0x61, 0x40); - err += sn9c102_i2c_write(cam, 0x62, 0x12); - err += sn9c102_i2c_write(cam, 0x63, 0x57); - err += sn9c102_i2c_write(cam, 0x64, 0x73); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x66, 0x55); - err += sn9c102_i2c_write(cam, 0x67, 0x01); - err += sn9c102_i2c_write(cam, 0x68, 0xac); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x1f); - err += sn9c102_i2c_write(cam, 0x70, 0x01); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x72, 0x10); - err += sn9c102_i2c_write(cam, 0x73, 0x50); - err += sn9c102_i2c_write(cam, 0x74, 0x20); - err += sn9c102_i2c_write(cam, 0x76, 0x01); - err += sn9c102_i2c_write(cam, 0x77, 0xf3); - err += sn9c102_i2c_write(cam, 0x78, 0x90); - err += sn9c102_i2c_write(cam, 0x79, 0x98); - err += sn9c102_i2c_write(cam, 0x7a, 0x98); - err += sn9c102_i2c_write(cam, 0x7b, 0x00); - err += sn9c102_i2c_write(cam, 0x7c, 0x38); - err += sn9c102_i2c_write(cam, 0x7d, 0xff); - break; - default: - break; - } - - return err; -} - - -static int ov7630_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - ctrl->value = sn9c102_i2c_read(cam, 0x10); - if (ctrl->value < 0) - return -EIO; - break; - case V4L2_CID_RED_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - ctrl->value = sn9c102_pread_reg(cam, 0x05); - else - ctrl->value = sn9c102_pread_reg(cam, 0x07); - break; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = sn9c102_pread_reg(cam, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - ctrl->value = sn9c102_pread_reg(cam, 0x07); - else - ctrl->value = sn9c102_pread_reg(cam, 0x05); - break; - break; - case V4L2_CID_GAIN: - ctrl->value = sn9c102_i2c_read(cam, 0x00); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_DO_WHITE_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x0c); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_WHITENESS: - ctrl->value = sn9c102_i2c_read(cam, 0x0d); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_AUTOGAIN: - ctrl->value = sn9c102_i2c_read(cam, 0x13); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x01; - break; - case V4L2_CID_VFLIP: - ctrl->value = sn9c102_i2c_read(cam, 0x75); - if (ctrl->value < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x80) ? 1 : 0; - break; - case SN9C102_V4L2_CID_GAMMA: - ctrl->value = sn9c102_i2c_read(cam, 0x14); - if (ctrl->value < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x02) ? 1 : 0; - break; - case SN9C102_V4L2_CID_BAND_FILTER: - ctrl->value = sn9c102_i2c_read(cam, 0x2d); - if (ctrl->value < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x02) ? 1 : 0; - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7630_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case V4L2_CID_RED_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - else - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - else - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x00, ctrl->value); - break; - case V4L2_CID_DO_WHITE_BALANCE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - case V4L2_CID_WHITENESS: - err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); - break; - case V4L2_CID_AUTOGAIN: - err += sn9c102_i2c_write(cam, 0x13, ctrl->value | - (ctrl->value << 1)); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); - break; - case SN9C102_V4L2_CID_GAMMA: - err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2); - break; - case SN9C102_V4L2_CID_BAND_FILTER: - err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7630_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int ov7630_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) - err += sn9c102_write_reg(cam, 0x50, 0x19); - else - err += sn9c102_write_reg(cam, 0x20, 0x19); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xe5, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x04); - } else { - err += sn9c102_write_reg(cam, 0xe2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x02); - } - break; - default: - break; - } - - return err; -} - - -static const struct sn9c102_sensor ov7630 = { - .name = "OV7630", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103 | - BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x21, - .init = &ov7630_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x60, - .flags = 0, - }, - { - .id = V4L2_CID_WHITENESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "white balance background: red", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_DO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "white balance background: blue", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto adjust", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_BAND_FILTER, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "band filter", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "rgb gamma", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &ov7630_get_ctrl, - .set_ctrl = &ov7630_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &ov7630_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SN9C10X, - .priv = 8, - }, - .set_pix_format = &ov7630_set_pix_format -}; - - -int sn9c102_probe_ov7630(struct sn9c102_device *cam) -{ - int pid, ver, err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - break; - case BRIDGE_SN9C103: /* do _not_ change anything! */ - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x42, 0x01}, - {0x28, 0x17}, {0x44, 0x02}); - pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); - if (err || pid < 0) /* try a different initialization */ - err += sn9c102_write_const_regs(cam, {0x01, 0x01}, - {0x00, 0x01}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x29, 0x01}, {0x74, 0x02}, - {0x0e, 0x01}, {0x44, 0x01}); - break; - default: - break; - } - - pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); - ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b); - if (err || pid < 0 || ver < 0) - return -EIO; - if (pid != 0x76 || ver != 0x31) - return -ENODEV; - sn9c102_attach_sensor(cam, &ov7630); - - return 0; -} diff --git a/drivers/staging/media/sn9c102/sn9c102_ov7660.c b/drivers/staging/media/sn9c102/sn9c102_ov7660.c deleted file mode 100644 index ac07805..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_ov7660.c +++ /dev/null @@ -1,546 +0,0 @@ -/*************************************************************************** - * Plug-in for OV7660 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int ov7660_init(struct sn9c102_device *cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x03, 0x10}, - {0x08, 0x14}, {0x20, 0x17}, - {0x8b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3f}, - {0xc7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xc7, 0x48}, {0x01, 0x49}, - {0xc7, 0x4a}, {0x01, 0x4b}, - {0xc7, 0x4c}, {0x01, 0x4d}, - {0x44, 0x4e}, {0x00, 0x4f}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xc7, 0x54}, {0x01, 0x55}, - {0xc7, 0x56}, {0x01, 0x57}, - {0xc7, 0x58}, {0x01, 0x59}, - {0x44, 0x5a}, {0x00, 0x5b}, - {0x44, 0x5c}, {0x00, 0x5d}, - {0x44, 0x5e}, {0x00, 0x5f}, - {0xc7, 0x60}, {0x01, 0x61}, - {0xc7, 0x62}, {0x01, 0x63}, - {0xc7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6a}, {0x00, 0x6b}, - {0xc7, 0x6c}, {0x01, 0x6d}, - {0xc7, 0x6e}, {0x01, 0x6f}, - {0xc7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xc7, 0x78}, {0x01, 0x79}, - {0xc7, 0x7a}, {0x01, 0x7b}, - {0xc7, 0x7c}, {0x01, 0x7d}, - {0x44, 0x7e}, {0x00, 0x7f}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xec, 0x8a}, {0x0f, 0x8b}, - {0xd8, 0x8c}, {0x0f, 0x8d}, - {0x3d, 0x8e}, {0x00, 0x8f}, - {0x3d, 0x90}, {0x00, 0x91}, - {0xcd, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0c, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9a}, {0x00, 0x9b}, - {0x04, 0x9c}, {0x00, 0x9d}, - {0x08, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x05, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3C, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x32, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x00, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_write(cam, 0x12, 0x80); - err += sn9c102_i2c_write(cam, 0x11, 0x09); - err += sn9c102_i2c_write(cam, 0x00, 0x0A); - err += sn9c102_i2c_write(cam, 0x01, 0x80); - err += sn9c102_i2c_write(cam, 0x02, 0x80); - err += sn9c102_i2c_write(cam, 0x03, 0x00); - err += sn9c102_i2c_write(cam, 0x04, 0x00); - err += sn9c102_i2c_write(cam, 0x05, 0x08); - err += sn9c102_i2c_write(cam, 0x06, 0x0B); - err += sn9c102_i2c_write(cam, 0x07, 0x00); - err += sn9c102_i2c_write(cam, 0x08, 0x1C); - err += sn9c102_i2c_write(cam, 0x09, 0x01); - err += sn9c102_i2c_write(cam, 0x0A, 0x76); - err += sn9c102_i2c_write(cam, 0x0B, 0x60); - err += sn9c102_i2c_write(cam, 0x0C, 0x00); - err += sn9c102_i2c_write(cam, 0x0D, 0x08); - err += sn9c102_i2c_write(cam, 0x0E, 0x04); - err += sn9c102_i2c_write(cam, 0x0F, 0x6F); - err += sn9c102_i2c_write(cam, 0x10, 0x20); - err += sn9c102_i2c_write(cam, 0x11, 0x03); - err += sn9c102_i2c_write(cam, 0x12, 0x05); - err += sn9c102_i2c_write(cam, 0x13, 0xC7); - err += sn9c102_i2c_write(cam, 0x14, 0x2C); - err += sn9c102_i2c_write(cam, 0x15, 0x00); - err += sn9c102_i2c_write(cam, 0x16, 0x02); - err += sn9c102_i2c_write(cam, 0x17, 0x10); - err += sn9c102_i2c_write(cam, 0x18, 0x60); - err += sn9c102_i2c_write(cam, 0x19, 0x02); - err += sn9c102_i2c_write(cam, 0x1A, 0x7B); - err += sn9c102_i2c_write(cam, 0x1B, 0x02); - err += sn9c102_i2c_write(cam, 0x1C, 0x7F); - err += sn9c102_i2c_write(cam, 0x1D, 0xA2); - err += sn9c102_i2c_write(cam, 0x1E, 0x01); - err += sn9c102_i2c_write(cam, 0x1F, 0x0E); - err += sn9c102_i2c_write(cam, 0x20, 0x05); - err += sn9c102_i2c_write(cam, 0x21, 0x05); - err += sn9c102_i2c_write(cam, 0x22, 0x05); - err += sn9c102_i2c_write(cam, 0x23, 0x05); - err += sn9c102_i2c_write(cam, 0x24, 0x68); - err += sn9c102_i2c_write(cam, 0x25, 0x58); - err += sn9c102_i2c_write(cam, 0x26, 0xD4); - err += sn9c102_i2c_write(cam, 0x27, 0x80); - err += sn9c102_i2c_write(cam, 0x28, 0x80); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2A, 0x00); - err += sn9c102_i2c_write(cam, 0x2B, 0x00); - err += sn9c102_i2c_write(cam, 0x2C, 0x80); - err += sn9c102_i2c_write(cam, 0x2D, 0x00); - err += sn9c102_i2c_write(cam, 0x2E, 0x00); - err += sn9c102_i2c_write(cam, 0x2F, 0x0E); - err += sn9c102_i2c_write(cam, 0x30, 0x08); - err += sn9c102_i2c_write(cam, 0x31, 0x30); - err += sn9c102_i2c_write(cam, 0x32, 0xB4); - err += sn9c102_i2c_write(cam, 0x33, 0x00); - err += sn9c102_i2c_write(cam, 0x34, 0x07); - err += sn9c102_i2c_write(cam, 0x35, 0x84); - err += sn9c102_i2c_write(cam, 0x36, 0x00); - err += sn9c102_i2c_write(cam, 0x37, 0x0C); - err += sn9c102_i2c_write(cam, 0x38, 0x02); - err += sn9c102_i2c_write(cam, 0x39, 0x43); - err += sn9c102_i2c_write(cam, 0x3A, 0x00); - err += sn9c102_i2c_write(cam, 0x3B, 0x0A); - err += sn9c102_i2c_write(cam, 0x3C, 0x6C); - err += sn9c102_i2c_write(cam, 0x3D, 0x99); - err += sn9c102_i2c_write(cam, 0x3E, 0x0E); - err += sn9c102_i2c_write(cam, 0x3F, 0x41); - err += sn9c102_i2c_write(cam, 0x40, 0xC1); - err += sn9c102_i2c_write(cam, 0x41, 0x22); - err += sn9c102_i2c_write(cam, 0x42, 0x08); - err += sn9c102_i2c_write(cam, 0x43, 0xF0); - err += sn9c102_i2c_write(cam, 0x44, 0x10); - err += sn9c102_i2c_write(cam, 0x45, 0x78); - err += sn9c102_i2c_write(cam, 0x46, 0xA8); - err += sn9c102_i2c_write(cam, 0x47, 0x60); - err += sn9c102_i2c_write(cam, 0x48, 0x80); - err += sn9c102_i2c_write(cam, 0x49, 0x00); - err += sn9c102_i2c_write(cam, 0x4A, 0x00); - err += sn9c102_i2c_write(cam, 0x4B, 0x00); - err += sn9c102_i2c_write(cam, 0x4C, 0x00); - err += sn9c102_i2c_write(cam, 0x4D, 0x00); - err += sn9c102_i2c_write(cam, 0x4E, 0x00); - err += sn9c102_i2c_write(cam, 0x4F, 0x46); - err += sn9c102_i2c_write(cam, 0x50, 0x36); - err += sn9c102_i2c_write(cam, 0x51, 0x0F); - err += sn9c102_i2c_write(cam, 0x52, 0x17); - err += sn9c102_i2c_write(cam, 0x53, 0x7F); - err += sn9c102_i2c_write(cam, 0x54, 0x96); - err += sn9c102_i2c_write(cam, 0x55, 0x40); - err += sn9c102_i2c_write(cam, 0x56, 0x40); - err += sn9c102_i2c_write(cam, 0x57, 0x40); - err += sn9c102_i2c_write(cam, 0x58, 0x0F); - err += sn9c102_i2c_write(cam, 0x59, 0xBA); - err += sn9c102_i2c_write(cam, 0x5A, 0x9A); - err += sn9c102_i2c_write(cam, 0x5B, 0x22); - err += sn9c102_i2c_write(cam, 0x5C, 0xB9); - err += sn9c102_i2c_write(cam, 0x5D, 0x9B); - err += sn9c102_i2c_write(cam, 0x5E, 0x10); - err += sn9c102_i2c_write(cam, 0x5F, 0xF0); - err += sn9c102_i2c_write(cam, 0x60, 0x05); - err += sn9c102_i2c_write(cam, 0x61, 0x60); - err += sn9c102_i2c_write(cam, 0x62, 0x00); - err += sn9c102_i2c_write(cam, 0x63, 0x00); - err += sn9c102_i2c_write(cam, 0x64, 0x50); - err += sn9c102_i2c_write(cam, 0x65, 0x30); - err += sn9c102_i2c_write(cam, 0x66, 0x00); - err += sn9c102_i2c_write(cam, 0x67, 0x80); - err += sn9c102_i2c_write(cam, 0x68, 0x7A); - err += sn9c102_i2c_write(cam, 0x69, 0x90); - err += sn9c102_i2c_write(cam, 0x6A, 0x80); - err += sn9c102_i2c_write(cam, 0x6B, 0x0A); - err += sn9c102_i2c_write(cam, 0x6C, 0x30); - err += sn9c102_i2c_write(cam, 0x6D, 0x48); - err += sn9c102_i2c_write(cam, 0x6E, 0x80); - err += sn9c102_i2c_write(cam, 0x6F, 0x74); - err += sn9c102_i2c_write(cam, 0x70, 0x64); - err += sn9c102_i2c_write(cam, 0x71, 0x60); - err += sn9c102_i2c_write(cam, 0x72, 0x5C); - err += sn9c102_i2c_write(cam, 0x73, 0x58); - err += sn9c102_i2c_write(cam, 0x74, 0x54); - err += sn9c102_i2c_write(cam, 0x75, 0x4C); - err += sn9c102_i2c_write(cam, 0x76, 0x40); - err += sn9c102_i2c_write(cam, 0x77, 0x38); - err += sn9c102_i2c_write(cam, 0x78, 0x34); - err += sn9c102_i2c_write(cam, 0x79, 0x30); - err += sn9c102_i2c_write(cam, 0x7A, 0x2F); - err += sn9c102_i2c_write(cam, 0x7B, 0x2B); - err += sn9c102_i2c_write(cam, 0x7C, 0x03); - err += sn9c102_i2c_write(cam, 0x7D, 0x07); - err += sn9c102_i2c_write(cam, 0x7E, 0x17); - err += sn9c102_i2c_write(cam, 0x7F, 0x34); - err += sn9c102_i2c_write(cam, 0x80, 0x41); - err += sn9c102_i2c_write(cam, 0x81, 0x4D); - err += sn9c102_i2c_write(cam, 0x82, 0x58); - err += sn9c102_i2c_write(cam, 0x83, 0x63); - err += sn9c102_i2c_write(cam, 0x84, 0x6E); - err += sn9c102_i2c_write(cam, 0x85, 0x77); - err += sn9c102_i2c_write(cam, 0x86, 0x87); - err += sn9c102_i2c_write(cam, 0x87, 0x95); - err += sn9c102_i2c_write(cam, 0x88, 0xAF); - err += sn9c102_i2c_write(cam, 0x89, 0xC7); - err += sn9c102_i2c_write(cam, 0x8A, 0xDF); - err += sn9c102_i2c_write(cam, 0x8B, 0x99); - err += sn9c102_i2c_write(cam, 0x8C, 0x99); - err += sn9c102_i2c_write(cam, 0x8D, 0xCF); - err += sn9c102_i2c_write(cam, 0x8E, 0x20); - err += sn9c102_i2c_write(cam, 0x8F, 0x26); - err += sn9c102_i2c_write(cam, 0x90, 0x10); - err += sn9c102_i2c_write(cam, 0x91, 0x0C); - err += sn9c102_i2c_write(cam, 0x92, 0x25); - err += sn9c102_i2c_write(cam, 0x93, 0x00); - err += sn9c102_i2c_write(cam, 0x94, 0x50); - err += sn9c102_i2c_write(cam, 0x95, 0x50); - err += sn9c102_i2c_write(cam, 0x96, 0x00); - err += sn9c102_i2c_write(cam, 0x97, 0x01); - err += sn9c102_i2c_write(cam, 0x98, 0x10); - err += sn9c102_i2c_write(cam, 0x99, 0x40); - err += sn9c102_i2c_write(cam, 0x9A, 0x40); - err += sn9c102_i2c_write(cam, 0x9B, 0x20); - err += sn9c102_i2c_write(cam, 0x9C, 0x00); - err += sn9c102_i2c_write(cam, 0x9D, 0x99); - err += sn9c102_i2c_write(cam, 0x9E, 0x7F); - err += sn9c102_i2c_write(cam, 0x9F, 0x00); - err += sn9c102_i2c_write(cam, 0xA0, 0x00); - err += sn9c102_i2c_write(cam, 0xA1, 0x00); - - return err; -} - - -static int ov7660_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - ctrl->value = sn9c102_i2c_read(cam, 0x10); - if (ctrl->value < 0) - return -EIO; - break; - case V4L2_CID_DO_WHITE_BALANCE: - ctrl->value = sn9c102_read_reg(cam, 0x02); - if (ctrl->value < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x04) ? 1 : 0; - break; - case V4L2_CID_RED_BALANCE: - ctrl->value = sn9c102_read_reg(cam, 0x05); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = sn9c102_read_reg(cam, 0x06); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = sn9c102_read_reg(cam, 0x07); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case SN9C102_V4L2_CID_BAND_FILTER: - ctrl->value = sn9c102_i2c_read(cam, 0x3b); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x08; - break; - case V4L2_CID_GAIN: - ctrl->value = sn9c102_i2c_read(cam, 0x00); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x1f; - break; - case V4L2_CID_AUTOGAIN: - ctrl->value = sn9c102_i2c_read(cam, 0x13); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x01; - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7660_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case V4L2_CID_DO_WHITE_BALANCE: - err += sn9c102_write_reg(cam, 0x43 | (ctrl->value << 2), 0x02); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - break; - case SN9C102_V4L2_CID_BAND_FILTER: - err += sn9c102_i2c_write(cam, ctrl->value << 3, 0x3b); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x00, 0x60 + ctrl->value); - break; - case V4L2_CID_AUTOGAIN: - err += sn9c102_i2c_write(cam, 0x13, 0xc0 | - (ctrl->value * 0x07)); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7660_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int ov7660_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int r0, err = 0; - - r0 = sn9c102_pread_reg(cam, 0x01); - - if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { - err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); - err += sn9c102_write_reg(cam, 0xa2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - } else { - err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); - err += sn9c102_write_reg(cam, 0xa2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x0d); - } - - return err; -} - - -static const struct sn9c102_sensor ov7660 = { - .name = "OV7660", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x21, - .init = &ov7660_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x09, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x27, - .flags = 0, - }, - { - .id = V4L2_CID_DO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "night mode", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto adjust", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_BAND_FILTER, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "band filter", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &ov7660_get_ctrl, - .set_ctrl = &ov7660_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &ov7660_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_JPEG, - .priv = 8, - }, - .set_pix_format = &ov7660_set_pix_format -}; - - -int sn9c102_probe_ov7660(struct sn9c102_device *cam) -{ - int pid, ver, err; - - err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - - pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a); - ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b); - if (err || pid < 0 || ver < 0) - return -EIO; - if (pid != 0x76 || ver != 0x60) - return -ENODEV; - - sn9c102_attach_sensor(cam, &ov7660); - - return 0; -} diff --git a/drivers/staging/media/sn9c102/sn9c102_pas106b.c b/drivers/staging/media/sn9c102/sn9c102_pas106b.c deleted file mode 100644 index 895931e..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_pas106b.c +++ /dev/null @@ -1,308 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS106B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int pas106b_init(struct sn9c102_device *cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x20, 0x19}, {0x09, 0x18}); - - err += sn9c102_i2c_write(cam, 0x02, 0x0c); - err += sn9c102_i2c_write(cam, 0x05, 0x5a); - err += sn9c102_i2c_write(cam, 0x06, 0x88); - err += sn9c102_i2c_write(cam, 0x07, 0x80); - err += sn9c102_i2c_write(cam, 0x10, 0x06); - err += sn9c102_i2c_write(cam, 0x11, 0x06); - err += sn9c102_i2c_write(cam, 0x12, 0x00); - err += sn9c102_i2c_write(cam, 0x14, 0x02); - err += sn9c102_i2c_write(cam, 0x13, 0x01); - - msleep(400); - - return err; -} - - -static int pas106b_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x03), - r2 = sn9c102_i2c_read(cam, 0x04); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 4) | (r2 & 0x0f); - } - return 0; - case V4L2_CID_RED_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x0c); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x09); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_GAIN: - ctrl->value = sn9c102_i2c_read(cam, 0x0e); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_CONTRAST: - ctrl->value = sn9c102_i2c_read(cam, 0x0f); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x07; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x0a); - if (ctrl->value < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x1f) << 1; - return 0; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - ctrl->value = sn9c102_i2c_read(cam, 0x08); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0xf8; - return 0; - default: - return -EINVAL; - } -} - - -static int pas106b_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4); - err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x0e, ctrl->value); - break; - case V4L2_CID_CONTRAST: - err += sn9c102_i2c_write(cam, 0x0f, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1); - err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1); - break; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3); - break; - default: - return -EINVAL; - } - err += sn9c102_i2c_write(cam, 0x13, 0x01); - - return err ? -EIO : 0; -} - - -static int pas106b_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int pas106b_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x2c, 0x17); - else - err += sn9c102_write_reg(cam, 0x20, 0x17); - - return err; -} - - -static const struct sn9c102_sensor pas106b = { - .name = "PAS106B", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x40, - .init = &pas106b_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x125, - .maximum = 0xfff, - .step = 0x001, - .default_value = 0x140, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0d, - .flags = 0, - }, - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "contrast", - .minimum = 0x00, - .maximum = 0x07, - .step = 0x01, - .default_value = 0x00, /* 0x00~0x03 have same effect */ - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x04, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x06, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3e, - .step = 0x02, - .default_value = 0x02, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - }, - .get_ctrl = &pas106b_get_ctrl, - .set_ctrl = &pas106b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &pas106b_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, /* we use this field as 'bits per pixel' */ - }, - .set_pix_format = &pas106b_set_pix_format -}; - - -int sn9c102_probe_pas106b(struct sn9c102_device *cam) -{ - int r0 = 0, r1 = 0; - unsigned int pid = 0; - - /* - Minimal initialization to enable the I2C communication - NOTE: do NOT change the values! - */ - if (sn9c102_write_const_regs(cam, - {0x01, 0x01}, /* sensor power down */ - {0x00, 0x01}, /* sensor power on */ - {0x28, 0x17})) /* sensor clock at 24 MHz */ - return -EIO; - - r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00); - r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01); - if (r0 < 0 || r1 < 0) - return -EIO; - - pid = (r0 << 11) | ((r1 & 0xf0) >> 4); - if (pid != 0x007) - return -ENODEV; - - sn9c102_attach_sensor(cam, &pas106b); - - return 0; -} diff --git a/drivers/staging/media/sn9c102/sn9c102_pas202bcb.c b/drivers/staging/media/sn9c102/sn9c102_pas202bcb.c deleted file mode 100644 index f9e31ae..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_pas202bcb.c +++ /dev/null @@ -1,340 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * - * * - * * - * Support for SN9C103, DAC Magnitude, exposure and green gain controls * - * added by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int pas202bcb_init(struct sn9c102_device *cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x30, 0x19}, {0x09, 0x18}); - break; - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x20, 0x05}, - {0x20, 0x06}, {0x20, 0x07}, - {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x30, 0x19}, {0x09, 0x18}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x10, 0x21}, - {0x20, 0x22}, {0x30, 0x23}, - {0x40, 0x24}, {0x50, 0x25}, - {0x60, 0x26}, {0x70, 0x27}, - {0x80, 0x28}, {0x90, 0x29}, - {0xa0, 0x2a}, {0xb0, 0x2b}, - {0xc0, 0x2c}, {0xd0, 0x2d}, - {0xe0, 0x2e}, {0xf0, 0x2f}, - {0xff, 0x30}); - break; - default: - break; - } - - err += sn9c102_i2c_write(cam, 0x02, 0x14); - err += sn9c102_i2c_write(cam, 0x03, 0x40); - err += sn9c102_i2c_write(cam, 0x0d, 0x2c); - err += sn9c102_i2c_write(cam, 0x0e, 0x01); - err += sn9c102_i2c_write(cam, 0x0f, 0xa9); - err += sn9c102_i2c_write(cam, 0x10, 0x08); - err += sn9c102_i2c_write(cam, 0x13, 0x63); - err += sn9c102_i2c_write(cam, 0x15, 0x70); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - msleep(400); - - return err; -} - - -static int pas202bcb_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x04), - r2 = sn9c102_i2c_read(cam, 0x05); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 6) | (r2 & 0x3f); - } - return 0; - case V4L2_CID_RED_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x09); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x07); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_GAIN: - ctrl->value = sn9c102_i2c_read(cam, 0x10); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = sn9c102_i2c_read(cam, 0x08); - if (ctrl->value < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - ctrl->value = sn9c102_i2c_read(cam, 0x0c); - if (ctrl->value < 0) - return -EIO; - return 0; - default: - return -EINVAL; - } -} - - -static int pas202bcb_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x28, 0x17); - else - err += sn9c102_write_reg(cam, 0x20, 0x17); - - return err; -} - - -static int pas202bcb_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); - err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x07, ctrl->value); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x08, ctrl->value); - break; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - default: - return -EINVAL; - } - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - return err ? -EIO : 0; -} - - -static int pas202bcb_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; - break; - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static const struct sn9c102_sensor pas202bcb = { - .name = "PAS202BCB", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x40, - .init = &pas202bcb_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x01e5, - .maximum = 0x3fff, - .step = 0x0001, - .default_value = 0x01e5, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0b, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x04, - .flags = 0, - }, - }, - .get_ctrl = &pas202bcb_get_ctrl, - .set_ctrl = &pas202bcb_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &pas202bcb_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &pas202bcb_set_pix_format -}; - - -int sn9c102_probe_pas202bcb(struct sn9c102_device *cam) -{ - int r0 = 0, r1 = 0, err = 0; - unsigned int pid = 0; - - /* - * Minimal initialization to enable the I2C communication - * NOTE: do NOT change the values! - */ - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, - {0x01, 0x01}, /* power down */ - {0x40, 0x01}, /* power on */ - {0x28, 0x17});/* clock 24 MHz */ - break; - case BRIDGE_SN9C103: /* do _not_ change anything! */ - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x01}, - {0x44, 0x02}, {0x29, 0x17}); - break; - default: - break; - } - - r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00); - r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01); - - if (err || r0 < 0 || r1 < 0) - return -EIO; - - pid = (r0 << 4) | ((r1 & 0xf0) >> 4); - if (pid != 0x017) - return -ENODEV; - - sn9c102_attach_sensor(cam, &pas202bcb); - - return 0; -} diff --git a/drivers/staging/media/sn9c102/sn9c102_sensor.h b/drivers/staging/media/sn9c102/sn9c102_sensor.h deleted file mode 100644 index 9f59c81..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_sensor.h +++ /dev/null @@ -1,307 +0,0 @@ -/*************************************************************************** - * API for image sensors connected to the SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#ifndef _SN9C102_SENSOR_H_ -#define _SN9C102_SENSOR_H_ - -#include -#include -#include -#include -#include -#include - -struct sn9c102_device; -struct sn9c102_sensor; - -/*****************************************************************************/ - -/* - OVERVIEW. - This is a small interface that allows you to add support for any CCD/CMOS - image sensors connected to the SN9C1XX bridges. The entire API is documented - below. In the most general case, to support a sensor there are three steps - you have to follow: - 1) define the main "sn9c102_sensor" structure by setting the basic fields; - 2) write a probing function to be called by the core module when the USB - camera is recognized, then add both the USB ids and the name of that - function to the two corresponding tables in sn9c102_devtable.h; - 3) implement the methods that you want/need (and fill the rest of the main - structure accordingly). - "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do - NOT need to touch the source code of the core module for the things to work - properly, unless you find bugs or flaws in it. Finally, do not forget to - read the V4L2 API for completeness. -*/ - -/*****************************************************************************/ - -enum sn9c102_bridge { - BRIDGE_SN9C101 = 0x01, - BRIDGE_SN9C102 = 0x02, - BRIDGE_SN9C103 = 0x04, - BRIDGE_SN9C105 = 0x08, - BRIDGE_SN9C120 = 0x10, -}; - -/* Return the bridge name */ -enum sn9c102_bridge sn9c102_get_bridge(struct sn9c102_device *cam); - -/* Return a pointer the sensor struct attached to the camera */ -struct sn9c102_sensor *sn9c102_get_sensor(struct sn9c102_device *cam); - -/* Identify a device */ -extern struct sn9c102_device* -sn9c102_match_id(struct sn9c102_device *cam, const struct usb_device_id *id); - -/* Attach a probed sensor to the camera. */ -extern void -sn9c102_attach_sensor(struct sn9c102_device *cam, - const struct sn9c102_sensor *sensor); - -/* - Read/write routines: they always return -1 on error, 0 or the read value - otherwise. NOTE that a real read operation is not supported by the SN9C1XX - chip for some of its registers. To work around this problem, a pseudo-read - call is provided instead: it returns the last successfully written value - on the register (0 if it has never been written), the usual -1 on error. -*/ - -/* The "try" I2C I/O versions are used when probing the sensor */ -extern int sn9c102_i2c_try_read(struct sn9c102_device*, - const struct sn9c102_sensor*, u8 address); - -/* - These must be used if and only if the sensor doesn't implement the standard - I2C protocol. There are a number of good reasons why you must use the - single-byte versions of these functions: do not abuse. The first function - writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C1XX - chip. The second one programs the registers 0x09 and 0x10 with data0 and - data1, and places the n bytes read from the sensor register table in the - buffer pointed by 'buffer'. Both the functions return -1 on error; the write - version returns 0 on success, while the read version returns the first read - byte. -*/ -extern int sn9c102_i2c_try_raw_write(struct sn9c102_device *cam, - const struct sn9c102_sensor *sensor, u8 n, - u8 data0, u8 data1, u8 data2, u8 data3, - u8 data4, u8 data5); -extern int sn9c102_i2c_try_raw_read(struct sn9c102_device *cam, - const struct sn9c102_sensor *sensor, - u8 data0, u8 data1, u8 n, u8 buffer[]); - -/* To be used after the sensor struct has been attached to the camera struct */ -extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value); -extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); - -/* I/O on registers in the bridge. Could be used by the sensor methods too */ -extern int sn9c102_read_reg(struct sn9c102_device*, u16 index); -extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); -extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); -extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2], - int count); -/* - Write multiple registers with constant values. For example: - sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, {0x0f, 0x18}); - Register addresses must be < 256. -*/ -#define sn9c102_write_const_regs(sn9c102_device, data...) \ - ({ static const u8 _valreg[][2] = {data}; \ - sn9c102_write_regs(sn9c102_device, _valreg, ARRAY_SIZE(_valreg)); }) - -/*****************************************************************************/ - -enum sn9c102_i2c_sysfs_ops { - SN9C102_I2C_READ = 0x01, - SN9C102_I2C_WRITE = 0x02, -}; - -enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */ - SN9C102_I2C_100KHZ = 0x01, - SN9C102_I2C_400KHZ = 0x02, -}; - -enum sn9c102_i2c_interface { - SN9C102_I2C_2WIRES, - SN9C102_I2C_3WIRES, -}; - -#define SN9C102_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) - -struct sn9c102_sensor { - char name[32], /* sensor name */ - maintainer[64]; /* name of the maintainer */ - - enum sn9c102_bridge supported_bridge; /* supported SN9C1xx bridges */ - - /* Supported operations through the 'sysfs' interface */ - enum sn9c102_i2c_sysfs_ops sysfs_ops; - - /* - These sensor capabilities must be provided if the SN9C1XX controller - needs to communicate through the sensor serial interface by using - at least one of the i2c functions available. - */ - enum sn9c102_i2c_frequency frequency; - enum sn9c102_i2c_interface interface; - - /* - This identifier must be provided if the image sensor implements - the standard I2C protocol. - */ - u8 i2c_slave_id; /* reg. 0x09 */ - - /* - NOTE: Where not noted,most of the functions below are not mandatory. - Set to null if you do not implement them. If implemented, - they must return 0 on success, the proper error otherwise. - */ - - int (*init)(struct sn9c102_device *cam); - /* - This function will be called after the sensor has been attached. - It should be used to initialize the sensor only, but may also - configure part of the SN9C1XX chip if necessary. You don't need to - setup picture settings like brightness, contrast, etc.. here, if - the corresponding controls are implemented (see below), since - they are adjusted in the core driver by calling the set_ctrl() - method after init(), where the arguments are the default values - specified in the v4l2_queryctrl list of supported controls; - Same suggestions apply for other settings, _if_ the corresponding - methods are present; if not, the initialization must configure the - sensor according to the default configuration structures below. - */ - - struct v4l2_queryctrl qctrl[SN9C102_MAX_CTRLS]; - /* - Optional list of default controls, defined as indicated in the - V4L2 API. Menu type controls are not handled by this interface. - */ - - int (*get_ctrl)(struct sn9c102_device *cam, struct v4l2_control *ctrl); - int (*set_ctrl)(struct sn9c102_device *cam, - const struct v4l2_control *ctrl); - /* - You must implement at least the set_ctrl method if you have defined - the list above. The returned value must follow the V4L2 - specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER - are not supported by this driver, so do not implement them. Also, - you don't have to check whether the passed values are out of bounds, - given that this is done by the core module. - */ - - struct v4l2_cropcap cropcap; - /* - Think the image sensor as a grid of R,G,B monochromatic pixels - disposed according to a particular Bayer pattern, which describes - the complete array of pixels, from (0,0) to (xmax, ymax). We will - use this coordinate system from now on. It is assumed the sensor - chip can be programmed to capture/transmit a subsection of that - array of pixels: we will call this subsection "active window". - It is not always true that the largest achievable active window can - cover the whole array of pixels. The V4L2 API defines another - area called "source rectangle", which, in turn, is a subrectangle of - the active window. The SN9C1XX chip is always programmed to read the - source rectangle. - The bounds of both the active window and the source rectangle are - specified in the cropcap substructures 'bounds' and 'defrect'. - By default, the source rectangle should cover the largest possible - area. Again, it is not always true that the largest source rectangle - can cover the entire active window, although it is a rare case for - the hardware we have. The bounds of the source rectangle _must_ be - multiple of 16 and must use the same coordinate system as indicated - before; their centers shall align initially. - If necessary, the sensor chip must be initialized during init() to - set the bounds of the active sensor window; however, by default, it - usually covers the largest achievable area (maxwidth x maxheight) - of pixels, so no particular initialization is needed, if you have - defined the correct default bounds in the structures. - See the V4L2 API for further details. - NOTE: once you have defined the bounds of the active window - (struct cropcap.bounds) you must not change them.anymore. - Only 'bounds' and 'defrect' fields are mandatory, other fields - will be ignored. - */ - - int (*set_crop)(struct sn9c102_device *cam, - const struct v4l2_rect *rect); - /* - To be called on VIDIOC_C_SETCROP. The core module always calls a - default routine which configures the appropriate SN9C1XX regs (also - scaling), but you may need to override/adjust specific stuff. - 'rect' contains width and height values that are multiple of 16: in - case you override the default function, you always have to program - the chip to match those values; on error return the corresponding - error code without rolling back. - NOTE: in case, you must program the SN9C1XX chip to get rid of - blank pixels or blank lines at the _start_ of each line or - frame after each HSYNC or VSYNC, so that the image starts with - real RGB data (see regs 0x12, 0x13) (having set H_SIZE and, - V_SIZE you don't have to care about blank pixels or blank - lines at the end of each line or frame). - */ - - struct v4l2_pix_format pix_format; - /* - What you have to define here are: 1) initial 'width' and 'height' of - the target rectangle 2) the initial 'pixelformat', which can be - either V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_JPEG (for ompressed video) - or V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate - the number of bits per pixel for uncompressed video, 8 or 9 (despite - the current value of 'pixelformat'). - NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 - of cropcap.defrect.width and cropcap.defrect.height. I - suggest 1/1. - NOTE 2: The initial compression quality is defined by the first bit - of reg 0x17 during the initialization of the image sensor. - NOTE 3: as said above, you have to program the SN9C1XX chip to get - rid of any blank pixels, so that the output of the sensor - matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). - */ - - int (*set_pix_format)(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix); - /* - To be called on VIDIOC_S_FMT, when switching from the SBGGR8 to - SN9C10X pixel format or viceversa. On error return the corresponding - error code without rolling back. - */ - - /* - Do NOT write to the data below, it's READ ONLY. It is used by the - core module to store successfully updated values of the above - settings, for rollbacks..etc..in case of errors during atomic I/O - */ - struct v4l2_queryctrl _qctrl[SN9C102_MAX_CTRLS]; - struct v4l2_rect _rect; -}; - -/*****************************************************************************/ - -/* Private ioctl's for control settings supported by some image sensors */ -#define SN9C102_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) -#define SN9C102_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) -#define SN9C102_V4L2_CID_RESET_LEVEL (V4L2_CID_PRIVATE_BASE + 2) -#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE (V4L2_CID_PRIVATE_BASE + 3) -#define SN9C102_V4L2_CID_GAMMA (V4L2_CID_PRIVATE_BASE + 4) -#define SN9C102_V4L2_CID_BAND_FILTER (V4L2_CID_PRIVATE_BASE + 5) -#define SN9C102_V4L2_CID_BRIGHT_LEVEL (V4L2_CID_PRIVATE_BASE + 6) - -#endif /* _SN9C102_SENSOR_H_ */ diff --git a/drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c b/drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c deleted file mode 100644 index 6a00b62..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c +++ /dev/null @@ -1,154 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5110C1B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5110c1b_init(struct sn9c102_device *cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x44, 0x01}, - {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x60, 0x17}, - {0x06, 0x18}, {0xfb, 0x19}); - - err += sn9c102_i2c_write(cam, 0xc0, 0x80); - - return err; -} - - -static int tas5110c1b_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int tas5110c1b_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - /* Don't change ! */ - err += sn9c102_write_reg(cam, 0x14, 0x1a); - err += sn9c102_write_reg(cam, 0x0a, 0x1b); - err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); - - return err; -} - - -static int tas5110c1b_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x2b, 0x19); - else - err += sn9c102_write_reg(cam, 0xfb, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5110c1b = { - .name = "TAS5110C1B", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_3WIRES, - .init = &tas5110c1b_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xf6, - .step = 0x01, - .default_value = 0x40, - .flags = 0, - }, - }, - .set_ctrl = &tas5110c1b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &tas5110c1b_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5110c1b_set_pix_format -}; - - -int sn9c102_probe_tas5110c1b(struct sn9c102_device *cam) -{ - const struct usb_device_id tas5110c1b_id_table[] = { - { USB_DEVICE(0x0c45, 0x6001), }, - { USB_DEVICE(0x0c45, 0x6005), }, - { USB_DEVICE(0x0c45, 0x60ab), }, - { } - }; - - /* Sensor detection is based on USB pid/vid */ - if (!sn9c102_match_id(cam, tas5110c1b_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5110c1b); - - return 0; -} diff --git a/drivers/staging/media/sn9c102/sn9c102_tas5110d.c b/drivers/staging/media/sn9c102/sn9c102_tas5110d.c deleted file mode 100644 index eefbf86..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_tas5110d.c +++ /dev/null @@ -1,119 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5110d_init(struct sn9c102_device *cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x04, 0x01}, - {0x0a, 0x14}, {0x60, 0x17}, - {0x06, 0x18}, {0xfb, 0x19}); - - err += sn9c102_i2c_write(cam, 0x9a, 0xca); - - return err; -} - - -static int tas5110d_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - err += sn9c102_write_reg(cam, 0x14, 0x1a); - err += sn9c102_write_reg(cam, 0x0a, 0x1b); - - return err; -} - - -static int tas5110d_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x3b, 0x19); - else - err += sn9c102_write_reg(cam, 0xfb, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5110d = { - .name = "TAS5110D", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x61, - .init = &tas5110d_init, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &tas5110d_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5110d_set_pix_format -}; - - -int sn9c102_probe_tas5110d(struct sn9c102_device *cam) -{ - const struct usb_device_id tas5110d_id_table[] = { - { USB_DEVICE(0x0c45, 0x6007), }, - { } - }; - - if (!sn9c102_match_id(cam, tas5110d_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5110d); - - return 0; -} diff --git a/drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c b/drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c deleted file mode 100644 index 725de85..0000000 --- a/drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c +++ /dev/null @@ -1,165 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5130D1B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5130d1b_init(struct sn9c102_device *cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x20, 0x17}, - {0x04, 0x01}, {0x01, 0x10}, - {0x00, 0x11}, {0x00, 0x14}, - {0x60, 0x17}, {0x07, 0x18}); - - return err; -} - - -static int tas5130d1b_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); - break; - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int tas5130d1b_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; - int err = 0; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - /* Do NOT change! */ - err += sn9c102_write_reg(cam, 0x1f, 0x1a); - err += sn9c102_write_reg(cam, 0x1a, 0x1b); - err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); - - return err; -} - - -static int tas5130d1b_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x63, 0x19); - else - err += sn9c102_write_reg(cam, 0xf3, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5130d1b = { - .name = "TAS5130D1B", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_3WIRES, - .init = &tas5130d1b_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xf6, - .step = 0x02, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x47, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .set_ctrl = &tas5130d1b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &tas5130d1b_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5130d1b_set_pix_format -}; - - -int sn9c102_probe_tas5130d1b(struct sn9c102_device *cam) -{ - const struct usb_device_id tas5130d1b_id_table[] = { - { USB_DEVICE(0x0c45, 0x6024), }, - { USB_DEVICE(0x0c45, 0x6025), }, - { USB_DEVICE(0x0c45, 0x60aa), }, - { } - }; - - /* Sensor detection is based on USB pid/vid */ - if (!sn9c102_match_id(cam, tas5130d1b_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5130d1b); - - return 0; -} -- cgit v0.10.2 From 7e49027e62b9cb8addcd00f7498d389469f119d4 Mon Sep 17 00:00:00 2001 From: Anthony DeStefano Date: Thu, 19 Jun 2014 22:53:21 -0300 Subject: [media] staging: solo6x10: fix for sparse warning message Define jpeg_dqt as static. Signed-off-by: Anthony DeStefano Signed-off-by: Ismael Luceno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/solo6x10/solo6x10-jpeg.h b/drivers/staging/media/solo6x10/solo6x10-jpeg.h index c5218ce..9e41185 100644 --- a/drivers/staging/media/solo6x10/solo6x10-jpeg.h +++ b/drivers/staging/media/solo6x10/solo6x10-jpeg.h @@ -110,7 +110,7 @@ static const unsigned char jpeg_header[] = { /* This is the byte marker for the start of the DQT */ #define DQT_START 17 #define DQT_LEN 138 -const unsigned char jpeg_dqt[4][DQT_LEN] = { +static const unsigned char jpeg_dqt[4][DQT_LEN] = { { 0xff, 0xdb, 0x00, 0x43, 0x00, 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, -- cgit v0.10.2 From a85fd20fb62e5720ea4831b132334abc618972dd Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 26 May 2014 10:49:22 -0300 Subject: [media] mem2mem: make queue lock in v4l2_m2m_poll interruptible This patch makes the queue lock taken in v4l2_m2m_poll interruptible. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 178ce96..97defed 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -568,8 +568,12 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, if (m2m_ctx->m2m_dev->m2m_ops->lock) m2m_ctx->m2m_dev->m2m_ops->lock(m2m_ctx->priv); - else if (m2m_ctx->q_lock) - mutex_lock(m2m_ctx->q_lock); + else if (m2m_ctx->q_lock) { + if (mutex_lock_interruptible(m2m_ctx->q_lock)) { + rc |= POLLERR; + goto end; + } + } spin_lock_irqsave(&src_q->done_lock, flags); if (!list_empty(&src_q->done_list)) -- cgit v0.10.2 From 6bbd4fec0cd93823fe651fa3907bee2bc6c814f6 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 26 May 2014 11:17:32 -0300 Subject: [media] videobuf2-dma-contig: allow to vmap contiguous dma buffers This allows drivers to vmap contiguous dma buffers so they can inspect the buffer contents with the CPU. This will be needed for the CODA driver's JPEG handling. On CODA960, the header parsing has to be done on the CPU. The hardware modules can only process the entropy coded segment after all registers and tables are set up. Signed-off-by: Philipp Zabel Acked-by: Marek Szyprowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 880be07..6b254b8 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -98,6 +98,9 @@ static void *vb2_dc_vaddr(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; + if (!buf->vaddr && buf->db_attach) + buf->vaddr = dma_buf_vmap(buf->db_attach->dmabuf); + return buf->vaddr; } @@ -735,6 +738,7 @@ static int vb2_dc_map_dmabuf(void *mem_priv) buf->dma_addr = sg_dma_address(sgt->sgl); buf->dma_sgt = sgt; + buf->vaddr = NULL; return 0; } @@ -754,6 +758,10 @@ static void vb2_dc_unmap_dmabuf(void *mem_priv) return; } + if (buf->vaddr) { + dma_buf_vunmap(buf->db_attach->dmabuf, buf->vaddr); + buf->vaddr = NULL; + } dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir); buf->dma_addr = 0; -- cgit v0.10.2 From c41ad9c38ef36545563dd55345879fe5f867aa19 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 17 Jun 2014 08:52:24 -0300 Subject: [media] adv7604: Update recommended writes for the adv7611 Update the recommended writes to those mentioned in the Rev 1.5 version of the ADV7611 Register Settings Recommendations document released by Analog Devices. The document does not mention why the recommended settings have been updated, but presumably those are more fine tuned settings that can enhance performance in some cases. Signed-off-by: Lars-Peter Clausen Acked-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 1778d32..d4fa213 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2588,8 +2588,11 @@ static const struct adv7604_reg_seq adv7604_recommended_settings_hdmi[] = { }; static const struct adv7604_reg_seq adv7611_recommended_settings_hdmi[] = { + /* ADV7611 Register Settings Recommendations Rev 1.5, May 2014 */ { ADV7604_REG(ADV7604_PAGE_CP, 0x6c), 0x00 }, - { ADV7604_REG(ADV7604_PAGE_HDMI, 0x6f), 0x0c }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x9b), 0x03 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x6f), 0x08 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x85), 0x1f }, { ADV7604_REG(ADV7604_PAGE_HDMI, 0x87), 0x70 }, { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xda }, { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x01 }, -- cgit v0.10.2 From b7284bb0be49441805ae7db0fc419ab7ee882bff Mon Sep 17 00:00:00 2001 From: Ramakrishnan Muthukrishnan Date: Thu, 19 Jun 2014 14:22:57 -0300 Subject: [media] media: v4l2-core: remove the use of V4L2_FL_USE_FH_PRIO flag Since all the drivers that use `struct v4l2_fh' use the core priority checking instead of doing it themselves, this flag can be removed. This patch removes the usage of the flag from v4l2-core. Signed-off-by: Ramakrishnan Muthukrishnan Reviewed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 634d863..35698aa 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -563,11 +563,9 @@ static void determine_valid_ioctls(struct video_device *vdev) /* vfl_type and vfl_dir independent ioctls */ SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap); - if (ops->vidioc_g_priority || - test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) + if (ops->vidioc_g_priority) set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); - if (ops->vidioc_s_priority || - test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) + if (ops->vidioc_s_priority) set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); diff --git a/drivers/media/v4l2-core/v4l2-fh.c b/drivers/media/v4l2-core/v4l2-fh.c index e57c002..c97067a 100644 --- a/drivers/media/v4l2-core/v4l2-fh.c +++ b/drivers/media/v4l2-core/v4l2-fh.c @@ -37,6 +37,13 @@ void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) fh->ctrl_handler = vdev->ctrl_handler; INIT_LIST_HEAD(&fh->list); set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags); + /* + * determine_valid_ioctls() does not know if struct v4l2_fh + * is used by this driver, but here we do. So enable the + * prio ioctls here. + */ + set_bit(_IOC_NR(VIDIOC_G_PRIORITY), vdev->valid_ioctls); + set_bit(_IOC_NR(VIDIOC_S_PRIORITY), vdev->valid_ioctls); fh->prio = V4L2_PRIORITY_UNSET; init_waitqueue_head(&fh->wait); INIT_LIST_HEAD(&fh->available); @@ -49,8 +56,7 @@ void v4l2_fh_add(struct v4l2_fh *fh) { unsigned long flags; - if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags)) - v4l2_prio_open(fh->vdev->prio, &fh->prio); + v4l2_prio_open(fh->vdev->prio, &fh->prio); spin_lock_irqsave(&fh->vdev->fh_lock, flags); list_add(&fh->list, &fh->vdev->fh_list); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); @@ -78,8 +84,7 @@ void v4l2_fh_del(struct v4l2_fh *fh) spin_lock_irqsave(&fh->vdev->fh_lock, flags); list_del_init(&fh->list); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags)) - v4l2_prio_close(fh->vdev->prio, fh->prio); + v4l2_prio_close(fh->vdev->prio, fh->prio); } EXPORT_SYMBOL_GPL(v4l2_fh_del); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 16bffd8..8d4a25d 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2190,7 +2190,6 @@ static long __video_do_ioctl(struct file *file, const struct v4l2_ioctl_info *info; void *fh = file->private_data; struct v4l2_fh *vfh = NULL; - int use_fh_prio = 0; int debug = vfd->debug; long ret = -ENOTTY; @@ -2200,10 +2199,8 @@ static long __video_do_ioctl(struct file *file, return ret; } - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) vfh = file->private_data; - use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); - } if (v4l2_is_known_ioctl(cmd)) { info = &v4l2_ioctls[_IOC_NR(cmd)]; @@ -2212,7 +2209,7 @@ static long __video_do_ioctl(struct file *file, !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler)) goto done; - if (use_fh_prio && (info->flags & INFO_FL_PRIO)) { + if (vfh && (info->flags & INFO_FL_PRIO)) { ret = v4l2_prio_check(vfd->prio, vfh->prio); if (ret) goto done; @@ -2237,7 +2234,7 @@ static long __video_do_ioctl(struct file *file, ret = -ENOTTY; } else { ret = ops->vidioc_default(file, fh, - use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, + vfh ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, cmd, arg); } -- cgit v0.10.2 From 95cd5d5ee67c2108765ae87d34761d54fd6671ce Mon Sep 17 00:00:00 2001 From: Ramakrishnan Muthukrishnan Date: Thu, 19 Jun 2014 14:22:58 -0300 Subject: [media] media: remove the setting of the flag V4L2_FL_USE_FH_PRIO Since all the drivers that use `struct v4l2_fh' use the core priority checking, the setting of the flag in the drivers can be removed. Signed-off-by: Ramakrishnan Muthukrishnan Reviewed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index eda01bc..f2cc521 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -613,7 +613,6 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, vfd->lock = &dev->v4l2_lock; vfd->v4l2_dev = &dev->v4l2_dev; vfd->tvnorms = 0; - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); for (i = 0; i < dev->ext_vv_data->num_stds; i++) vfd->tvnorms |= dev->ext_vv_data->stds[i].id; strlcpy(vfd->name, name, sizeof(vfd->name)); diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c index 416507a..3c5dac4 100644 --- a/drivers/media/parport/bw-qcam.c +++ b/drivers/media/parport/bw-qcam.c @@ -990,7 +990,6 @@ static struct qcam *qcam_init(struct parport *port) qcam->vdev.fops = &qcam_fops; qcam->vdev.lock = &qcam->lock; qcam->vdev.ioctl_ops = &qcam_ioctl_ops; - set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags); qcam->vdev.release = video_device_release_empty; video_set_drvdata(&qcam->vdev, qcam); diff --git a/drivers/media/parport/c-qcam.c b/drivers/media/parport/c-qcam.c index ec51e1f..b9010bd 100644 --- a/drivers/media/parport/c-qcam.c +++ b/drivers/media/parport/c-qcam.c @@ -761,7 +761,6 @@ static struct qcam *qcam_init(struct parport *port) qcam->vdev.ioctl_ops = &qcam_ioctl_ops; qcam->vdev.release = video_device_release_empty; qcam->vdev.ctrl_handler = &qcam->hdl; - set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags); video_set_drvdata(&qcam->vdev, qcam); mutex_init(&qcam->lock); diff --git a/drivers/media/parport/pms.c b/drivers/media/parport/pms.c index 66c957a..9bc105b 100644 --- a/drivers/media/parport/pms.c +++ b/drivers/media/parport/pms.c @@ -1091,7 +1091,6 @@ static int pms_probe(struct device *pdev, unsigned int card) dev->vdev.release = video_device_release_empty; dev->vdev.lock = &dev->lock; dev->vdev.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); video_set_drvdata(&dev->vdev, dev); dev->std = V4L2_STD_NTSC_M; dev->height = 240; diff --git a/drivers/media/parport/w9966.c b/drivers/media/parport/w9966.c index db2a600..f7502f3 100644 --- a/drivers/media/parport/w9966.c +++ b/drivers/media/parport/w9966.c @@ -883,7 +883,6 @@ static int w9966_init(struct w9966 *cam, struct parport *port) cam->vdev.ioctl_ops = &w9966_ioctl_ops; cam->vdev.release = video_device_release_empty; cam->vdev.ctrl_handler = &cam->hdl; - set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); video_set_drvdata(&cam->vdev, cam); mutex_init(&cam->lock); diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index da780f4..970e542 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3886,7 +3886,6 @@ static struct video_device *vdev_init(struct bttv *btv, vfd->v4l2_dev = &btv->c.v4l2_dev; vfd->release = video_device_release; vfd->debug = bttv_debug; - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); video_set_drvdata(vfd, btv); snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)", btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "", diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c index 843c62b..f3541b5 100644 --- a/drivers/media/pci/cx18/cx18-streams.c +++ b/drivers/media/pci/cx18/cx18-streams.c @@ -375,7 +375,6 @@ static int cx18_prep_dev(struct cx18 *cx, int type) s->video_dev->release = video_device_release; s->video_dev->tvnorms = V4L2_STD_ALL; s->video_dev->lock = &cx->serialize_lock; - set_bit(V4L2_FL_USE_FH_PRIO, &s->video_dev->flags); cx18_set_funcs(s->video_dev); return 0; } diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index d270819..8d2f1ab 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -1109,7 +1109,6 @@ int cx25821_video_register(struct cx25821_dev *dev) else vdev->vfl_dir = VFL_DIR_TX; vdev->lock = &dev->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); snprintf(vdev->name, sizeof(vdev->name), "%s #%d", dev->name, i); video_set_drvdata(vdev, chan); diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index e061c88..7163023 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -1045,7 +1045,6 @@ struct video_device *cx88_vdev_init(struct cx88_core *core, vfd->release = video_device_release; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", core->name, type, core->board.name); - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); return vfd; } diff --git a/drivers/media/pci/ivtv/ivtv-streams.c b/drivers/media/pci/ivtv/ivtv-streams.c index 70dad58..f0a1cc4 100644 --- a/drivers/media/pci/ivtv/ivtv-streams.c +++ b/drivers/media/pci/ivtv/ivtv-streams.c @@ -251,7 +251,6 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) v4l2_disable_ioctl(s->vdev, VIDIOC_G_TUNER); v4l2_disable_ioctl(s->vdev, VIDIOC_S_STD); } - set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags); ivtv_set_funcs(s->vdev); return 0; } diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 54d5c82..1a77f8d 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -1749,7 +1749,6 @@ static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) v4l2_ctrl_handler_setup(&meye.hdl); meye.vdev->ctrl_handler = &meye.hdl; - set_bit(V4L2_FL_USE_FH_PRIO, &meye.vdev->flags); if (video_register_device(meye.vdev, VFL_TYPE_GRABBER, video_nr) < 0) { diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index be19a05..9ff03a6 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -811,7 +811,6 @@ static struct video_device *vdev_init(struct saa7134_dev *dev, vfd->release = video_device_release; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, saa7134_boards[dev->board].name); - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); video_set_drvdata(vfd, dev); return vfd; } diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index e65c760..5526ed5 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -270,7 +270,6 @@ static int empress_init(struct saa7134_dev *dev) snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name), "%s empress (%s)", dev->name, saa7134_boards[dev->board].name); - set_bit(V4L2_FL_USE_FH_PRIO, &dev->empress_dev->flags); v4l2_ctrl_handler_init(hdl, 21); v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler, empress_ctrl_filter); if (dev->empress_sd) diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index d2abd3b..f2d8c70 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1093,7 +1093,6 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, vip->video_dev = &video_dev_template; vip->video_dev->v4l2_dev = &vip->v4l2_dev; vip->video_dev->queue = &vip->vb_vidq; - set_bit(V4L2_FL_USE_FH_PRIO, &vip->video_dev->flags); video_set_drvdata(vip->video_dev, vip); ret = video_register_device(vip->video_dev, VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/platform/arv.c b/drivers/media/platform/arv.c index e9410e4..03c5098 100644 --- a/drivers/media/platform/arv.c +++ b/drivers/media/platform/arv.c @@ -773,7 +773,6 @@ static int __init ar_init(void) ar->vdev.fops = &ar_fops; ar->vdev.ioctl_ops = &ar_ioctl_ops; ar->vdev.release = video_device_release_empty; - set_bit(V4L2_FL_USE_FH_PRIO, &ar->vdev.flags); video_set_drvdata(&ar->vdev, ar); if (vga) { diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 16e4b1c..6ef9866 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -966,7 +966,6 @@ static int bcap_probe(struct platform_device *pdev) vfd->ioctl_ops = &bcap_ioctl_ops; vfd->tvnorms = 0; vfd->v4l2_dev = &bcap_dev->v4l2_dev; - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name)); bcap_dev->video_dev = vfd; diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index bf5eff9..73496d9 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1709,7 +1709,6 @@ static int register_device(struct vpbe_layer *vpbe_display_layer, vpbe_display_layer->disp_dev = disp_dev; /* set the driver data in platform device */ platform_set_drvdata(pdev, disp_dev); - set_bit(V4L2_FL_USE_FH_PRIO, &vpbe_display_layer->video_dev.flags); video_set_drvdata(&vpbe_display_layer->video_dev, vpbe_display_layer); diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index a51bda2..ea7661a 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -1916,7 +1916,6 @@ static int vpfe_probe(struct platform_device *pdev) v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "video_dev=%x\n", (int)&vpfe_dev->video_dev); vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - set_bit(V4L2_FL_USE_FH_PRIO, &vpfe_dev->video_dev->flags); ret = video_register_device(vpfe_dev->video_dev, VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index ee0a5fc..2f90f0d 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1397,7 +1397,6 @@ static int vpif_probe_complete(void) vdev->vfl_dir = VFL_DIR_RX; vdev->queue = q; vdev->lock = &common->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); video_set_drvdata(ch->video_dev, ch); err = video_register_device(vdev, VFL_TYPE_GRABBER, (j ? 1 : 0)); diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 5bb085b..877b46e 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1223,7 +1223,6 @@ static int vpif_probe_complete(void) vdev->vfl_dir = VFL_DIR_TX; vdev->queue = q; vdev->lock = &common->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); video_set_drvdata(ch->video_dev, ch); err = video_register_device(vdev, VFL_TYPE_GRABBER, (j ? 3 : 2)); diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index deba425..8ea5209 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -1172,7 +1172,6 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx) goto err_vd_rel; video_set_drvdata(vfd, vp); - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); v4l2_ctrl_handler_init(&vp->ctrl_handler, 1); ctrl = v4l2_ctrl_new_std(&vp->ctrl_handler, &s3c_camif_video_ctrl_ops, diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 8a8dbc8..b4d2696 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -1109,8 +1109,6 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, .ioctl_ops = &mxr_ioctl_ops, }; strlcpy(layer->vfd.name, name, sizeof(layer->vfd.name)); - /* let framework control PRIORITY */ - set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags); video_set_drvdata(&layer->vfd, layer); layer->vfd.lock = &layer->mutex; diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index d00bf3d..b7fbcdf 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -1459,7 +1459,6 @@ static int __init vivi_create_instance(int inst) vfd->debug = debug; vfd->v4l2_dev = &dev->v4l2_dev; vfd->queue = q; - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); /* * Provide a mutex to v4l2 core. It will be used to protect diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index 142c2ee..2262b81 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c @@ -390,7 +390,6 @@ static int usb_dsbr100_probe(struct usb_interface *intf, radio->videodev.release = video_device_release_empty; radio->videodev.lock = &radio->v4l2_lock; radio->videodev.ctrl_handler = &radio->hdl; - set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); radio->usbdev = interface_to_usbdev(intf); radio->curfreq = FREQ_MIN * FREQ_MUL; diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index d719e59..82affae 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -650,7 +650,6 @@ static int __init cadet_init(void) dev->vdev.ioctl_ops = &cadet_ioctl_ops; dev->vdev.release = video_device_release_empty; dev->vdev.lock = &dev->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); video_set_drvdata(&dev->vdev, dev); res = video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr); diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c index 6ff3508..c309ee4 100644 --- a/drivers/media/radio/radio-isa.c +++ b/drivers/media/radio/radio-isa.c @@ -253,7 +253,6 @@ static int radio_isa_common_probe(struct radio_isa_card *isa, isa->vdev.fops = &radio_isa_fops; isa->vdev.ioctl_ops = &radio_isa_ioctl_ops; isa->vdev.release = video_device_release_empty; - set_bit(V4L2_FL_USE_FH_PRIO, &isa->vdev.flags); video_set_drvdata(&isa->vdev, isa); isa->freq = FREQ_LOW; isa->stereo = drv->has_stereo; diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index 3d12782..67ac72e 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -380,7 +380,6 @@ static int usb_keene_probe(struct usb_interface *intf, usb_set_intfdata(intf, &radio->v4l2_dev); video_set_drvdata(&radio->vdev, radio); - set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); /* at least 11ms is needed in order to settle hardware */ msleep(20); diff --git a/drivers/media/radio/radio-ma901.c b/drivers/media/radio/radio-ma901.c index a85b064..b3000ef 100644 --- a/drivers/media/radio/radio-ma901.c +++ b/drivers/media/radio/radio-ma901.c @@ -411,7 +411,6 @@ static int usb_ma901radio_probe(struct usb_interface *intf, radio->vdev.ioctl_ops = &usb_ma901radio_ioctl_ops; radio->vdev.release = video_device_release_empty; radio->vdev.lock = &radio->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index a7e93d7..3d12edf 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -210,7 +210,6 @@ static int __init pcm20_init(void) dev->vdev.ioctl_ops = &pcm20_ioctl_ops; dev->vdev.release = video_device_release_empty; dev->vdev.lock = &dev->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); video_set_drvdata(&dev->vdev, dev); snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, dev->audmode == V4L2_TUNER_MODE_MONO, -1); diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index f476071..c2927fd 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -558,7 +558,6 @@ static int usb_amradio_probe(struct usb_interface *intf, radio->vdev.ioctl_ops = &usb_amradio_ioctl_ops; radio->vdev.release = video_device_release_empty; radio->vdev.lock = &radio->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; diff --git a/drivers/media/radio/radio-raremono.c b/drivers/media/radio/radio-raremono.c index 7b3bdbb..bfb3a6d 100644 --- a/drivers/media/radio/radio-raremono.c +++ b/drivers/media/radio/radio-raremono.c @@ -361,7 +361,6 @@ static int usb_raremono_probe(struct usb_interface *intf, usb_set_intfdata(intf, &radio->v4l2_dev); video_set_drvdata(&radio->vdev, radio); - set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); raremono_cmd_main(radio, BAND_FM, 95160); diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 6f4318f..d7ce8fe 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -344,7 +344,6 @@ static int __init fmi_init(void) fmi->vdev.fops = &fmi_fops; fmi->vdev.ioctl_ops = &fmi_ioctl_ops; fmi->vdev.release = video_device_release_empty; - set_bit(V4L2_FL_USE_FH_PRIO, &fmi->vdev.flags); video_set_drvdata(&fmi->vdev, fmi); mutex_init(&fmi->lock); diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c index 2fd9009..633022b 100644 --- a/drivers/media/radio/radio-si476x.c +++ b/drivers/media/radio/radio-si476x.c @@ -1470,7 +1470,6 @@ static int si476x_radio_probe(struct platform_device *pdev) video_set_drvdata(&radio->videodev, radio); platform_set_drvdata(pdev, radio); - set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); radio->v4l2dev.ctrl_handler = &radio->ctrl_handler; v4l2_ctrl_handler_init(&radio->ctrl_handler, diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index 3ed1f56..9250496 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -478,7 +478,6 @@ static int tea5764_i2c_probe(struct i2c_client *client, video_set_drvdata(&radio->vdev, radio); radio->vdev.lock = &radio->mutex; radio->vdev.v4l2_dev = v4l2_dev; - set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); /* initialize and power off the chip */ tea5764_i2c_read(radio); diff --git a/drivers/media/radio/radio-tea5777.c b/drivers/media/radio/radio-tea5777.c index e245597..83fe7ab 100644 --- a/drivers/media/radio/radio-tea5777.c +++ b/drivers/media/radio/radio-tea5777.c @@ -570,7 +570,6 @@ int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner) tea->fops = tea575x_fops; tea->fops.owner = owner; tea->vd.fops = &tea->fops; - set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags); tea->vd.ctrl_handler = &tea->ctrl_handler; v4l2_ctrl_handler_init(&tea->ctrl_handler, 1); diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c index 0817964..b9285e6 100644 --- a/drivers/media/radio/radio-timb.c +++ b/drivers/media/radio/radio-timb.c @@ -126,7 +126,6 @@ static int timbradio_probe(struct platform_device *pdev) tr->video_dev.release = video_device_release_empty; tr->video_dev.minor = -1; tr->video_dev.lock = &tr->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &tr->video_dev.flags); strlcpy(tr->v4l2_dev.name, DRIVER_NAME, sizeof(tr->v4l2_dev.name)); err = v4l2_device_register(NULL, &tr->v4l2_dev); diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index 07ef405..494fac0 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -680,7 +680,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, radio->videodev.lock = &radio->lock; radio->videodev.v4l2_dev = &radio->v4l2_dev; radio->videodev.release = video_device_release_empty; - set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); video_set_drvdata(&radio->videodev, radio); /* get device and chip versions */ diff --git a/drivers/media/radio/si4713/radio-platform-si4713.c b/drivers/media/radio/si4713/radio-platform-si4713.c index ba4cfc9..a47502a 100644 --- a/drivers/media/radio/si4713/radio-platform-si4713.c +++ b/drivers/media/radio/si4713/radio-platform-si4713.c @@ -196,7 +196,6 @@ static int radio_si4713_pdriver_probe(struct platform_device *pdev) rsdev->radio_dev = radio_si4713_vdev_template; rsdev->radio_dev.v4l2_dev = &rsdev->v4l2_dev; rsdev->radio_dev.ctrl_handler = sd->ctrl_handler; - set_bit(V4L2_FL_USE_FH_PRIO, &rsdev->radio_dev.flags); /* Serialize all access to the si4713 */ rsdev->radio_dev.lock = &rsdev->lock; video_set_drvdata(&rsdev->radio_dev, rsdev); diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c index 86502b2..a77319d 100644 --- a/drivers/media/radio/si4713/radio-usb-si4713.c +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -492,7 +492,6 @@ static int usb_si4713_probe(struct usb_interface *intf, radio->vdev.vfl_dir = VFL_DIR_TX; video_set_drvdata(&radio->vdev, radio); - set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1); if (retval < 0) { diff --git a/drivers/media/radio/tea575x.c b/drivers/media/radio/tea575x.c index 7c14060..f1a0867 100644 --- a/drivers/media/radio/tea575x.c +++ b/drivers/media/radio/tea575x.c @@ -523,7 +523,6 @@ int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner) tea->fops = tea575x_fops; tea->fops.owner = owner; tea->vd.fops = &tea->fops; - set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags); /* disable hw_freq_seek if we can't use it */ if (tea->cannot_read_data) v4l2_disable_ioctl(&tea->vd, VIDIOC_S_HW_FREQ_SEEK); diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 385894a..98f7ea1 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -2016,14 +2016,12 @@ int au0828_analog_register(struct au0828_dev *dev, *dev->vdev = au0828_video_template; dev->vdev->v4l2_dev = &dev->v4l2_dev; dev->vdev->lock = &dev->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev->flags); strcpy(dev->vdev->name, "au0828a video"); /* Setup the VBI device */ *dev->vbi_dev = au0828_video_template; dev->vbi_dev->v4l2_dev = &dev->v4l2_dev; dev->vbi_dev->lock = &dev->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &dev->vbi_dev->flags); strcpy(dev->vbi_dev->name, "au0828a vbi"); /* Register the v4l2 device */ diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index d5d42b6..9caea83 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -1169,7 +1169,6 @@ int cpia2_register_camera(struct camera_data *cam) cam->vdev.lock = &cam->v4l2_lock; cam->vdev.ctrl_handler = hdl; cam->vdev.v4l2_dev = &cam->v4l2_dev; - set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); reset_camera_struct_v4l(cam); diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 30a0c69..f0400e2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1923,7 +1923,6 @@ static struct video_device *cx231xx_video_dev_alloc( vfd->v4l2_dev = &dev->v4l2_dev; vfd->lock = &dev->lock; vfd->release = video_device_release; - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); vfd->ctrl_handler = &dev->mpeg_ctrl_handler.hdl; video_set_drvdata(vfd, dev); if (dev->tuner_type == TUNER_ABSENT) { diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index cba7fea..ae31ca2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -2065,7 +2065,6 @@ static struct video_device *cx231xx_vdev_init(struct cx231xx *dev, vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index f6b49c9..3f8b5aa 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2208,7 +2208,6 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, vfd->v4l2_dev = &dev->v4l2->v4l2_dev; vfd->debug = video_debug; vfd->lock = &dev->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); if (dev->board.is_webcam) vfd->tvnorms = 0; diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index f9a75ad..42d2232 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -2066,7 +2066,6 @@ int gspca_dev_probe2(struct usb_interface *intf, gspca_dev->vdev = gspca_template; gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; video_set_drvdata(&gspca_dev->vdev, gspca_dev); - set_bit(V4L2_FL_USE_FH_PRIO, &gspca_dev->vdev.flags); gspca_dev->module = module; gspca_dev->present = 1; diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 0500c417..dca4b65 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -1240,7 +1240,6 @@ int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, strcpy(dev->video_dev->name, "Hauppauge HD PVR"); dev->video_dev->v4l2_dev = &dev->v4l2_dev; video_set_drvdata(dev->video_dev, dev); - set_bit(V4L2_FL_USE_FH_PRIO, &dev->video_dev->flags); res = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum); if (res < 0) { diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index a73b0bc..15b754d 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1013,7 +1013,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id strcpy(pdev->vdev.name, name); pdev->vdev.queue = &pdev->vb_queue; pdev->vdev.queue->lock = &pdev->vb_queue_lock; - set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags); video_set_drvdata(&pdev->vdev, pdev); pdev->release = le16_to_cpu(udev->descriptor.bcdDevice); diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index a44466b..2c90186 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -1676,7 +1676,6 @@ static int s2255_probe_v4l(struct s2255_dev *dev) vc->vdev.ctrl_handler = &vc->hdl; vc->vdev.lock = &dev->lock; vc->vdev.v4l2_dev = &dev->v4l2_dev; - set_bit(V4L2_FL_USE_FH_PRIO, &vc->vdev.flags); video_set_drvdata(&vc->vdev, vc); if (video_nr == -1) ret = video_register_device(&vc->vdev, diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 5461341..2330543 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -671,7 +671,6 @@ int stk1160_video_register(struct stk1160 *dev) /* This will be used to set video_device parent */ dev->vdev.v4l2_dev = &dev->v4l2_dev; - set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); /* NTSC is default */ dev->norm = V4L2_STD_NTSC_M; diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index be77482..d76860b 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -1266,7 +1266,6 @@ static int stk_register_video_device(struct stk_camera *dev) dev->vdev.lock = &dev->lock; dev->vdev.debug = debug; dev->vdev.v4l2_dev = &dev->v4l2_dev; - set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); video_set_drvdata(&dev->vdev, dev); err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); if (err) diff --git a/drivers/media/usb/tlg2300/pd-radio.c b/drivers/media/usb/tlg2300/pd-radio.c index ea6070b..b391194 100644 --- a/drivers/media/usb/tlg2300/pd-radio.c +++ b/drivers/media/usb/tlg2300/pd-radio.c @@ -327,7 +327,6 @@ int poseidon_fm_init(struct poseidon *p) } vfd->v4l2_dev = &p->v4l2_dev; vfd->ctrl_handler = hdl; - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); video_set_drvdata(vfd, p); return video_register_device(vfd, VFL_TYPE_RADIO, -1); } diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index e6b3d5d..9bde064 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1626,7 +1626,6 @@ static struct video_device *vdev_init(struct tm6000_core *dev, vfd->release = video_device_release; vfd->debug = tm6000_debug; vfd->lock = &dev->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 2967e80..030c585 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -701,7 +701,6 @@ int usbtv_video_init(struct usbtv *usbtv) usbtv->vdev.tvnorms = USBTV_TV_STD; usbtv->vdev.queue = &usbtv->vb2q; usbtv->vdev.lock = &usbtv->v4l2_lock; - set_bit(V4L2_FL_USE_FH_PRIO, &usbtv->vdev.flags); video_set_drvdata(&usbtv->vdev, usbtv); ret = video_register_device(&usbtv->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index ad47c5c..f8135f4 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1746,7 +1746,6 @@ static int uvc_register_video(struct uvc_device *dev, vdev->fops = &uvc_fops; vdev->release = uvc_release; vdev->prio = &stream->chain->prio; - set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) vdev->vfl_dir = VFL_DIR_TX; strlcpy(vdev->name, dev->name, sizeof vdev->name); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 74d56df..3b80579 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -1456,7 +1456,6 @@ static int zr364xx_probe(struct usb_interface *intf, cam->vdev.lock = &cam->lock; cam->vdev.v4l2_dev = &cam->v4l2_dev; cam->vdev.ctrl_handler = &cam->ctrl_handler; - set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); video_set_drvdata(&cam->vdev, cam); if (debug) cam->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index d95c427..6f9171c 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -1606,7 +1606,6 @@ int vpfe_video_init(struct vpfe_video_device *video, const char *name) if (ret < 0) return ret; - set_bit(V4L2_FL_USE_FH_PRIO, &video->video_dev.flags); video_set_drvdata(&video->video_dev, video); return 0; diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index da7b549..ecb5336 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1001,7 +1001,6 @@ int go7007_v4l2_init(struct go7007 *go) *vdev = go7007_template; vdev->lock = &go->serialize_lock; vdev->queue = &go->vidq; - set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); video_set_drvdata(vdev, go); vdev->v4l2_dev = &go->v4l2_dev; if (!v4l2_device_has_op(&go->v4l2_dev, video, querystd)) diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 08d0d09..53aca38 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -1418,7 +1418,6 @@ static int msi3101_probe(struct usb_interface *intf, s->vdev = msi3101_template; s->vdev.queue = &s->vb_queue; s->vdev.queue->lock = &s->vb_queue_lock; - set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev.flags); video_set_drvdata(&s->vdev, s); /* Register the v4l2_device structure */ diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c index 4d2e30f..cee1273 100644 --- a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c +++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c @@ -1480,7 +1480,6 @@ struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, s->vdev = rtl2832_sdr_template; s->vdev.queue = &s->vb_queue; s->vdev.queue->lock = &s->vb_queue_lock; - set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev.flags); video_set_drvdata(&s->vdev, s); /* Register the v4l2_device structure */ diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index b8ff113..bb2604e 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -1326,7 +1326,6 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, solo_enc->vfd->ctrl_handler = hdl; solo_enc->vfd->queue = &solo_enc->vidq; solo_enc->vfd->lock = &solo_enc->lock; - set_bit(V4L2_FL_USE_FH_PRIO, &solo_enc->vfd->flags); video_set_drvdata(solo_enc->vfd, solo_enc); ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr); if (ret < 0) diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2.c b/drivers/staging/media/solo6x10/solo6x10-v4l2.c index 5d0100e..ba2526c 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2.c @@ -666,7 +666,6 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr) goto fail; } solo_dev->vfd->ctrl_handler = &solo_dev->disp_hdl; - set_bit(V4L2_FL_USE_FH_PRIO, &solo_dev->vfd->flags); video_set_drvdata(solo_dev->vfd, solo_dev); -- cgit v0.10.2 From dc3094a7135c2386d6ff465b13d008ea2262718d Mon Sep 17 00:00:00 2001 From: Ramakrishnan Muthukrishnan Date: Thu, 19 Jun 2014 14:22:59 -0300 Subject: [media] media: v4l2-dev.h: remove V4L2_FL_USE_FH_PRIO flag Since none of the drivers are using it, this flag can be removed. Signed-off-by: Ramakrishnan Muthukrishnan Reviewed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index eec6e46..eb76cfd 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -44,8 +44,6 @@ struct v4l2_ctrl_handler; #define V4L2_FL_REGISTERED (0) /* file->private_data points to struct v4l2_fh */ #define V4L2_FL_USES_V4L2_FH (1) -/* Use the prio field of v4l2_fh for core priority checking */ -#define V4L2_FL_USE_FH_PRIO (2) /* Priority helper functions */ -- cgit v0.10.2 From ff792c85e60727e66774eb3da8129298690eab0c Mon Sep 17 00:00:00 2001 From: Ramakrishnan Muthukrishnan Date: Thu, 19 Jun 2014 14:23:00 -0300 Subject: [media] media: Documentation: remove V4L2_FL_USE_FH_PRIO flag The V4L2_FL_USE_FH_PRIO has been removed from the code, now remove it from the documentation. Signed-off-by: Ramakrishnan Muthukrishnan Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 667a433..a11dff0 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -675,11 +675,6 @@ You should also set these fields: video_device is initialized you *do* know which parent PCI device to use and so you set dev_device to the correct PCI device. -- flags: optional. Set to V4L2_FL_USE_FH_PRIO if you want to let the framework - handle the VIDIOC_G/S_PRIORITY ioctls. This requires that you use struct - v4l2_fh. Eventually this flag will disappear once all drivers use the core - priority handling. But for now it has to be set explicitly. - If you use v4l2_ioctl_ops, then you should set .unlocked_ioctl to video_ioctl2 in your v4l2_file_operations struct. @@ -909,8 +904,7 @@ struct v4l2_fh struct v4l2_fh provides a way to easily keep file handle specific data that is used by the V4L2 framework. New drivers must use struct v4l2_fh -since it is also used to implement priority handling (VIDIOC_G/S_PRIORITY) -if the video_device flag V4L2_FL_USE_FH_PRIO is also set. +since it is also used to implement priority handling (VIDIOC_G/S_PRIORITY). The users of v4l2_fh (in the V4L2 framework, not the driver) know whether a driver uses v4l2_fh as its file->private_data pointer by diff --git a/Documentation/video4linux/v4l2-pci-skeleton.c b/Documentation/video4linux/v4l2-pci-skeleton.c index 46904fe..006721e 100644 --- a/Documentation/video4linux/v4l2-pci-skeleton.c +++ b/Documentation/video4linux/v4l2-pci-skeleton.c @@ -883,11 +883,6 @@ static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent) vdev->v4l2_dev = &skel->v4l2_dev; /* Supported SDTV standards, if any */ vdev->tvnorms = SKEL_TVNORMS; - /* If this bit is set, then the v4l2 core will provide the support - * for the VIDIOC_G/S_PRIORITY ioctls. This flag will eventually - * go away once all drivers have been converted to use struct v4l2_fh. - */ - set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); video_set_drvdata(vdev, skel); ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); diff --git a/Documentation/zh_CN/video4linux/v4l2-framework.txt b/Documentation/zh_CN/video4linux/v4l2-framework.txt index 0da95db..2b828e6 100644 --- a/Documentation/zh_CN/video4linux/v4l2-framework.txt +++ b/Documentation/zh_CN/video4linux/v4l2-framework.txt @@ -580,11 +580,6 @@ release()回调必须被设置,且在最后一个 video_device 用户退出之 v4l2_device 无法与特定的 PCI 设备关联,所有没有设置父设备。但当 video_device 配置后,就知道使用哪个父 PCI 设备了。 -- flags:可选。如果你要让框架处理设置 VIDIOC_G/S_PRIORITY ioctls, - 请设置 V4L2_FL_USE_FH_PRIO。这要求你使用 v4l2_fh 结构体。 - 一旦所有驱动使用了核心的优先级处理,最终这个标志将消失。但现在它 - 必须被显式设置。 - 如果你使用 v4l2_ioctl_ops,则应该在 v4l2_file_operations 结构体中 设置 .unlocked_ioctl 指向 video_ioctl2。 @@ -789,7 +784,7 @@ v4l2_fh 结构体 ------------- v4l2_fh 结构体提供一个保存用于 V4L2 框架的文件句柄特定数据的简单方法。 -如果 video_device 的 flag 设置了 V4L2_FL_USE_FH_PRIO 标志,新驱动 +如果 video_device 标志,新驱动 必须使用 v4l2_fh 结构体,因为它也用于实现优先级处理(VIDIOC_G/S_PRIORITY)。 v4l2_fh 的用户(位于 V4l2 框架中,并非驱动)可通过测试 -- cgit v0.10.2 From 34ba2e65bab3693cdd7c2ee0b8ba6477bd8c366b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:27 -0300 Subject: [media] dib8000: Fix handling of interleave bigger than 2 If interleave is bigger than 2, the code will set it to 0, as dib8000 registers use a log2(). So, change the code to handle it accordingly. Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 270a58e..78ccbe1 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -2033,9 +2033,8 @@ static u16 dib8000_set_layer(struct dib8000_state *state, u8 layer_index, u16 ma break; } - if ((c->layer[layer_index].interleaving > 0) && ((c->layer[layer_index].interleaving <= 3) || (c->layer[layer_index].interleaving == 4 && c->isdbt_sb_mode == 1))) - time_intlv = c->layer[layer_index].interleaving; - else + time_intlv = fls(c->layer[layer_index].interleaving); + if (time_intlv > 3 && !(time_intlv == 4 && c->isdbt_sb_mode == 1)) time_intlv = 0; dib8000_write_word(state, 2 + layer_index, (constellation << 10) | ((c->layer[layer_index].segment_count & 0xf) << 6) | (cr << 3) | time_intlv); -- cgit v0.10.2 From c063c7c6a355cc7214ed8fb31d5e989b3baa0b87 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:28 -0300 Subject: [media] dib8000: Fix ADC OFF settings The ADC OFF values are wrong. This causes troubles on detecting weak signals. Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 78ccbe1..8dd34e0 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -588,8 +588,8 @@ static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_s break; case DIBX000_ADC_OFF: // leave the VBG voltage on - reg_907 |= (1 << 14) | (1 << 13) | (1 << 12); - reg_908 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); + reg_907 = (1 << 13) | (1 << 12); + reg_908 = (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 1); break; case DIBX000_VBG_ENABLE: -- cgit v0.10.2 From 7fa676c6f4d758271dfe6ff5b4bfece3eacb9aeb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:29 -0300 Subject: [media] dib8000: Fix alignments at dib8000_tune() There are two tabs instead of one aligning this struct. Worse than that, on some places, the alignment is wrong. Fix it. No functional changes. Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 8dd34e0..43d1261 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3018,313 +3018,313 @@ static int dib8000_tune(struct dvb_frontend *fe) switch (*tune_state) { case CT_DEMOD_START: /* 30 */ - dib8000_reset_stats(fe); + dib8000_reset_stats(fe); - if (state->revision == 0x8090) - dib8090p_init_sdram(state); - state->status = FE_STATUS_TUNE_PENDING; - state->channel_parameters_set = is_manual_mode(c); + if (state->revision == 0x8090) + dib8090p_init_sdram(state); + state->status = FE_STATUS_TUNE_PENDING; + state->channel_parameters_set = is_manual_mode(c); - dprintk("Tuning channel on %s search mode", - state->channel_parameters_set ? "manual" : "auto"); + dprintk("Tuning channel on %s search mode", + state->channel_parameters_set ? "manual" : "auto"); - dib8000_viterbi_state(state, 0); /* force chan dec in restart */ + dib8000_viterbi_state(state, 0); /* force chan dec in restart */ - /* Layer monitor */ - dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); + /* Layer monitor */ + dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); - dib8000_set_frequency_offset(state); - dib8000_set_bandwidth(fe, c->bandwidth_hz / 1000); + dib8000_set_frequency_offset(state); + dib8000_set_bandwidth(fe, c->bandwidth_hz / 1000); - if (state->channel_parameters_set == 0) { /* The channel struct is unknown, search it ! */ + if (state->channel_parameters_set == 0) { /* The channel struct is unknown, search it ! */ #ifdef DIB8000_AGC_FREEZE - if (state->revision != 0x8090) { - state->agc1_max = dib8000_read_word(state, 108); - state->agc1_min = dib8000_read_word(state, 109); - state->agc2_max = dib8000_read_word(state, 110); - state->agc2_min = dib8000_read_word(state, 111); - agc1 = dib8000_read_word(state, 388); - agc2 = dib8000_read_word(state, 389); - dib8000_write_word(state, 108, agc1); - dib8000_write_word(state, 109, agc1); - dib8000_write_word(state, 110, agc2); - dib8000_write_word(state, 111, agc2); - } -#endif - state->autosearch_state = AS_SEARCHING_FFT; - state->found_nfft = TRANSMISSION_MODE_AUTO; - state->found_guard = GUARD_INTERVAL_AUTO; - *tune_state = CT_DEMOD_SEARCH_NEXT; - } else { /* we already know the channel struct so TUNE only ! */ - state->autosearch_state = AS_DONE; - *tune_state = CT_DEMOD_STEP_3; + if (state->revision != 0x8090) { + state->agc1_max = dib8000_read_word(state, 108); + state->agc1_min = dib8000_read_word(state, 109); + state->agc2_max = dib8000_read_word(state, 110); + state->agc2_min = dib8000_read_word(state, 111); + agc1 = dib8000_read_word(state, 388); + agc2 = dib8000_read_word(state, 389); + dib8000_write_word(state, 108, agc1); + dib8000_write_word(state, 109, agc1); + dib8000_write_word(state, 110, agc2); + dib8000_write_word(state, 111, agc2); } - state->symbol_duration = dib8000_get_symbol_duration(state); - break; +#endif + state->autosearch_state = AS_SEARCHING_FFT; + state->found_nfft = TRANSMISSION_MODE_AUTO; + state->found_guard = GUARD_INTERVAL_AUTO; + *tune_state = CT_DEMOD_SEARCH_NEXT; + } else { /* we already know the channel struct so TUNE only ! */ + state->autosearch_state = AS_DONE; + *tune_state = CT_DEMOD_STEP_3; + } + state->symbol_duration = dib8000_get_symbol_duration(state); + break; case CT_DEMOD_SEARCH_NEXT: /* 51 */ - dib8000_autosearch_start(fe); - if (state->revision == 0x8090) - ret = 50; - else - ret = 15; - *tune_state = CT_DEMOD_STEP_1; - break; + dib8000_autosearch_start(fe); + if (state->revision == 0x8090) + ret = 50; + else + ret = 15; + *tune_state = CT_DEMOD_STEP_1; + break; case CT_DEMOD_STEP_1: /* 31 */ - switch (dib8000_autosearch_irq(fe)) { - case 1: /* fail */ - state->status = FE_STATUS_TUNE_FAILED; - state->autosearch_state = AS_DONE; - *tune_state = CT_DEMOD_STOP; /* else we are done here */ - break; - case 2: /* Succes */ - state->status = FE_STATUS_FFT_SUCCESS; /* signal to the upper layer, that there was a channel found and the parameters can be read */ - *tune_state = CT_DEMOD_STEP_3; - if (state->autosearch_state == AS_SEARCHING_GUARD) - *tune_state = CT_DEMOD_STEP_2; - else - state->autosearch_state = AS_DONE; - break; - case 3: /* Autosearch FFT max correlation endded */ - *tune_state = CT_DEMOD_STEP_2; - break; - } + switch (dib8000_autosearch_irq(fe)) { + case 1: /* fail */ + state->status = FE_STATUS_TUNE_FAILED; + state->autosearch_state = AS_DONE; + *tune_state = CT_DEMOD_STOP; /* else we are done here */ + break; + case 2: /* Succes */ + state->status = FE_STATUS_FFT_SUCCESS; /* signal to the upper layer, that there was a channel found and the parameters can be read */ + *tune_state = CT_DEMOD_STEP_3; + if (state->autosearch_state == AS_SEARCHING_GUARD) + *tune_state = CT_DEMOD_STEP_2; + else + state->autosearch_state = AS_DONE; + break; + case 3: /* Autosearch FFT max correlation endded */ + *tune_state = CT_DEMOD_STEP_2; break; + } + break; case CT_DEMOD_STEP_2: - switch (state->autosearch_state) { - case AS_SEARCHING_FFT: - /* searching for the correct FFT */ - if (state->revision == 0x8090) { - corm[2] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597)); - corm[1] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599)); - corm[0] = (dib8000_read_word(state, 600) << 16) | (dib8000_read_word(state, 601)); - } else { - corm[2] = (dib8000_read_word(state, 594) << 16) | (dib8000_read_word(state, 595)); - corm[1] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597)); - corm[0] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599)); - } - /* dprintk("corm fft: %u %u %u", corm[0], corm[1], corm[2]); */ + switch (state->autosearch_state) { + case AS_SEARCHING_FFT: + /* searching for the correct FFT */ + if (state->revision == 0x8090) { + corm[2] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597)); + corm[1] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599)); + corm[0] = (dib8000_read_word(state, 600) << 16) | (dib8000_read_word(state, 601)); + } else { + corm[2] = (dib8000_read_word(state, 594) << 16) | (dib8000_read_word(state, 595)); + corm[1] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597)); + corm[0] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599)); + } + /* dprintk("corm fft: %u %u %u", corm[0], corm[1], corm[2]); */ - max_value = 0; - for (find_index = 1 ; find_index < 3 ; find_index++) { - if (corm[max_value] < corm[find_index]) - max_value = find_index ; - } + max_value = 0; + for (find_index = 1 ; find_index < 3 ; find_index++) { + if (corm[max_value] < corm[find_index]) + max_value = find_index ; + } - switch (max_value) { - case 0: - state->found_nfft = TRANSMISSION_MODE_2K; - break; - case 1: - state->found_nfft = TRANSMISSION_MODE_4K; - break; - case 2: - default: - state->found_nfft = TRANSMISSION_MODE_8K; - break; - } - /* dprintk("Autosearch FFT has found Mode %d", max_value + 1); */ - - *tune_state = CT_DEMOD_SEARCH_NEXT; - state->autosearch_state = AS_SEARCHING_GUARD; - if (state->revision == 0x8090) - ret = 50; - else - ret = 10; - break; - case AS_SEARCHING_GUARD: - /* searching for the correct guard interval */ - if (state->revision == 0x8090) - state->found_guard = dib8000_read_word(state, 572) & 0x3; - else - state->found_guard = dib8000_read_word(state, 570) & 0x3; - /* dprintk("guard interval found=%i", state->found_guard); */ - - *tune_state = CT_DEMOD_STEP_3; - break; + switch (max_value) { + case 0: + state->found_nfft = TRANSMISSION_MODE_2K; + break; + case 1: + state->found_nfft = TRANSMISSION_MODE_4K; + break; + case 2: default: - /* the demod should never be in this state */ - state->status = FE_STATUS_TUNE_FAILED; - state->autosearch_state = AS_DONE; - *tune_state = CT_DEMOD_STOP; /* else we are done here */ - break; + state->found_nfft = TRANSMISSION_MODE_8K; + break; } + /* dprintk("Autosearch FFT has found Mode %d", max_value + 1); */ + + *tune_state = CT_DEMOD_SEARCH_NEXT; + state->autosearch_state = AS_SEARCHING_GUARD; + if (state->revision == 0x8090) + ret = 50; + else + ret = 10; break; + case AS_SEARCHING_GUARD: + /* searching for the correct guard interval */ + if (state->revision == 0x8090) + state->found_guard = dib8000_read_word(state, 572) & 0x3; + else + state->found_guard = dib8000_read_word(state, 570) & 0x3; + /* dprintk("guard interval found=%i", state->found_guard); */ - case CT_DEMOD_STEP_3: /* 33 */ - state->symbol_duration = dib8000_get_symbol_duration(state); - dib8000_set_isdbt_loop_params(state, LOOP_TUNE_1); - dib8000_set_isdbt_common_channel(state, 0, 0);/* setting the known channel parameters here */ - *tune_state = CT_DEMOD_STEP_4; + *tune_state = CT_DEMOD_STEP_3; break; + default: + /* the demod should never be in this state */ + state->status = FE_STATUS_TUNE_FAILED; + state->autosearch_state = AS_DONE; + *tune_state = CT_DEMOD_STOP; /* else we are done here */ + break; + } + break; + + case CT_DEMOD_STEP_3: /* 33 */ + state->symbol_duration = dib8000_get_symbol_duration(state); + dib8000_set_isdbt_loop_params(state, LOOP_TUNE_1); + dib8000_set_isdbt_common_channel(state, 0, 0);/* setting the known channel parameters here */ + *tune_state = CT_DEMOD_STEP_4; + break; case CT_DEMOD_STEP_4: /* (34) */ - dib8000_demod_restart(state); + dib8000_demod_restart(state); - dib8000_set_sync_wait(state); - dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); + dib8000_set_sync_wait(state); + dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); - locks = (dib8000_read_word(state, 180) >> 6) & 0x3f; /* P_coff_winlen ? */ - /* coff should lock over P_coff_winlen ofdm symbols : give 3 times this length to lock */ - *timeout = dib8000_get_timeout(state, 2 * locks, SYMBOL_DEPENDENT_ON); - *tune_state = CT_DEMOD_STEP_5; - break; + locks = (dib8000_read_word(state, 180) >> 6) & 0x3f; /* P_coff_winlen ? */ + /* coff should lock over P_coff_winlen ofdm symbols : give 3 times this length to lock */ + *timeout = dib8000_get_timeout(state, 2 * locks, SYMBOL_DEPENDENT_ON); + *tune_state = CT_DEMOD_STEP_5; + break; case CT_DEMOD_STEP_5: /* (35) */ - locks = dib8000_read_lock(fe); - if (locks & (0x3 << 11)) { /* coff-lock and off_cpil_lock achieved */ - dib8000_update_timf(state); /* we achieved a coff_cpil_lock - it's time to update the timf */ - if (!state->differential_constellation) { - /* 2 times lmod4_win_len + 10 symbols (pipe delay after coff + nb to compute a 1st correlation) */ - *timeout = dib8000_get_timeout(state, (20 * ((dib8000_read_word(state, 188)>>5)&0x1f)), SYMBOL_DEPENDENT_ON); - *tune_state = CT_DEMOD_STEP_7; - } else { - *tune_state = CT_DEMOD_STEP_8; - } - } else if (now > *timeout) { - *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ + locks = dib8000_read_lock(fe); + if (locks & (0x3 << 11)) { /* coff-lock and off_cpil_lock achieved */ + dib8000_update_timf(state); /* we achieved a coff_cpil_lock - it's time to update the timf */ + if (!state->differential_constellation) { + /* 2 times lmod4_win_len + 10 symbols (pipe delay after coff + nb to compute a 1st correlation) */ + *timeout = dib8000_get_timeout(state, (20 * ((dib8000_read_word(state, 188)>>5)&0x1f)), SYMBOL_DEPENDENT_ON); + *tune_state = CT_DEMOD_STEP_7; + } else { + *tune_state = CT_DEMOD_STEP_8; } - break; + } else if (now > *timeout) { + *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ + } + break; case CT_DEMOD_STEP_6: /* (36) if there is an input (diversity) */ - if ((state->fe[1] != NULL) && (state->output_mode != OUTMODE_DIVERSITY)) { - /* if there is a diversity fe in input and this fe is has not already failled : wait here until this this fe has succedeed or failled */ - if (dib8000_get_status(state->fe[1]) <= FE_STATUS_STD_SUCCESS) /* Something is locked on the input fe */ - *tune_state = CT_DEMOD_STEP_8; /* go for mpeg */ - else if (dib8000_get_status(state->fe[1]) >= FE_STATUS_TUNE_TIME_TOO_SHORT) { /* fe in input failled also, break the current one */ - *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */ - dib8000_viterbi_state(state, 1); /* start viterbi chandec */ - dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); - state->status = FE_STATUS_TUNE_FAILED; - } - } else { + if ((state->fe[1] != NULL) && (state->output_mode != OUTMODE_DIVERSITY)) { + /* if there is a diversity fe in input and this fe is has not already failled : wait here until this this fe has succedeed or failled */ + if (dib8000_get_status(state->fe[1]) <= FE_STATUS_STD_SUCCESS) /* Something is locked on the input fe */ + *tune_state = CT_DEMOD_STEP_8; /* go for mpeg */ + else if (dib8000_get_status(state->fe[1]) >= FE_STATUS_TUNE_TIME_TOO_SHORT) { /* fe in input failled also, break the current one */ + *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */ dib8000_viterbi_state(state, 1); /* start viterbi chandec */ dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); - *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */ state->status = FE_STATUS_TUNE_FAILED; } - break; + } else { + dib8000_viterbi_state(state, 1); /* start viterbi chandec */ + dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); + *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */ + state->status = FE_STATUS_TUNE_FAILED; + } + break; case CT_DEMOD_STEP_7: /* 37 */ - locks = dib8000_read_lock(fe); - if (locks & (1<<10)) { /* lmod4_lock */ - ret = 14; /* wait for 14 symbols */ - *tune_state = CT_DEMOD_STEP_8; - } else if (now > *timeout) - *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ - break; + locks = dib8000_read_lock(fe); + if (locks & (1<<10)) { /* lmod4_lock */ + ret = 14; /* wait for 14 symbols */ + *tune_state = CT_DEMOD_STEP_8; + } else if (now > *timeout) + *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ + break; case CT_DEMOD_STEP_8: /* 38 */ - dib8000_viterbi_state(state, 1); /* start viterbi chandec */ - dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); - - /* mpeg will never lock on this condition because init_prbs is not set : search for it !*/ - if (c->isdbt_sb_mode - && c->isdbt_sb_subchannel < 14 - && !state->differential_constellation) { - state->subchannel = 0; - *tune_state = CT_DEMOD_STEP_11; - } else { - *tune_state = CT_DEMOD_STEP_9; - state->status = FE_STATUS_LOCKED; - } - break; + dib8000_viterbi_state(state, 1); /* start viterbi chandec */ + dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); + + /* mpeg will never lock on this condition because init_prbs is not set : search for it !*/ + if (c->isdbt_sb_mode + && c->isdbt_sb_subchannel < 14 + && !state->differential_constellation) { + state->subchannel = 0; + *tune_state = CT_DEMOD_STEP_11; + } else { + *tune_state = CT_DEMOD_STEP_9; + state->status = FE_STATUS_LOCKED; + } + break; case CT_DEMOD_STEP_9: /* 39 */ - if ((state->revision == 0x8090) || ((dib8000_read_word(state, 1291) >> 9) & 0x1)) { /* fe capable of deinterleaving : esram */ - /* defines timeout for mpeg lock depending on interleaver length of longest layer */ - for (i = 0; i < 3; i++) { - if (c->layer[i].interleaving >= deeper_interleaver) { - dprintk("layer%i: time interleaver = %d ", i, c->layer[i].interleaving); - if (c->layer[i].segment_count > 0) { /* valid layer */ - deeper_interleaver = c->layer[0].interleaving; - state->longest_intlv_layer = i; - } + if ((state->revision == 0x8090) || ((dib8000_read_word(state, 1291) >> 9) & 0x1)) { /* fe capable of deinterleaving : esram */ + /* defines timeout for mpeg lock depending on interleaver length of longest layer */ + for (i = 0; i < 3; i++) { + if (c->layer[i].interleaving >= deeper_interleaver) { + dprintk("layer%i: time interleaver = %d ", i, c->layer[i].interleaving); + if (c->layer[i].segment_count > 0) { /* valid layer */ + deeper_interleaver = c->layer[0].interleaving; + state->longest_intlv_layer = i; } } + } - if (deeper_interleaver == 0) - locks = 2; /* locks is the tmp local variable name */ - else if (deeper_interleaver == 3) - locks = 8; - else - locks = 2 * deeper_interleaver; + if (deeper_interleaver == 0) + locks = 2; /* locks is the tmp local variable name */ + else if (deeper_interleaver == 3) + locks = 8; + else + locks = 2 * deeper_interleaver; - if (state->diversity_onoff != 0) /* because of diversity sync */ - locks *= 2; + if (state->diversity_onoff != 0) /* because of diversity sync */ + locks *= 2; - *timeout = now + (2000 * locks); /* give the mpeg lock 800ms if sram is present */ - dprintk("Deeper interleaver mode = %d on layer %d : timeout mult factor = %d => will use timeout = %d", deeper_interleaver, state->longest_intlv_layer, locks, *timeout); + *timeout = now + (2000 * locks); /* give the mpeg lock 800ms if sram is present */ + dprintk("Deeper interleaver mode = %d on layer %d : timeout mult factor = %d => will use timeout = %d", deeper_interleaver, state->longest_intlv_layer, locks, *timeout); - *tune_state = CT_DEMOD_STEP_10; - } else - *tune_state = CT_DEMOD_STOP; - break; + *tune_state = CT_DEMOD_STEP_10; + } else + *tune_state = CT_DEMOD_STOP; + break; case CT_DEMOD_STEP_10: /* 40 */ - locks = dib8000_read_lock(fe); - if (locks&(1<<(7-state->longest_intlv_layer))) { /* mpeg lock : check the longest one */ - dprintk("Mpeg locks [ L0 : %d | L1 : %d | L2 : %d ]", (locks>>7)&0x1, (locks>>6)&0x1, (locks>>5)&0x1); - if (c->isdbt_sb_mode - && c->isdbt_sb_subchannel < 14 - && !state->differential_constellation) - /* signal to the upper layer, that there was a channel found and the parameters can be read */ - state->status = FE_STATUS_DEMOD_SUCCESS; - else + locks = dib8000_read_lock(fe); + if (locks&(1<<(7-state->longest_intlv_layer))) { /* mpeg lock : check the longest one */ + dprintk("Mpeg locks [ L0 : %d | L1 : %d | L2 : %d ]", (locks>>7)&0x1, (locks>>6)&0x1, (locks>>5)&0x1); + if (c->isdbt_sb_mode + && c->isdbt_sb_subchannel < 14 + && !state->differential_constellation) + /* signal to the upper layer, that there was a channel found and the parameters can be read */ + state->status = FE_STATUS_DEMOD_SUCCESS; + else + state->status = FE_STATUS_DATA_LOCKED; + *tune_state = CT_DEMOD_STOP; + } else if (now > *timeout) { + if (c->isdbt_sb_mode + && c->isdbt_sb_subchannel < 14 + && !state->differential_constellation) { /* continue to try init prbs autosearch */ + state->subchannel += 3; + *tune_state = CT_DEMOD_STEP_11; + } else { /* we are done mpeg of the longest interleaver xas not locking but let's try if an other layer has locked in the same time */ + if (locks & (0x7<<5)) { + dprintk("Mpeg locks [ L0 : %d | L1 : %d | L2 : %d ]", (locks>>7)&0x1, (locks>>6)&0x1, (locks>>5)&0x1); state->status = FE_STATUS_DATA_LOCKED; + } else + state->status = FE_STATUS_TUNE_FAILED; *tune_state = CT_DEMOD_STOP; - } else if (now > *timeout) { - if (c->isdbt_sb_mode - && c->isdbt_sb_subchannel < 14 - && !state->differential_constellation) { /* continue to try init prbs autosearch */ - state->subchannel += 3; - *tune_state = CT_DEMOD_STEP_11; - } else { /* we are done mpeg of the longest interleaver xas not locking but let's try if an other layer has locked in the same time */ - if (locks & (0x7<<5)) { - dprintk("Mpeg locks [ L0 : %d | L1 : %d | L2 : %d ]", (locks>>7)&0x1, (locks>>6)&0x1, (locks>>5)&0x1); - state->status = FE_STATUS_DATA_LOCKED; - } else - state->status = FE_STATUS_TUNE_FAILED; - *tune_state = CT_DEMOD_STOP; - } } - break; + } + break; case CT_DEMOD_STEP_11: /* 41 : init prbs autosearch */ - if (state->subchannel <= 41) { - dib8000_set_subchannel_prbs(state, dib8000_get_init_prbs(state, state->subchannel)); - *tune_state = CT_DEMOD_STEP_9; - } else { - *tune_state = CT_DEMOD_STOP; - state->status = FE_STATUS_TUNE_FAILED; - } - break; + if (state->subchannel <= 41) { + dib8000_set_subchannel_prbs(state, dib8000_get_init_prbs(state, state->subchannel)); + *tune_state = CT_DEMOD_STEP_9; + } else { + *tune_state = CT_DEMOD_STOP; + state->status = FE_STATUS_TUNE_FAILED; + } + break; default: - break; + break; } /* tuning is finished - cleanup the demod */ switch (*tune_state) { case CT_DEMOD_STOP: /* (42) */ #ifdef DIB8000_AGC_FREEZE - if ((state->revision != 0x8090) && (state->agc1_max != 0)) { - dib8000_write_word(state, 108, state->agc1_max); - dib8000_write_word(state, 109, state->agc1_min); - dib8000_write_word(state, 110, state->agc2_max); - dib8000_write_word(state, 111, state->agc2_min); - state->agc1_max = 0; - state->agc1_min = 0; - state->agc2_max = 0; - state->agc2_min = 0; - } + if ((state->revision != 0x8090) && (state->agc1_max != 0)) { + dib8000_write_word(state, 108, state->agc1_max); + dib8000_write_word(state, 109, state->agc1_min); + dib8000_write_word(state, 110, state->agc2_max); + dib8000_write_word(state, 111, state->agc2_min); + state->agc1_max = 0; + state->agc1_min = 0; + state->agc2_max = 0; + state->agc2_min = 0; + } #endif - ret = FE_CALLBACK_TIME_NEVER; - break; + ret = FE_CALLBACK_TIME_NEVER; + break; default: - break; + break; } if ((ret > 0) && (*tune_state > CT_DEMOD_STEP_3)) -- cgit v0.10.2 From 7fec1c80a78eefd898d09e6b6732e05eb1b80977 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:30 -0300 Subject: [media] dib8000: Fix: add missing 4K FFT mode Without that, tuning may fail on 4K FFT mode, as the transmission parameter cache will be initialized with a wrong value. Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 43d1261..d4f37f5 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3432,6 +3432,9 @@ static int dib8000_get_frontend(struct dvb_frontend *fe) case 1: fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K; break; + case 2: + fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_4K; + break; case 3: default: fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; -- cgit v0.10.2 From 08623517f4fd6164c61a0c0d73c82e8e136a8b17 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:31 -0300 Subject: [media] dib8000: remove a double call for dib8000_get_symbol_duration() The symbol duration was already obtained at CT_DEMOD_START. No need to do it again at CT_DEMOD_STEP_3. Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index d4f37f5..7fea047 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3154,7 +3154,6 @@ static int dib8000_tune(struct dvb_frontend *fe) break; case CT_DEMOD_STEP_3: /* 33 */ - state->symbol_duration = dib8000_get_symbol_duration(state); dib8000_set_isdbt_loop_params(state, LOOP_TUNE_1); dib8000_set_isdbt_common_channel(state, 0, 0);/* setting the known channel parameters here */ *tune_state = CT_DEMOD_STEP_4; -- cgit v0.10.2 From dde8e115365c32bc8074aaa3ca492db1eaa913be Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:32 -0300 Subject: [media] dib8000: In auto-search, try first with partial reception enabled TV broadcasters generally use partial reception. So, enable it by default in auto-search mode. The driver will latter detect if the transmission is on some other mode. Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 7fea047..7088940 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -2351,6 +2351,9 @@ static void dib8000_set_isdbt_common_channel(struct dib8000_state *state, u8 seq int init_prbs; struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; + if (autosearching) + c->isdbt_partial_reception = 1; + /* P_mode */ dib8000_write_word(state, 10, (seq << 4)); -- cgit v0.10.2 From 6d38454a59a01ce24f93c990a08cac0c0d4df2c8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:33 -0300 Subject: [media] dib8000: Restart sad during dib8000_reset Just like the Windows driver, restart SAD during reset Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 7088940..5d1e141 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -1050,6 +1050,7 @@ static int dib8000_reset(struct dvb_frontend *fe) dib8000_write_word(state, 770, 0xffff); dib8000_write_word(state, 771, 0xffff); dib8000_write_word(state, 772, 0xfffc); + dib8000_write_word(state, 898, 0x000c); /* restart sad */ if (state->revision == 0x8090) dib8000_write_word(state, 1280, 0x0045); else -- cgit v0.10.2 From 40d1a7c3ded03347336fbd33234af89aaf5ffdca Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:34 -0300 Subject: [media] dib0700: better document struct init Instead of using anonymous initialization for dib0896 structs, identify each field by name. That helps to understand what's being initialized. No functional changes. Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index d067bb7..501947e 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -1412,99 +1412,113 @@ static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap) /* STK8096GP */ static struct dibx000_agc_config dib8090_agc_config[2] = { { - BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, + .band_caps = BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ - (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), - 787, - 10, + .inv_gain = 787, + .time_stabiliz = 10, - 0, - 118, + .alpha_level = 0, + .thlock = 118, - 0, - 3530, - 1, - 5, + .wbd_inv = 0, + .wbd_ref = 3530, + .wbd_sel = 1, + .wbd_alpha = 5, - 65535, - 0, + .agc1_max = 65535, + .agc1_min = 0, - 65535, - 0, - - 0, - 32, - 114, - 143, - 144, - 114, - 227, - 116, - 117, - - 28, - 26, - 31, - 51, + .agc2_max = 65535, + .agc2_min = 0, - 0, + .agc1_pt1 = 0, + .agc1_pt2 = 32, + .agc1_pt3 = 114, + .agc1_slope1 = 143, + .agc1_slope2 = 144, + .agc2_pt1 = 114, + .agc2_pt2 = 227, + .agc2_slope1 = 116, + .agc2_slope2 = 117, + + .alpha_mant = 28, + .alpha_exp = 26, + .beta_mant = 31, + .beta_exp = 51, + + .perform_agc_softsplit = 0, }, { - BAND_CBAND, + .band_caps = BAND_CBAND, /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ - (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), - 787, - 10, + .inv_gain = 787, + .time_stabiliz = 10, - 0, - 118, + .alpha_level = 0, + .thlock = 118, - 0, - 3530, - 1, - 5, - - 0, - 0, - - 65535, - 0, + .wbd_inv = 0, + .wbd_ref = 3530, + .wbd_sel = 1, + .wbd_alpha = 5, - 0, - 32, - 114, - 143, - 144, - 114, - 227, - 116, - 117, + .agc1_max = 0, + .agc1_min = 0, - 28, - 26, - 31, - 51, + .agc2_max = 65535, + .agc2_min = 0, - 0, + .agc1_pt1 = 0, + .agc1_pt2 = 32, + .agc1_pt3 = 114, + .agc1_slope1 = 143, + .agc1_slope2 = 144, + .agc2_pt1 = 114, + .agc2_pt2 = 227, + .agc2_slope1 = 116, + .agc2_slope2 = 117, + + .alpha_mant = 28, + .alpha_exp = 26, + .beta_mant = 31, + .beta_exp = 51, + + .perform_agc_softsplit = 0, } }; static struct dibx000_bandwidth_config dib8090_pll_config_12mhz = { - 54000, 13500, - 1, 18, 3, 1, 0, - 0, 0, 1, 1, 2, - (3 << 14) | (1 << 12) | (599 << 0), - (0 << 25) | 0, - 20199727, - 12000000, + .internal = 54000, + .sampling = 13500, + + .pll_prediv = 1, + .pll_ratio = 18, + .pll_range = 3, + .pll_reset = 1, + .pll_bypass = 0, + + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 1, + .ADClkSrc = 1, + .modulo = 2, + + .sad_cfg = (3 << 14) | (1 << 12) | (599 << 0), + + .ifreq = (0 << 25) | 0, + .timf = 20199727, + + .xtal_hz = 12000000, }; static int dib8090_get_adc_power(struct dvb_frontend *fe) -- cgit v0.10.2 From 4607bb7a472482ed985bf0a8d5a288ecd3a04ec8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:35 -0300 Subject: [media] dib8000: Fix the sleep time at the state machine msleep() is not too precise: its precision depends on the HZ config. As the driver selects precise timings for the state machine, change it to usleep_range(). Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 5d1e141..91bdecc 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3606,10 +3606,19 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) else if ((time_slave != FE_CALLBACK_TIME_NEVER) && (time_slave > time)) time = time_slave; } - if (time != FE_CALLBACK_TIME_NEVER) - msleep(time / 10); - else + if (time == FE_CALLBACK_TIME_NEVER) break; + + /* + * Despite dib8000_agc_startup returns time at a 0.1 ms range, + * the actual sleep time depends on CONFIG_HZ. The worse case + * is when CONFIG_HZ=100. In such case, the minimum granularity + * is 10ms. On some real field tests, the tuner sometimes don't + * lock when this timer is lower than 10ms. So, enforce a 10ms + * granularity. + */ + time = 10 * (time + 99)/100; + usleep_range(time * 1000, (time + 1) * 1000); exit_condition = 1; for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_AGC_STOP) { -- cgit v0.10.2 From 0de04ca1dc0bd38fd7b88871ab9a17e2d8dbd0db Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:36 -0300 Subject: [media] dib0090: Fix the sleep time at the state machine msleep() is not too precise: its precision depends on the HZ config. As the driver selects precise timings for the state machine, change it to usleep_range(). Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c index 3ee22ff..68e2af2 100644 --- a/drivers/media/dvb-frontends/dib0090.c +++ b/drivers/media/dvb-frontends/dib0090.c @@ -2557,10 +2557,19 @@ static int dib0090_set_params(struct dvb_frontend *fe) do { ret = dib0090_tune(fe); - if (ret != FE_CALLBACK_TIME_NEVER) - msleep(ret / 10); - else + if (ret == FE_CALLBACK_TIME_NEVER) break; + + /* + * Despite dib0090_tune returns time at a 0.1 ms range, + * the actual sleep time depends on CONFIG_HZ. The worse case + * is when CONFIG_HZ=100. In such case, the minimum granularity + * is 10ms. On some real field tests, the tuner sometimes don't + * lock when this timer is lower than 10ms. So, enforce a 10ms + * granularity and use usleep_range() instead of msleep(). + */ + ret = 10 * (ret + 99)/100; + usleep_range(ret * 1000, (ret + 1) * 1000); } while (state->tune_state != CT_TUNER_STOP); return 0; -- cgit v0.10.2 From d6c62b7638d22c978174973b487ef81ac9082d08 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:37 -0300 Subject: [media] dib8000: use jifies instead of current_kernel_time() Instead of doing the tuning delays and timeouts using current_kernel_time(), use jiffies. That consumes less CPU cycles, and it is monotonic. Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 91bdecc..6419ae6 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -115,7 +115,7 @@ struct dib8000_state { u16 found_guard; u8 subchannel; u8 symbol_duration; - u32 timeout; + unsigned long timeout; u8 longest_intlv_layer; u16 output_mode; @@ -1237,7 +1237,7 @@ static int dib8000_agc_soft_split(struct dib8000_state *state) u16 agc, split_offset; if (!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0) - return FE_CALLBACK_TIME_NEVER; + return 0; // n_agc_global agc = dib8000_read_word(state, 390); @@ -2849,12 +2849,12 @@ static void dib8000_set_sync_wait(struct dib8000_state *state) dib8000_write_word(state, 273, (dib8000_read_word(state, 273) & 0x000f) | (sync_wait << 4)); } -static u32 dib8000_get_timeout(struct dib8000_state *state, u32 delay, enum timeout_mode mode) +static unsigned long dib8000_get_timeout(struct dib8000_state *state, u32 delay, enum timeout_mode mode) { if (mode == SYMBOL_DEPENDENT_ON) - return systime() + (delay * state->symbol_duration); - else - return systime() + delay; + delay *= state->symbol_duration; + + return jiffies + usecs_to_jiffies(delay * 100); } static s32 dib8000_get_status(struct dvb_frontend *fe) @@ -3006,8 +3006,8 @@ static int dib8000_tune(struct dvb_frontend *fe) u16 locks, deeper_interleaver = 0, i; int ret = 1; /* 1 symbol duration (in 100us unit) delay most of the time */ - u32 *timeout = &state->timeout; - u32 now = systime(); + unsigned long *timeout = &state->timeout; + unsigned long now = jiffies; #ifdef DIB8000_AGC_FREEZE u16 agc1, agc2; #endif @@ -3017,7 +3017,8 @@ static int dib8000_tune(struct dvb_frontend *fe) #if 0 if (*tune_state < CT_DEMOD_STOP) - dprintk("IN: context status = %d, TUNE_STATE %d autosearch step = %u systime = %u", state->channel_parameters_set, *tune_state, state->autosearch_state, now); + dprintk("IN: context status = %d, TUNE_STATE %d autosearch step = %u jiffies = %lu", + state->channel_parameters_set, *tune_state, state->autosearch_state, now); #endif switch (*tune_state) { @@ -3186,7 +3187,7 @@ static int dib8000_tune(struct dvb_frontend *fe) } else { *tune_state = CT_DEMOD_STEP_8; } - } else if (now > *timeout) { + } else if (time_after(now, *timeout)) { *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ } break; @@ -3215,7 +3216,7 @@ static int dib8000_tune(struct dvb_frontend *fe) if (locks & (1<<10)) { /* lmod4_lock */ ret = 14; /* wait for 14 symbols */ *tune_state = CT_DEMOD_STEP_8; - } else if (now > *timeout) + } else if (time_after(now, *timeout)) *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ break; @@ -3258,8 +3259,9 @@ static int dib8000_tune(struct dvb_frontend *fe) if (state->diversity_onoff != 0) /* because of diversity sync */ locks *= 2; - *timeout = now + (2000 * locks); /* give the mpeg lock 800ms if sram is present */ - dprintk("Deeper interleaver mode = %d on layer %d : timeout mult factor = %d => will use timeout = %d", deeper_interleaver, state->longest_intlv_layer, locks, *timeout); + *timeout = now + msecs_to_jiffies(200 * locks); /* give the mpeg lock 800ms if sram is present */ + dprintk("Deeper interleaver mode = %d on layer %d : timeout mult factor = %d => will use timeout = %ld", + deeper_interleaver, state->longest_intlv_layer, locks, *timeout); *tune_state = CT_DEMOD_STEP_10; } else @@ -3278,7 +3280,7 @@ static int dib8000_tune(struct dvb_frontend *fe) else state->status = FE_STATUS_DATA_LOCKED; *tune_state = CT_DEMOD_STOP; - } else if (now > *timeout) { + } else if (time_after(now, *timeout)) { if (c->isdbt_sb_mode && c->isdbt_sb_subchannel < 14 && !state->differential_constellation) { /* continue to try init prbs autosearch */ @@ -3324,7 +3326,7 @@ static int dib8000_tune(struct dvb_frontend *fe) state->agc2_min = 0; } #endif - ret = FE_CALLBACK_TIME_NEVER; + ret = 0; break; default: break; @@ -3547,9 +3549,9 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; - int l, i, active, time, time_slave = FE_CALLBACK_TIME_NEVER; + int l, i, active, time, time_slave = 0; u8 exit_condition, index_frontend; - u32 delay, callback_time; + unsigned long delay, callback_time; if (c->frequency == 0) { dprintk("dib8000: must at least specify frequency "); @@ -3601,12 +3603,12 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) time = dib8000_agc_startup(state->fe[0]); for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { time_slave = dib8000_agc_startup(state->fe[index_frontend]); - if (time == FE_CALLBACK_TIME_NEVER) + if (time == 0) time = time_slave; - else if ((time_slave != FE_CALLBACK_TIME_NEVER) && (time_slave > time)) + else if ((time_slave != 0) && (time_slave > time)) time = time_slave; } - if (time == FE_CALLBACK_TIME_NEVER) + if (time == 0) break; /* @@ -3633,11 +3635,14 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) active = 1; do { - callback_time = FE_CALLBACK_TIME_NEVER; + callback_time = 0; for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { delay = dib8000_tune(state->fe[index_frontend]); - if (delay != FE_CALLBACK_TIME_NEVER) - delay += systime(); + if (delay != 0) { + delay = jiffies + usecs_to_jiffies(100 * delay); + if (!callback_time || delay < callback_time) + callback_time = delay; + } /* we are in autosearch */ if (state->channel_parameters_set == 0) { /* searching */ @@ -3666,8 +3671,6 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) } } } - if (delay < callback_time) - callback_time = delay; } /* tuning is done when the master frontend is done (failed or success) */ if (dib8000_get_status(state->fe[0]) == FE_STATUS_TUNE_FAILED || @@ -3683,12 +3686,12 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) dprintk("tuning done with status %d", dib8000_get_status(state->fe[0])); } - if ((active == 1) && (callback_time == FE_CALLBACK_TIME_NEVER)) { + if ((active == 1) && (callback_time == 0)) { dprintk("strange callback time something went wrong"); active = 0; } - while ((active == 1) && (systime() < callback_time)) + while ((active == 1) && (time_before(jiffies, callback_time))) msleep(100); } while (active); -- cgit v0.10.2 From a768f90ef0a214de2dfcce5ebf2bed93b2c4c015 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:38 -0300 Subject: [media] dib8000: Update the ADC gain table This table doesn't match the new one. Update it. Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 6419ae6..637e96a 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -1980,18 +1980,9 @@ static u32 dib8000_ctrl_timf(struct dvb_frontend *fe, uint8_t op, uint32_t timf) } static const u16 adc_target_16dB[11] = { - (1 << 13) - 825 - 117, - (1 << 13) - 837 - 117, - (1 << 13) - 811 - 117, - (1 << 13) - 766 - 117, - (1 << 13) - 737 - 117, - (1 << 13) - 693 - 117, - (1 << 13) - 648 - 117, - (1 << 13) - 619 - 117, - (1 << 13) - 575 - 117, - (1 << 13) - 531 - 117, - (1 << 13) - 501 - 117 + 7250, 7238, 7264, 7309, 7338, 7382, 7427, 7456, 7500, 7544, 7574 }; + static const u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 }; static u16 dib8000_set_layer(struct dib8000_state *state, u8 layer_index, u16 max_constellation) -- cgit v0.10.2 From ecc31d557e0b5fd44d68733188290f4fa56d7db8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:39 -0300 Subject: [media] dib8000: improve debug messages When debug is enabled: - Report when frontend gets restarted; - Be coherent on the displayed lines; - Show the transmission mode; - Hide unused layers. No functional changes (except at the printk's). Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 637e96a..6221482 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3391,7 +3391,7 @@ static int dib8000_get_frontend(struct dvb_frontend *fe) if (!(stat & FE_HAS_SYNC)) return 0; - dprintk("TMCC lock"); + dprintk("dib8000_get_frontend: TMCC lock"); for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat); if (stat&FE_HAS_SYNC) { @@ -3427,94 +3427,117 @@ static int dib8000_get_frontend(struct dvb_frontend *fe) switch ((val & 0x30) >> 4) { case 1: fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K; + dprintk("dib8000_get_frontend: transmission mode 2K"); break; case 2: fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_4K; + dprintk("dib8000_get_frontend: transmission mode 4K"); break; case 3: default: fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + dprintk("dib8000_get_frontend: transmission mode 8K"); break; } switch (val & 0x3) { case 0: fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32; - dprintk("dib8000_get_frontend GI = 1/32 "); + dprintk("dib8000_get_frontend: Guard Interval = 1/32 "); break; case 1: fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16; - dprintk("dib8000_get_frontend GI = 1/16 "); + dprintk("dib8000_get_frontend: Guard Interval = 1/16 "); break; case 2: - dprintk("dib8000_get_frontend GI = 1/8 "); + dprintk("dib8000_get_frontend: Guard Interval = 1/8 "); fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; break; case 3: - dprintk("dib8000_get_frontend GI = 1/4 "); + dprintk("dib8000_get_frontend: Guard Interval = 1/4 "); fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4; break; } val = dib8000_read_word(state, 505); fe->dtv_property_cache.isdbt_partial_reception = val & 1; - dprintk("dib8000_get_frontend : partial_reception = %d ", fe->dtv_property_cache.isdbt_partial_reception); + dprintk("dib8000_get_frontend: partial_reception = %d ", fe->dtv_property_cache.isdbt_partial_reception); for (i = 0; i < 3; i++) { - val = dib8000_read_word(state, 493 + i); - fe->dtv_property_cache.layer[i].segment_count = val & 0x0F; - dprintk("dib8000_get_frontend : Layer %d segments = %d ", i, fe->dtv_property_cache.layer[i].segment_count); + int show; + + val = dib8000_read_word(state, 493 + i) & 0x0f; + fe->dtv_property_cache.layer[i].segment_count = val; + + if (val == 0 || val > 13) + show = 0; + else + show = 1; + + if (show) + dprintk("dib8000_get_frontend: Layer %d segments = %d ", + i, fe->dtv_property_cache.layer[i].segment_count); val = dib8000_read_word(state, 499 + i) & 0x3; /* Interleaving can be 0, 1, 2 or 4 */ if (val == 3) val = 4; fe->dtv_property_cache.layer[i].interleaving = val; - dprintk("dib8000_get_frontend : Layer %d time_intlv = %d ", - i, fe->dtv_property_cache.layer[i].interleaving); + if (show) + dprintk("dib8000_get_frontend: Layer %d time_intlv = %d ", + i, fe->dtv_property_cache.layer[i].interleaving); val = dib8000_read_word(state, 481 + i); switch (val & 0x7) { case 1: fe->dtv_property_cache.layer[i].fec = FEC_1_2; - dprintk("dib8000_get_frontend : Layer %d Code Rate = 1/2 ", i); + if (show) + dprintk("dib8000_get_frontend: Layer %d Code Rate = 1/2 ", i); break; case 2: fe->dtv_property_cache.layer[i].fec = FEC_2_3; - dprintk("dib8000_get_frontend : Layer %d Code Rate = 2/3 ", i); + if (show) + dprintk("dib8000_get_frontend: Layer %d Code Rate = 2/3 ", i); break; case 3: fe->dtv_property_cache.layer[i].fec = FEC_3_4; - dprintk("dib8000_get_frontend : Layer %d Code Rate = 3/4 ", i); + if (show) + dprintk("dib8000_get_frontend: Layer %d Code Rate = 3/4 ", i); break; case 5: fe->dtv_property_cache.layer[i].fec = FEC_5_6; - dprintk("dib8000_get_frontend : Layer %d Code Rate = 5/6 ", i); + if (show) + dprintk("dib8000_get_frontend: Layer %d Code Rate = 5/6 ", i); break; default: fe->dtv_property_cache.layer[i].fec = FEC_7_8; - dprintk("dib8000_get_frontend : Layer %d Code Rate = 7/8 ", i); + if (show) + dprintk("dib8000_get_frontend: Layer %d Code Rate = 7/8 ", i); break; } val = dib8000_read_word(state, 487 + i); switch (val & 0x3) { case 0: - dprintk("dib8000_get_frontend : Layer %d DQPSK ", i); fe->dtv_property_cache.layer[i].modulation = DQPSK; + if (show) + dprintk("dib8000_get_frontend: Layer %d DQPSK ", i); break; case 1: fe->dtv_property_cache.layer[i].modulation = QPSK; - dprintk("dib8000_get_frontend : Layer %d QPSK ", i); + if (show) + dprintk("dib8000_get_frontend: Layer %d QPSK ", i); break; case 2: fe->dtv_property_cache.layer[i].modulation = QAM_16; - dprintk("dib8000_get_frontend : Layer %d QAM16 ", i); + if (show) + dprintk("dib8000_get_frontend: Layer %d QAM16 ", i); break; case 3: default: - dprintk("dib8000_get_frontend : Layer %d QAM64 ", i); fe->dtv_property_cache.layer[i].modulation = QAM_64; + if (show) + dprintk("dib8000_get_frontend: Layer %d QAM64 ", i); break; } } @@ -3644,6 +3667,7 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) for (l = 0; (l < MAX_NUMBER_OF_FRONTENDS) && (state->fe[l] != NULL); l++) { if (l != index_frontend) { /* and for all frontend except the successful one */ + dprintk("Restarting frontend %d\n", l); dib8000_tune_restart_from_demod(state->fe[l]); state->fe[l]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode; -- cgit v0.10.2 From 3c0d394ea7022bb9666d9df97a5776c4bcc3045c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jul 2014 14:15:40 -0300 Subject: [media] dib8000: improve the message that reports per-layer locks The message is currently highly encoded: [70299.863521] DiB8000: Mpeg locks [ L0 : 0 | L1 : 1 | L2 : 0 ] And doesn't properly reflect that some problems might have happened. Instead, display it as: [75160.822321] DiB8000: Not all ISDB-T layers locked in 32 ms: Layer A NOT LOCKED, Layer B locked, Layer C not enabled In order to better reflect what's happening with layer locking. Acked-By: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 6221482..61e31f2 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3262,7 +3262,10 @@ static int dib8000_tune(struct dvb_frontend *fe) case CT_DEMOD_STEP_10: /* 40 */ locks = dib8000_read_lock(fe); if (locks&(1<<(7-state->longest_intlv_layer))) { /* mpeg lock : check the longest one */ - dprintk("Mpeg locks [ L0 : %d | L1 : %d | L2 : %d ]", (locks>>7)&0x1, (locks>>6)&0x1, (locks>>5)&0x1); + dprintk("ISDB-T layer locks: Layer A %s, Layer B %s, Layer C %s", + c->layer[0].segment_count ? (locks >> 7) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled", + c->layer[1].segment_count ? (locks >> 6) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled", + c->layer[2].segment_count ? (locks >> 5) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled"); if (c->isdbt_sb_mode && c->isdbt_sb_subchannel < 14 && !state->differential_constellation) @@ -3278,8 +3281,13 @@ static int dib8000_tune(struct dvb_frontend *fe) state->subchannel += 3; *tune_state = CT_DEMOD_STEP_11; } else { /* we are done mpeg of the longest interleaver xas not locking but let's try if an other layer has locked in the same time */ - if (locks & (0x7<<5)) { - dprintk("Mpeg locks [ L0 : %d | L1 : %d | L2 : %d ]", (locks>>7)&0x1, (locks>>6)&0x1, (locks>>5)&0x1); + if (locks & (0x7 << 5)) { + dprintk("Not all ISDB-T layers locked in %d ms: Layer A %s, Layer B %s, Layer C %s", + jiffies_to_msecs(now - *timeout), + c->layer[0].segment_count ? (locks >> 7) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled", + c->layer[1].segment_count ? (locks >> 6) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled", + c->layer[2].segment_count ? (locks >> 5) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled"); + state->status = FE_STATUS_DATA_LOCKED; } else state->status = FE_STATUS_TUNE_FAILED; -- cgit v0.10.2 From a83d7d17ad4c920b9b3f5330521fa346dddd3de3 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 9 Jul 2014 18:37:30 -0300 Subject: [media] si2157: implement sleep Implement sleep for power-management. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 271a752..3f88e53 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -89,12 +89,23 @@ static int si2157_init(struct dvb_frontend *fe) static int si2157_sleep(struct dvb_frontend *fe) { struct si2157 *s = fe->tuner_priv; + int ret; + struct si2157_cmd cmd; dev_dbg(&s->client->dev, "%s:\n", __func__); s->active = false; + memcpy(cmd.args, "\x13", 1); + cmd.len = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + return 0; +err: + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; } static int si2157_set_params(struct dvb_frontend *fe) -- cgit v0.10.2 From 4de0ed7cf7a49384f3438a39543fb85d6dcd3bfc Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 9 Jul 2014 18:45:31 -0300 Subject: [media] si2168: implement sleep Implement sleep for power-management. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 8637d2e..0d0545e 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -438,13 +438,6 @@ static int si2168_init(struct dvb_frontend *fe) dev_dbg(&s->client->dev, "%s:\n", __func__); - cmd.args[0] = 0x13; - cmd.wlen = 1; - cmd.rlen = 0; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - cmd.args[0] = 0xc0; cmd.args[1] = 0x12; cmd.args[2] = 0x00; @@ -559,12 +552,24 @@ err: static int si2168_sleep(struct dvb_frontend *fe) { struct si2168 *s = fe->demodulator_priv; + int ret; + struct si2168_cmd cmd; dev_dbg(&s->client->dev, "%s:\n", __func__); s->active = false; + memcpy(cmd.args, "\x13", 1); + cmd.wlen = 1; + cmd.rlen = 0; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + return 0; +err: + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; } static int si2168_get_tune_settings(struct dvb_frontend *fe, -- cgit v0.10.2 From 888680ff9d2690b64ef7ee3b53d4169a7c4d31b6 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 9 Jul 2014 19:36:58 -0300 Subject: [media] si2168: set cmd args using memcpy Use memcpy for set cmd buffer in order to keep style in line with rest of file. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 0d0545e..dc8ec9e 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -95,20 +95,17 @@ static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) switch (c->delivery_system) { case SYS_DVBT: - cmd.args[0] = 0xa0; - cmd.args[1] = 0x01; + memcpy(cmd.args, "\xa0\x01", 2); cmd.wlen = 2; cmd.rlen = 13; break; case SYS_DVBC_ANNEX_A: - cmd.args[0] = 0x90; - cmd.args[1] = 0x01; + memcpy(cmd.args, "\x90\x01", 2); cmd.wlen = 2; cmd.rlen = 9; break; case SYS_DVBT2: - cmd.args[0] = 0x50; - cmd.args[1] = 0x01; + memcpy(cmd.args, "\x50\x01", 2); cmd.wlen = 2; cmd.rlen = 14; break; @@ -412,7 +409,7 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - cmd.args[0] = 0x85; + memcpy(cmd.args, "\x85", 1); cmd.wlen = 1; cmd.rlen = 1; ret = si2168_cmd_execute(s, &cmd); @@ -438,40 +435,21 @@ static int si2168_init(struct dvb_frontend *fe) dev_dbg(&s->client->dev, "%s:\n", __func__); - cmd.args[0] = 0xc0; - cmd.args[1] = 0x12; - cmd.args[2] = 0x00; - cmd.args[3] = 0x0c; - cmd.args[4] = 0x00; - cmd.args[5] = 0x0d; - cmd.args[6] = 0x16; - cmd.args[7] = 0x00; - cmd.args[8] = 0x00; - cmd.args[9] = 0x00; - cmd.args[10] = 0x00; - cmd.args[11] = 0x00; - cmd.args[12] = 0x00; + memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13); cmd.wlen = 13; cmd.rlen = 0; ret = si2168_cmd_execute(s, &cmd); if (ret) goto err; - cmd.args[0] = 0xc0; - cmd.args[1] = 0x06; - cmd.args[2] = 0x01; - cmd.args[3] = 0x0f; - cmd.args[4] = 0x00; - cmd.args[5] = 0x20; - cmd.args[6] = 0x20; - cmd.args[7] = 0x01; + memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8); cmd.wlen = 8; cmd.rlen = 1; ret = si2168_cmd_execute(s, &cmd); if (ret) goto err; - cmd.args[0] = 0x02; + memcpy(cmd.args, "\x02", 1); cmd.wlen = 1; cmd.rlen = 13; ret = si2168_cmd_execute(s, &cmd); @@ -527,8 +505,7 @@ static int si2168_init(struct dvb_frontend *fe) release_firmware(fw); fw = NULL; - cmd.args[0] = 0x01; - cmd.args[1] = 0x01; + memcpy(cmd.args, "\x01\x01", 2); cmd.wlen = 2; cmd.rlen = 1; ret = si2168_cmd_execute(s, &cmd); -- cgit v0.10.2 From 88ac8f860640f28ae6fae43ca690adc3f7294b90 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 9 Jul 2014 20:22:27 -0300 Subject: [media] si2168: implement CNR statistic Implement CNR statistic. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index dc8ec9e..150d866 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -141,6 +141,15 @@ static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) s->fe_status = *status; + if (*status & FE_HAS_LOCK) { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cmd.args[3] * 1000 / 4; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + dev_dbg(&s->client->dev, "%s: status=%02x args=%*ph\n", __func__, *status, cmd.rlen, cmd.args); -- cgit v0.10.2 From e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 10 Jul 2014 06:02:53 -0300 Subject: [media] si2157: add read data support for fw cmd func We want also read data from firmware. Add support for it. Copied from si2168 driver. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 3f88e53..a4908ee 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -20,50 +20,52 @@ static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) { int ret; - u8 buf[1]; unsigned long timeout; mutex_lock(&s->i2c_mutex); - if (cmd->len) { + if (cmd->wlen) { /* write cmd and args for firmware */ - ret = i2c_master_send(s->client, cmd->args, cmd->len); + ret = i2c_master_send(s->client, cmd->args, cmd->wlen); if (ret < 0) { goto err_mutex_unlock; - } else if (ret != cmd->len) { + } else if (ret != cmd->wlen) { ret = -EREMOTEIO; goto err_mutex_unlock; } } - /* wait cmd execution terminate */ - #define TIMEOUT 80 - timeout = jiffies + msecs_to_jiffies(TIMEOUT); - while (!time_after(jiffies, timeout)) { - ret = i2c_master_recv(s->client, buf, 1); - if (ret < 0) { - goto err_mutex_unlock; - } else if (ret != 1) { - ret = -EREMOTEIO; - goto err_mutex_unlock; + if (cmd->rlen) { + /* wait cmd execution terminate */ + #define TIMEOUT 80 + timeout = jiffies + msecs_to_jiffies(TIMEOUT); + while (!time_after(jiffies, timeout)) { + ret = i2c_master_recv(s->client, cmd->args, cmd->rlen); + if (ret < 0) { + goto err_mutex_unlock; + } else if (ret != cmd->rlen) { + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* firmware ready? */ + if ((cmd->args[0] >> 7) & 0x01) + break; } - /* firmware ready? */ - if ((buf[0] >> 7) & 0x01) - break; - } + dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", + __func__, + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - TIMEOUT)); - dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", __func__, - jiffies_to_msecs(jiffies) - - (jiffies_to_msecs(timeout) - TIMEOUT)); - - if (!(buf[0] >> 7) & 0x01) { - ret = -ETIMEDOUT; - goto err_mutex_unlock; - } else { - ret = 0; + if (!((cmd->args[0] >> 7) & 0x01)) { + ret = -ETIMEDOUT; + goto err_mutex_unlock; + } } + ret = 0; + err_mutex_unlock: mutex_unlock(&s->i2c_mutex); if (ret) @@ -97,7 +99,8 @@ static int si2157_sleep(struct dvb_frontend *fe) s->active = false; memcpy(cmd.args, "\x13", 1); - cmd.len = 1; + cmd.wlen = 1; + cmd.rlen = 0; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; @@ -141,20 +144,23 @@ static int si2157_set_params(struct dvb_frontend *fe) cmd.args[12] = 0x00; cmd.args[13] = 0x00; cmd.args[14] = 0x01; - cmd.len = 15; + cmd.wlen = 15; + cmd.rlen = 1; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; cmd.args[0] = 0x02; - cmd.len = 1; + cmd.wlen = 1; + cmd.rlen = 13; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; cmd.args[0] = 0x01; cmd.args[1] = 0x01; - cmd.len = 2; + cmd.wlen = 2; + cmd.rlen = 1; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; @@ -168,7 +174,8 @@ static int si2157_set_params(struct dvb_frontend *fe) cmd.args[5] = (c->frequency >> 8) & 0xff; cmd.args[6] = (c->frequency >> 16) & 0xff; cmd.args[7] = (c->frequency >> 24) & 0xff; - cmd.len = 8; + cmd.wlen = 8; + cmd.rlen = 1; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; @@ -212,7 +219,8 @@ static int si2157_probe(struct i2c_client *client, mutex_init(&s->i2c_mutex); /* check if the tuner is there */ - cmd.len = 0; + cmd.wlen = 0; + cmd.rlen = 1; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index 6cc6c6f..6db4c97 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -31,7 +31,8 @@ struct si2157 { #define SI2157_ARGLEN 30 struct si2157_cmd { u8 args[SI2157_ARGLEN]; - unsigned len; + unsigned wlen; + unsigned rlen; }; #endif -- cgit v0.10.2 From 93f726396db152ba7f60edd4c0faa096ff772318 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 10 Jul 2014 20:21:59 -0300 Subject: [media] si2168: remove duplicate command Same command was executed twice, but different value. Remove redundant command. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 150d866..7d0fb8f 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -334,7 +334,7 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - memcpy(cmd.args, "\x14\x00\x01\x10\x00\x00", 6); + memcpy(cmd.args, "\x14\x00\x01\x10\x16\x00", 6); cmd.wlen = 6; cmd.rlen = 1; ret = si2168_cmd_execute(s, &cmd); @@ -404,13 +404,6 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - memcpy(cmd.args, "\x14\x00\x01\x10\x16\x00", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6); cmd.wlen = 6; cmd.rlen = 1; -- cgit v0.10.2 From 982c6238cfb88551dc0aa730c94acfae06b25412 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 11 Jul 2014 10:28:44 -0300 Subject: [media] si2168: do not set values which are already on default No need to set explicitly value that are already defaulted same. Setting new value returns old value. Firmware default values can be found just looking returned value. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 7d0fb8f..6220544 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -249,27 +249,6 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - memcpy(cmd.args, "\x14\x00\x01\x04\x00\x00", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - - memcpy(cmd.args, "\x14\x00\x03\x10\x17\x00", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - - memcpy(cmd.args, "\x14\x00\x02\x10\x15\x00", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6); cmd.wlen = 6; cmd.rlen = 1; @@ -284,13 +263,6 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - memcpy(cmd.args, "\x14\x00\x0b\x10\x88\x13", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6); cmd.wlen = 6; cmd.rlen = 1; @@ -306,20 +278,6 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - memcpy(cmd.args, "\x14\x00\x04\x10\x15\x00", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - - memcpy(cmd.args, "\x14\x00\x05\x10\xa1\x00", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6); cmd.wlen = 6; cmd.rlen = 1; @@ -327,13 +285,6 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - memcpy(cmd.args, "\x14\x00\x0d\x10\xd0\x02", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - memcpy(cmd.args, "\x14\x00\x01\x10\x16\x00", 6); cmd.wlen = 6; cmd.rlen = 1; @@ -355,55 +306,6 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; - memcpy(cmd.args, "\x14\x00\x04\x03\x00\x00", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - - memcpy(cmd.args, "\x14\x00\x03\x03\x00\x00", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - - memcpy(cmd.args, "\x14\x00\x08\x03\x00\x00", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - - memcpy(cmd.args, "\x14\x00\x07\x03\x01\x02", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - - memcpy(cmd.args, "\x14\x00\x06\x03\x00\x00", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - - memcpy(cmd.args, "\x14\x00\x05\x03\x00\x00", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - - memcpy(cmd.args, "\x14\x00\x01\x03\x0c\x40", 6); - cmd.wlen = 6; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6); cmd.wlen = 6; cmd.rlen = 1; -- cgit v0.10.2 From 1d518c2763555309deebcd1b0230385d3496e948 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 11 Jul 2014 10:40:29 -0300 Subject: [media] si2168: receive 4 bytes reply from cmd 0x14 Command 0x14 returns 4 bytes as a reply. It is used for setting key/value pairs to firmware and it returns 4 bytes back including old value. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 6220544..5481c39 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -251,21 +251,21 @@ static int si2168_set_frontend(struct dvb_frontend *fe) memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6); cmd.wlen = 6; - cmd.rlen = 1; + cmd.rlen = 4; ret = si2168_cmd_execute(s, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x06\x10\x24\x00", 6); cmd.wlen = 6; - cmd.rlen = 1; + cmd.rlen = 4; ret = si2168_cmd_execute(s, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6); cmd.wlen = 6; - cmd.rlen = 1; + cmd.rlen = 4; ret = si2168_cmd_execute(s, &cmd); if (ret) goto err; @@ -273,42 +273,42 @@ static int si2168_set_frontend(struct dvb_frontend *fe) memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6); cmd.args[4] = delivery_system | bandwidth; cmd.wlen = 6; - cmd.rlen = 1; + cmd.rlen = 4; ret = si2168_cmd_execute(s, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6); cmd.wlen = 6; - cmd.rlen = 1; + cmd.rlen = 4; ret = si2168_cmd_execute(s, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x01\x10\x16\x00", 6); cmd.wlen = 6; - cmd.rlen = 1; + cmd.rlen = 4; ret = si2168_cmd_execute(s, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x18", 6); cmd.wlen = 6; - cmd.rlen = 1; + cmd.rlen = 4; ret = si2168_cmd_execute(s, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x15", 6); cmd.wlen = 6; - cmd.rlen = 1; + cmd.rlen = 4; ret = si2168_cmd_execute(s, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6); cmd.wlen = 6; - cmd.rlen = 1; + cmd.rlen = 4; ret = si2168_cmd_execute(s, &cmd); if (ret) goto err; -- cgit v0.10.2 From b02db9e1e01253e375e8b760c22858027a2130de Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Sun, 13 Jul 2014 10:52:17 -0300 Subject: [media] si2168: Small typo fix (SI2157 -> SI2168) Signed-off-by: Olli Salonen Reviewed-by: Antti Palosaari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index 2a343e8..e7d963d 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -36,9 +36,9 @@ struct si2168 { }; /* firmare command struct */ -#define SI2157_ARGLEN 30 +#define SI2168_ARGLEN 30 struct si2168_cmd { - u8 args[SI2157_ARGLEN]; + u8 args[SI2168_ARGLEN]; unsigned wlen; unsigned rlen; }; -- cgit v0.10.2 From c9cb0820d5d756f2fac550213e664cc0be511349 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Sun, 13 Jul 2014 10:52:18 -0300 Subject: [media] si2168: Add support for chip revision Si2168 A30 Add handling for different chip revisions and firmwares. Signed-off-by: Olli Salonen Reviewed-by: Antti Palosaari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 5481c39..c10f4a1 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -333,7 +333,7 @@ static int si2168_init(struct dvb_frontend *fe) struct si2168 *s = fe->demodulator_priv; int ret, len, remaining; const struct firmware *fw = NULL; - u8 *fw_file = SI2168_FIRMWARE; + u8 *fw_file; const unsigned int i2c_wr_max = 8; struct si2168_cmd cmd; @@ -353,6 +353,7 @@ static int si2168_init(struct dvb_frontend *fe) if (ret) goto err; + /* query chip revision */ memcpy(cmd.args, "\x02", 1); cmd.wlen = 1; cmd.rlen = 13; @@ -374,6 +375,20 @@ static int si2168_init(struct dvb_frontend *fe) if (ret) goto err; + if (((cmd.args[1] & 0x0f) == 2) && (cmd.args[3] == '4') && + (cmd.args[4] == '0')) + fw_file = SI2168_B40_FIRMWARE; + else if (((cmd.args[1] & 0x0f) == 1) && (cmd.args[3] == '3') && + (cmd.args[4] == '0')) + fw_file = SI2168_A30_FIRMWARE; + else { + dev_err(&s->client->dev, + "%s: no firmware file for Si2168-%c%c defined\n", + KBUILD_MODNAME, cmd.args[3], cmd.args[4]); + ret = -EINVAL; + goto err; + } + /* cold state - try to download firmware */ dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", KBUILD_MODNAME, si2168_ops.info.name); @@ -381,9 +396,18 @@ static int si2168_init(struct dvb_frontend *fe) /* request the firmware, this will block and timeout */ ret = request_firmware(&fw, fw_file, &s->client->dev); if (ret) { - dev_err(&s->client->dev, "%s: firmare file '%s' not found\n", - KBUILD_MODNAME, fw_file); - goto err; + /* fallback mechanism to handle old name for + SI2168_B40_FIRMWARE */ + if (((cmd.args[1] & 0x0f) == 2) && (cmd.args[3] == '4') && + (cmd.args[4] == '0')) { + fw_file = SI2168_B40_FIRMWARE_FALLBACK; + ret = request_firmware(&fw, fw_file, &s->client->dev); + } + if (ret) { + dev_err(&s->client->dev, "%s: firmware file '%s' not found\n", + KBUILD_MODNAME, fw_file); + goto err; + } } dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", @@ -643,4 +667,4 @@ module_i2c_driver(si2168_driver); MODULE_AUTHOR("Antti Palosaari "); MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver"); MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(SI2168_FIRMWARE); +MODULE_FIRMWARE(SI2168_B40_FIRMWARE); diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index e7d963d..d606915 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -22,7 +22,9 @@ #include #include -#define SI2168_FIRMWARE "dvb-demod-si2168-01.fw" +#define SI2168_A30_FIRMWARE "dvb-demod-si2168-a30-01.fw" +#define SI2168_B40_FIRMWARE "dvb-demod-si2168-b40-01.fw" +#define SI2168_B40_FIRMWARE_FALLBACK "dvb-demod-si2168-01.fw" /* state struct */ struct si2168 { -- cgit v0.10.2 From b154121c6470b927794ff7ed17355aedfaa073ca Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Sun, 13 Jul 2014 10:52:19 -0300 Subject: [media] si2157: Move chip initialization to si2157_init Move chip initialization related code from si2157_set_params to si2157_init. Signed-off-by: Olli Salonen Reviewed-by: Antti Palosaari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index a4908ee..a92570f9 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -80,12 +80,41 @@ err: static int si2157_init(struct dvb_frontend *fe) { struct si2157 *s = fe->tuner_priv; + int ret; + struct si2157_cmd cmd; dev_dbg(&s->client->dev, "%s:\n", __func__); + /* configure? */ + memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); + cmd.wlen = 15; + cmd.rlen = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + + /* query chip revision */ + memcpy(cmd.args, "\x02", 1); + cmd.wlen = 1; + cmd.rlen = 13; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + + /* reboot the tuner with new firmware? */ + memcpy(cmd.args, "\x01\x01", 2); + cmd.wlen = 2; + cmd.rlen = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + s->active = true; return 0; +err: + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; } static int si2157_sleep(struct dvb_frontend *fe) @@ -128,48 +157,8 @@ static int si2157_set_params(struct dvb_frontend *fe) goto err; } - /* configure? */ - cmd.args[0] = 0xc0; - cmd.args[1] = 0x00; - cmd.args[2] = 0x0c; - cmd.args[3] = 0x00; - cmd.args[4] = 0x00; - cmd.args[5] = 0x01; - cmd.args[6] = 0x01; - cmd.args[7] = 0x01; - cmd.args[8] = 0x01; - cmd.args[9] = 0x01; - cmd.args[10] = 0x01; - cmd.args[11] = 0x02; - cmd.args[12] = 0x00; - cmd.args[13] = 0x00; - cmd.args[14] = 0x01; - cmd.wlen = 15; - cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); - if (ret) - goto err; - - cmd.args[0] = 0x02; - cmd.wlen = 1; - cmd.rlen = 13; - ret = si2157_cmd_execute(s, &cmd); - if (ret) - goto err; - - cmd.args[0] = 0x01; - cmd.args[1] = 0x01; - cmd.wlen = 2; - cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); - if (ret) - goto err; - /* set frequency */ - cmd.args[0] = 0x41; - cmd.args[1] = 0x00; - cmd.args[2] = 0x00; - cmd.args[3] = 0x00; + memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); cmd.args[4] = (c->frequency >> 0) & 0xff; cmd.args[5] = (c->frequency >> 8) & 0xff; cmd.args[6] = (c->frequency >> 16) & 0xff; -- cgit v0.10.2 From 1b92373f4b845019064d6a7c47b2ba72ebac191c Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Sun, 13 Jul 2014 10:52:20 -0300 Subject: [media] si2157: Add support for Si2158 chip Add support for Si2158 A20 chip. Signed-off-by: Olli Salonen Reviewed-by: Antti Palosaari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index a92570f9..58c5ef5 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -1,5 +1,5 @@ /* - * Silicon Labs Si2157 silicon tuner driver + * Silicon Labs Si2157/2158 silicon tuner driver * * Copyright (C) 2014 Antti Palosaari * @@ -16,6 +16,8 @@ #include "si2157_priv.h" +static const struct dvb_tuner_ops si2157_ops; + /* execute firmware command */ static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) { @@ -80,8 +82,11 @@ err: static int si2157_init(struct dvb_frontend *fe) { struct si2157 *s = fe->tuner_priv; - int ret; + int ret, remaining; struct si2157_cmd cmd; + u8 chip, len = 0; + const struct firmware *fw = NULL; + u8 *fw_file; dev_dbg(&s->client->dev, "%s:\n", __func__); @@ -101,6 +106,64 @@ static int si2157_init(struct dvb_frontend *fe) if (ret) goto err; + chip = cmd.args[2]; /* 57 for Si2157, 58 for Si2158 */ + + /* Si2158 requires firmware download */ + if (chip == 58) { + if (((cmd.args[1] & 0x0f) == 1) && (cmd.args[3] == '2') && + (cmd.args[4] == '0')) + fw_file = SI2158_A20_FIRMWARE; + else { + dev_err(&s->client->dev, + "%s: no firmware file for Si%d-%c%c defined\n", + KBUILD_MODNAME, chip, cmd.args[3], cmd.args[4]); + ret = -EINVAL; + goto err; + } + + /* cold state - try to download firmware */ + dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", + KBUILD_MODNAME, si2157_ops.info.name); + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, &s->client->dev); + if (ret) { + dev_err(&s->client->dev, "%s: firmware file '%s' not found\n", + KBUILD_MODNAME, fw_file); + goto err; + } + + dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", + KBUILD_MODNAME, fw_file); + + /* firmware should be n chunks of 17 bytes */ + if (fw->size % 17 != 0) { + dev_err(&s->client->dev, "%s: firmware file '%s' is invalid\n", + KBUILD_MODNAME, fw_file); + ret = -EINVAL; + goto err; + } + + for (remaining = fw->size; remaining > 0; remaining -= 17) { + memcpy(&len, &fw->data[fw->size - remaining], 1); + memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], + len); + cmd.wlen = len; + cmd.rlen = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) { + dev_err(&s->client->dev, + "%s: firmware download failed=%d\n", + KBUILD_MODNAME, ret); + goto err; + } + } + + release_firmware(fw); + fw = NULL; + + } + /* reboot the tuner with new firmware? */ memcpy(cmd.args, "\x01\x01", 2); cmd.wlen = 2; @@ -177,7 +240,7 @@ err: static const struct dvb_tuner_ops si2157_tuner_ops = { .info = { - .name = "Silicon Labs Si2157", + .name = "Silicon Labs Si2157/Si2158", .frequency_min = 110000000, .frequency_max = 862000000, }, @@ -221,7 +284,7 @@ static int si2157_probe(struct i2c_client *client, i2c_set_clientdata(client, s); dev_info(&s->client->dev, - "%s: Silicon Labs Si2157 successfully attached\n", + "%s: Silicon Labs Si2157/Si2158 successfully attached\n", KBUILD_MODNAME); return 0; err: @@ -263,6 +326,6 @@ static struct i2c_driver si2157_driver = { module_i2c_driver(si2157_driver); -MODULE_DESCRIPTION("Silicon Labs Si2157 silicon tuner driver"); +MODULE_DESCRIPTION("Silicon Labs Si2157/Si2158 silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h index f469a09..4465c46 100644 --- a/drivers/media/tuners/si2157.h +++ b/drivers/media/tuners/si2157.h @@ -1,5 +1,5 @@ /* - * Silicon Labs Si2157 silicon tuner driver + * Silicon Labs Si2157/2158 silicon tuner driver * * Copyright (C) 2014 Antti Palosaari * diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index 6db4c97..db79f3c 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -1,5 +1,5 @@ /* - * Silicon Labs Si2157 silicon tuner driver + * Silicon Labs Si2157/2158 silicon tuner driver * * Copyright (C) 2014 Antti Palosaari * @@ -17,6 +17,7 @@ #ifndef SI2157_PRIV_H #define SI2157_PRIV_H +#include #include "si2157.h" /* state struct */ @@ -35,4 +36,6 @@ struct si2157_cmd { unsigned rlen; }; +#define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw" + #endif -- cgit v0.10.2 From a1dad50d8cdce0be71489c7544ffcd36cf80e994 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Sun, 13 Jul 2014 10:52:21 -0300 Subject: [media] si2157: Set delivery system and bandwidth before tuning Tell used TV standard and bandwidth for tuner firmware. Signed-off-by: Olli Salonen Reviewed-by: Antti Palosaari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 58c5ef5..b656f9b 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -209,6 +209,7 @@ static int si2157_set_params(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; struct si2157_cmd cmd; + u8 bandwidth, delivery_system; dev_dbg(&s->client->dev, "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n", @@ -220,6 +221,36 @@ static int si2157_set_params(struct dvb_frontend *fe) goto err; } + if (c->bandwidth_hz <= 6000000) + bandwidth = 0x06; + else if (c->bandwidth_hz <= 7000000) + bandwidth = 0x07; + else if (c->bandwidth_hz <= 8000000) + bandwidth = 0x08; + else + bandwidth = 0x0f; + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */ + delivery_system = 0x20; + break; + case SYS_DVBC_ANNEX_A: + delivery_system = 0x30; + break; + default: + ret = -EINVAL; + goto err; + } + + memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6); + cmd.args[4] = delivery_system | bandwidth; + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + /* set frequency */ memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); cmd.args[4] = (c->frequency >> 0) & 0xff; -- cgit v0.10.2 From 26c42b0dd5fa552bf26451cbd1d4c70fb6b95b67 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Sun, 13 Jul 2014 10:52:22 -0300 Subject: [media] cxusb: TechnoTrend CT2-4400 USB DVB-T2/C tuner support USB ID 0b48:3014. USB interface: Cypress CY7C68013A-56LTXC Demodulator: Silicon Labs Si2168-30 Tuner: Silicon Labs Si2158-20 Cc: Michael Krufky Signed-off-by: Olli Salonen Reviewed-by: Antti Palosaari Signed-off-by: Antti Palosaari Reviewed-by: Michael Ira Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 11d2bea..f8e3150 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -244,6 +244,7 @@ #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM 0x3009 #define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d +#define USB_PID_TECHNOTREND_TVSTICK_CT2_4400 0x3014 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081 #define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058 diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index c5d9566..10aef21 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -117,10 +117,12 @@ config DVB_USB_CXUSB select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT select DVB_ATBM8830 if MEDIA_SUBDRV_AUTOSELECT select DVB_LGS8GXX if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MAX2165 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Conexant USB2.0 hybrid reference design. Currently, only DVB and ATSC modes are supported, analog mode @@ -128,6 +130,7 @@ config DVB_USB_CXUSB Medion MD95700 hybrid USB2.0 device. DViCO FusionHDTV (Bluebird) USB2.0 devices + TechnoTrend TVStick CT2-4400 config DVB_USB_M920X tristate "Uli m920x DVB-T USB2.0 support" diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 6acde5e..d60e36d 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -42,6 +42,8 @@ #include "dib0070.h" #include "lgs8gxx.h" #include "atbm8830.h" +#include "si2168.h" +#include "si2157.h" /* Max transfer size done by I2C transfer functions */ #define MAX_XFER_SIZE 64 @@ -144,6 +146,22 @@ static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d, } } +static int cxusb_tt_ct2_4400_gpio_tuner(struct dvb_usb_device *d, int onoff) +{ + u8 o[2], i; + int rc; + + o[0] = 0x83; + o[1] = onoff; + rc = cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1); + + if (rc) { + deb_info("gpio_write failed.\n"); + return -EIO; + } + return 0; +} + /* I2C */ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) @@ -505,6 +523,30 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event, return 0; } +static int cxusb_tt_ct2_4400_rc_query(struct dvb_usb_device *d) +{ + u8 i[2]; + int ret; + u32 cmd, keycode; + u8 rc5_cmd, rc5_addr, rc5_toggle; + + ret = cxusb_ctrl_msg(d, 0x10, NULL, 0, i, 2); + if (ret) + return ret; + + cmd = (i[0] << 8) | i[1]; + + if (cmd != 0xffff) { + rc5_cmd = cmd & 0x3F; /* bits 1-6 for command */ + rc5_addr = (cmd & 0x07C0) >> 6; /* bits 7-11 for address */ + rc5_toggle = (cmd & 0x0800) >> 11; /* bit 12 for toggle */ + keycode = (rc5_addr << 8) | rc5_cmd; + rc_keydown(d->rc_dev, keycode, rc5_toggle); + } + + return 0; +} + static struct rc_map_table rc_map_dvico_mce_table[] = { { 0xfe02, KEY_TV }, { 0xfe0e, KEY_MP3 }, @@ -1301,6 +1343,73 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) return 0; } +static int cxusb_tt_ct2_4400_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct cxusb_state *st = d->priv; + struct i2c_adapter *adapter; + struct i2c_client *client_demod; + struct i2c_client *client_tuner; + struct i2c_board_info info; + struct si2168_config si2168_config; + struct si2157_config si2157_config; + + /* reset the tuner */ + if (cxusb_tt_ct2_4400_gpio_tuner(d, 0) < 0) { + err("clear tuner gpio failed"); + return -EIO; + } + msleep(100); + if (cxusb_tt_ct2_4400_gpio_tuner(d, 1) < 0) { + err("set tuner gpio failed"); + return -EIO; + } + msleep(100); + + /* attach frontend */ + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &adap->fe_adap[0].fe; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2168_config; + request_module(info.type); + client_demod = i2c_new_device(&d->i2c_adap, &info); + if (client_demod == NULL || client_demod->dev.driver == NULL) + return -ENODEV; + + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + return -ENODEV; + } + + st->i2c_client_demod = client_demod; + + /* attach tuner */ + si2157_config.fe = adap->fe_adap[0].fe; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module(info.type); + client_tuner = i2c_new_device(adapter, &info); + if (client_tuner == NULL || client_tuner->dev.driver == NULL) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + return -ENODEV; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + return -ENODEV; + } + + st->i2c_client_tuner = client_tuner; + + return 0; +} + /* * DViCO has shipped two devices with the same USB ID, but only one of them * needs a firmware download. Check the device class details to see if they @@ -1382,6 +1491,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope static struct dvb_usb_device_properties cxusb_aver_a868r_properties; static struct dvb_usb_device_properties cxusb_d680_dmb_properties; static struct dvb_usb_device_properties cxusb_mygica_d689_properties; +static struct dvb_usb_device_properties cxusb_tt_ct2_4400_properties; static int cxusb_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -1412,12 +1522,37 @@ static int cxusb_probe(struct usb_interface *intf, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties, THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_tt_ct2_4400_properties, + THIS_MODULE, NULL, adapter_nr) || 0) return 0; return -EINVAL; } +static void cxusb_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + struct cxusb_state *st = d->priv; + struct i2c_client *client; + + /* remove I2C client for tuner */ + client = st->i2c_client_tuner; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + + /* remove I2C client for demodulator */ + client = st->i2c_client_demod; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + + dvb_usb_device_exit(intf); +} + static struct usb_device_id cxusb_table [] = { { USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) }, { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) }, @@ -1439,6 +1574,7 @@ static struct usb_device_id cxusb_table [] = { { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) }, { USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) }, { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) }, + { USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_TVSTICK_CT2_4400) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, cxusb_table); @@ -2085,10 +2221,63 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { } }; +static struct dvb_usb_device_properties cxusb_tt_ct2_4400_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .streaming_ctrl = cxusb_streaming_ctrl, + /* both frontend and tuner attached in the + same function */ + .frontend_attach = cxusb_tt_ct2_4400_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + } }, + }, + }, + + .i2c_algo = &cxusb_i2c_algo, + .generic_bulk_ctrl_endpoint = 0x01, + .generic_bulk_ctrl_endpoint_response = 0x81, + + .rc.core = { + .rc_codes = RC_MAP_TT_1500, + .allowed_protos = RC_BIT_RC5, + .rc_query = cxusb_tt_ct2_4400_rc_query, + .rc_interval = 150, + }, + + .num_device_descs = 1, + .devices = { + { + "TechnoTrend TVStick CT2-4400", + { NULL }, + { &cxusb_table[20], NULL }, + }, + } +}; + static struct usb_driver cxusb_driver = { .name = "dvb_usb_cxusb", .probe = cxusb_probe, - .disconnect = dvb_usb_device_exit, + .disconnect = cxusb_disconnect, .id_table = cxusb_table, }; diff --git a/drivers/media/usb/dvb-usb/cxusb.h b/drivers/media/usb/dvb-usb/cxusb.h index 1a51eaf..527ff79 100644 --- a/drivers/media/usb/dvb-usb/cxusb.h +++ b/drivers/media/usb/dvb-usb/cxusb.h @@ -30,6 +30,8 @@ struct cxusb_state { u8 gpio_write_state[3]; + struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_tuner; }; #endif -- cgit v0.10.2 From 668aa63cfcc33e13f2d4c2eef6f65146775b9ed6 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 13 Jul 2014 16:09:03 -0300 Subject: [media] si2168: advertise Si2168 A30 firmware Driver uses that new firmware too, so advertise it. Signed-off-by: Antti Palosaari Tested-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index c10f4a1..db4b28a 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -667,4 +667,5 @@ module_i2c_driver(si2168_driver); MODULE_AUTHOR("Antti Palosaari "); MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(SI2168_A30_FIRMWARE); MODULE_FIRMWARE(SI2168_B40_FIRMWARE); -- cgit v0.10.2 From bac53a2c604779297ac8ee54ce7eda4cc07b65f5 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 13 Jul 2014 16:13:49 -0300 Subject: [media] si2157: advertise Si2158 A20 firmware Driver uses that firmware. Add it module firmware list. Signed-off-by: Antti Palosaari Tested-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index b656f9b..3fa1f26 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -360,3 +360,4 @@ module_i2c_driver(si2157_driver); MODULE_DESCRIPTION("Silicon Labs Si2157/Si2158 silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari "); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(SI2158_A20_FIRMWARE); -- cgit v0.10.2 From b6b6fd6feb5f71267ef535f15061a397f86ebb26 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 13 Jul 2014 19:22:19 -0300 Subject: [media] si2168: few firmware download changes Rework firmware selection logic a little bit. Print notice asking user update firmware when old Si2168 B40 firmware is used. Signed-off-by: Antti Palosaari Tested-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index db4b28a..1b56b8e 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -336,6 +336,7 @@ static int si2168_init(struct dvb_frontend *fe) u8 *fw_file; const unsigned int i2c_wr_max = 8; struct si2168_cmd cmd; + unsigned int chip_id; dev_dbg(&s->client->dev, "%s:\n", __func__); @@ -375,16 +376,24 @@ static int si2168_init(struct dvb_frontend *fe) if (ret) goto err; - if (((cmd.args[1] & 0x0f) == 2) && (cmd.args[3] == '4') && - (cmd.args[4] == '0')) - fw_file = SI2168_B40_FIRMWARE; - else if (((cmd.args[1] & 0x0f) == 1) && (cmd.args[3] == '3') && - (cmd.args[4] == '0')) + chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | + cmd.args[4] << 0; + + #define SI2168_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0) + #define SI2168_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0) + + switch (chip_id) { + case SI2168_A30: fw_file = SI2168_A30_FIRMWARE; - else { + break; + case SI2168_B40: + fw_file = SI2168_B40_FIRMWARE; + break; + default: dev_err(&s->client->dev, - "%s: no firmware file for Si2168-%c%c defined\n", - KBUILD_MODNAME, cmd.args[3], cmd.args[4]); + "%s: unkown chip version Si21%d-%c%c%c\n", + KBUILD_MODNAME, cmd.args[2], cmd.args[1], + cmd.args[3], cmd.args[4]); ret = -EINVAL; goto err; } @@ -396,15 +405,19 @@ static int si2168_init(struct dvb_frontend *fe) /* request the firmware, this will block and timeout */ ret = request_firmware(&fw, fw_file, &s->client->dev); if (ret) { - /* fallback mechanism to handle old name for - SI2168_B40_FIRMWARE */ - if (((cmd.args[1] & 0x0f) == 2) && (cmd.args[3] == '4') && - (cmd.args[4] == '0')) { + /* fallback mechanism to handle old name for Si2168 B40 fw */ + if (chip_id == SI2168_B40) { fw_file = SI2168_B40_FIRMWARE_FALLBACK; ret = request_firmware(&fw, fw_file, &s->client->dev); } - if (ret) { - dev_err(&s->client->dev, "%s: firmware file '%s' not found\n", + + if (ret == 0) { + dev_notice(&s->client->dev, + "%s: please install firmware file '%s'\n", + KBUILD_MODNAME, SI2168_B40_FIRMWARE); + } else { + dev_err(&s->client->dev, + "%s: firmware file '%s' not found\n", KBUILD_MODNAME, fw_file); goto err; } -- cgit v0.10.2 From 7d6bc608e062f6b6667c9eeeb17055f017ecadb1 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 13 Jul 2014 20:05:28 -0300 Subject: [media] si2157: rework firmware download logic a little bit Rework firmware selection / chip detection logic a little bit. Add missing release_firmware() to error path. Signed-off-by: Antti Palosaari Tested-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 3fa1f26..329004f 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -82,11 +82,11 @@ err: static int si2157_init(struct dvb_frontend *fe) { struct si2157 *s = fe->tuner_priv; - int ret, remaining; + int ret, len, remaining; struct si2157_cmd cmd; - u8 chip, len = 0; const struct firmware *fw = NULL; u8 *fw_file; + unsigned int chip_id; dev_dbg(&s->client->dev, "%s:\n", __func__); @@ -106,64 +106,69 @@ static int si2157_init(struct dvb_frontend *fe) if (ret) goto err; - chip = cmd.args[2]; /* 57 for Si2157, 58 for Si2158 */ + chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | + cmd.args[4] << 0; - /* Si2158 requires firmware download */ - if (chip == 58) { - if (((cmd.args[1] & 0x0f) == 1) && (cmd.args[3] == '2') && - (cmd.args[4] == '0')) - fw_file = SI2158_A20_FIRMWARE; - else { - dev_err(&s->client->dev, - "%s: no firmware file for Si%d-%c%c defined\n", - KBUILD_MODNAME, chip, cmd.args[3], cmd.args[4]); - ret = -EINVAL; - goto err; - } + #define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0) + #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) - /* cold state - try to download firmware */ - dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", - KBUILD_MODNAME, si2157_ops.info.name); + switch (chip_id) { + case SI2158_A20: + fw_file = SI2158_A20_FIRMWARE; + break; + case SI2157_A30: + goto skip_fw_download; + break; + default: + dev_err(&s->client->dev, + "%s: unkown chip version Si21%d-%c%c%c\n", + KBUILD_MODNAME, cmd.args[2], cmd.args[1], + cmd.args[3], cmd.args[4]); + ret = -EINVAL; + goto err; + } - /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, &s->client->dev); - if (ret) { - dev_err(&s->client->dev, "%s: firmware file '%s' not found\n", - KBUILD_MODNAME, fw_file); - goto err; - } + /* cold state - try to download firmware */ + dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", + KBUILD_MODNAME, si2157_ops.info.name); - dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, &s->client->dev); + if (ret) { + dev_err(&s->client->dev, "%s: firmware file '%s' not found\n", KBUILD_MODNAME, fw_file); + goto err; + } - /* firmware should be n chunks of 17 bytes */ - if (fw->size % 17 != 0) { - dev_err(&s->client->dev, "%s: firmware file '%s' is invalid\n", - KBUILD_MODNAME, fw_file); - ret = -EINVAL; - goto err; - } - - for (remaining = fw->size; remaining > 0; remaining -= 17) { - memcpy(&len, &fw->data[fw->size - remaining], 1); - memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], - len); - cmd.wlen = len; - cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); - if (ret) { - dev_err(&s->client->dev, - "%s: firmware download failed=%d\n", - KBUILD_MODNAME, ret); - goto err; - } - } + /* firmware should be n chunks of 17 bytes */ + if (fw->size % 17 != 0) { + dev_err(&s->client->dev, "%s: firmware file '%s' is invalid\n", + KBUILD_MODNAME, fw_file); + ret = -EINVAL; + goto err; + } - release_firmware(fw); - fw = NULL; + dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", + KBUILD_MODNAME, fw_file); + for (remaining = fw->size; remaining > 0; remaining -= 17) { + len = fw->data[fw->size - remaining]; + memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); + cmd.wlen = len; + cmd.rlen = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) { + dev_err(&s->client->dev, + "%s: firmware download failed=%d\n", + KBUILD_MODNAME, ret); + goto err; + } } + release_firmware(fw); + fw = NULL; + +skip_fw_download: /* reboot the tuner with new firmware? */ memcpy(cmd.args, "\x01\x01", 2); cmd.wlen = 2; @@ -176,6 +181,9 @@ static int si2157_init(struct dvb_frontend *fe) return 0; err: + if (fw) + release_firmware(fw); + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); return ret; } -- cgit v0.10.2 From 0ba2aeb6dab80920edd9cf5b93b1ea4d6913b8f3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Apr 2014 09:41:25 -0300 Subject: [media] v4l2-ctrls: increase internal min/max/step/def to 64 bit While VIDIOC_QUERYCTRL is limited to 32 bit min/max/step/def values for controls, the upcoming VIDIOC_QUERY_EXT_CTRL isn't. So increase the internal representation to 64 bits in preparation. Because of these changes the msi3101 driver has been modified slightly to fix a formatting issue (%d becomes %lld), vivi had to be modified as well to cope with the new 64-bit min/max values and the PIXEL_RATE control in a few sensor drivers required proper min/max/def values. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 40172b8..65e6e1a 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -931,7 +931,7 @@ static int mt9v032_probe(struct i2c_client *client, mt9v032->pixel_rate = v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); if (pdata && pdata->link_freqs) { unsigned int def = 0; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 446c82c..72350f0 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -533,7 +533,7 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) sensor->pixel_rate_parray = v4l2_ctrl_new_std( &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); if (sensor->pixel_array->ctrl_handler.error) { dev_err(&client->dev, @@ -562,7 +562,7 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) sensor->pixel_rate_csi = v4l2_ctrl_new_std( &sensor->src->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); if (sensor->src->ctrl_handler.error) { dev_err(&client->dev, diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index b7fbcdf..cd535b3 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -1236,7 +1236,7 @@ static const struct v4l2_ctrl_config vivi_ctrl_int32 = { .id = VIVI_CID_CUSTOM_BASE + 2, .name = "Integer 32 Bits", .type = V4L2_CTRL_TYPE_INTEGER, - .min = 0x80000000, + .min = -0x80000000LL, .max = 0x7fffffff, .step = 1, }; @@ -1246,6 +1246,9 @@ static const struct v4l2_ctrl_config vivi_ctrl_int64 = { .id = VIVI_CID_CUSTOM_BASE + 3, .name = "Integer 64 Bits", .type = V4L2_CTRL_TYPE_INTEGER64, + .min = LLONG_MIN, + .max = LLONG_MAX, + .step = 1, }; static const char * const vivi_ctrl_menu_strings[] = { diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 433d6d7..ccaa38f 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -111,9 +111,13 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, EXPORT_SYMBOL(v4l2_ctrl_check); /* Fill in a struct v4l2_queryctrl */ -int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def) +int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 _min, s32 _max, s32 _step, s32 _def) { const char *name; + s64 min = _min; + s64 max = _max; + u64 step = _step; + s64 def = _def; v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type, &min, &max, &step, &def, &qctrl->flags); diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 55c6832..e132fa2 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -877,7 +877,7 @@ const char *v4l2_ctrl_get_name(u32 id) EXPORT_SYMBOL(v4l2_ctrl_get_name); void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, - s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) + s64 *min, s64 *max, u64 *step, s64 *def, u32 *flags) { *name = v4l2_ctrl_get_name(id); *flags = 0; @@ -1041,14 +1041,23 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *type = V4L2_CTRL_TYPE_INTEGER; *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; - case V4L2_CID_MPEG_VIDEO_DEC_FRAME: case V4L2_CID_MPEG_VIDEO_DEC_PTS: - *flags |= V4L2_CTRL_FLAG_VOLATILE; - /* Fall through */ + *type = V4L2_CTRL_TYPE_INTEGER64; + *flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY; + *min = *def = 0; + *max = 0x1ffffffffLL; + *step = 1; + break; + case V4L2_CID_MPEG_VIDEO_DEC_FRAME: + *type = V4L2_CTRL_TYPE_INTEGER64; + *flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY; + *min = *def = 0; + *max = 0x7fffffffffffffffLL; + *step = 1; + break; case V4L2_CID_PIXEL_RATE: *type = V4L2_CTRL_TYPE_INTEGER64; *flags |= V4L2_CTRL_FLAG_READ_ONLY; - *min = *max = *step = *def = 0; break; default: *type = V4L2_CTRL_TYPE_INTEGER; @@ -1352,7 +1361,7 @@ static int cluster_changed(struct v4l2_ctrl *master) /* Control range checking */ static int check_range(enum v4l2_ctrl_type type, - s32 min, s32 max, u32 step, s32 def) + s64 min, s64 max, u64 step, s64 def) { switch (type) { case V4L2_CTRL_TYPE_BOOLEAN: @@ -1360,7 +1369,8 @@ static int check_range(enum v4l2_ctrl_type type, return -ERANGE; /* fall through */ case V4L2_CTRL_TYPE_INTEGER: - if (step <= 0 || min > max || def < min || def > max) + case V4L2_CTRL_TYPE_INTEGER64: + if (step == 0 || min > max || def < min || def > max) return -ERANGE; return 0; case V4L2_CTRL_TYPE_BITMASK: @@ -1385,23 +1395,30 @@ static int check_range(enum v4l2_ctrl_type type, } } +/* Round towards the closest legal value */ +#define ROUND_TO_RANGE(val, offset_type, ctrl) \ +({ \ + offset_type offset; \ + val += (ctrl)->step / 2; \ + val = clamp_t(typeof(val), val, \ + (ctrl)->minimum, (ctrl)->maximum); \ + offset = (val) - (ctrl)->minimum; \ + offset = (ctrl)->step * (offset / (ctrl)->step); \ + val = (ctrl)->minimum + offset; \ + 0; \ +}) + /* Validate a new control */ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) { size_t len; - u32 offset; - s32 val; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - /* Round towards the closest legal value */ - val = c->value + ctrl->step / 2; - val = clamp(val, ctrl->minimum, ctrl->maximum); - offset = val - ctrl->minimum; - offset = ctrl->step * (offset / ctrl->step); - c->value = ctrl->minimum + offset; - return 0; + return ROUND_TO_RANGE(*(s32 *)&c->value, u32, ctrl); + case V4L2_CTRL_TYPE_INTEGER64: + return ROUND_TO_RANGE(*(s64 *)&c->value64, u64, ctrl); case V4L2_CTRL_TYPE_BOOLEAN: c->value = !!c->value; @@ -1427,9 +1444,6 @@ static int validate_new(const struct v4l2_ctrl *ctrl, c->value = 0; return 0; - case V4L2_CTRL_TYPE_INTEGER64: - return 0; - case V4L2_CTRL_TYPE_STRING: len = strlen(c->string); if (len < ctrl->minimum) @@ -1653,7 +1667,7 @@ unlock: static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const char *name, enum v4l2_ctrl_type type, - s32 min, s32 max, u32 step, s32 def, + s64 min, s64 max, u64 step, s64 def, u32 flags, const char * const *qmenu, const s64 *qmenu_int, void *priv) { @@ -1738,10 +1752,10 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, const s64 *qmenu_int = cfg->qmenu_int; enum v4l2_ctrl_type type = cfg->type; u32 flags = cfg->flags; - s32 min = cfg->min; - s32 max = cfg->max; - u32 step = cfg->step; - s32 def = cfg->def; + s64 min = cfg->min; + s64 max = cfg->max; + u64 step = cfg->step; + s64 def = cfg->def; if (name == NULL) v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step, @@ -1774,7 +1788,7 @@ EXPORT_SYMBOL(v4l2_ctrl_new_custom); /* Helper function for standard non-menu controls */ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 min, s32 max, u32 step, s32 def) + u32 id, s64 min, s64 max, u64 step, s64 def) { const char *name; enum v4l2_ctrl_type type; @@ -1794,15 +1808,17 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std); /* Helper function for standard menu controls */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 mask, s32 def) + u32 id, u8 _max, u64 mask, u8 _def) { const char * const *qmenu = NULL; const s64 *qmenu_int = NULL; unsigned int qmenu_int_len = 0; const char *name; enum v4l2_ctrl_type type; - s32 min; - s32 step; + s64 min; + s64 max = _max; + s64 def = _def; + u64 step; u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); @@ -1823,14 +1839,16 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); /* Helper function for standard menu controls with driver defined menu */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_ops *ops, u32 id, s32 max, - s32 mask, s32 def, const char * const *qmenu) + const struct v4l2_ctrl_ops *ops, u32 id, u8 _max, + u64 mask, u8 _def, const char * const *qmenu) { enum v4l2_ctrl_type type; const char *name; u32 flags; - s32 step; - s32 min; + u64 step; + s64 min; + s64 max = _max; + s64 def = _def; /* v4l2_ctrl_new_std_menu_items() should only be called for * standard controls without a standard menu. @@ -1854,12 +1872,14 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items); /* Helper function for standard integer menu controls */ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 def, const s64 *qmenu_int) + u32 id, u8 _max, u8 _def, const s64 *qmenu_int) { const char *name; enum v4l2_ctrl_type type; - s32 min; - s32 step; + s64 min; + u64 step; + s64 max = _max; + s64 def = _def; u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); @@ -2887,13 +2907,14 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void EXPORT_SYMBOL(v4l2_ctrl_notify); int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, - s32 min, s32 max, u32 step, s32 def) + s64 min, s64 max, u64 step, s64 def) { int ret = check_range(ctrl->type, min, max, step, def); struct v4l2_ext_control c; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_INTEGER64: case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: diff --git a/drivers/staging/media/msi3101/msi001.c b/drivers/staging/media/msi3101/msi001.c index bd0b93c..ee99e37 100644 --- a/drivers/staging/media/msi3101/msi001.c +++ b/drivers/staging/media/msi3101/msi001.c @@ -381,7 +381,7 @@ static int msi001_s_ctrl(struct v4l2_ctrl *ctrl) int ret; dev_dbg(&s->spi->dev, - "%s: id=%d name=%s val=%d min=%d max=%d step=%d\n", + "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", __func__, ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum, ctrl->step); diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c index cee1273..f81b1c8 100644 --- a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c +++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c @@ -1337,7 +1337,7 @@ static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) int ret; dev_dbg(&s->udev->dev, - "%s: id=%d name=%s val=%d min=%d max=%d step=%d\n", + "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", __func__, ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum, ctrl->step); @@ -1350,8 +1350,8 @@ static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) s32 val = s->f_adc + s->bandwidth->step / 2; u32 offset; - val = clamp(val, s->bandwidth->minimum, - s->bandwidth->maximum); + val = clamp_t(s32, val, s->bandwidth->minimum, + s->bandwidth->maximum); offset = val - s->bandwidth->minimum; offset = s->bandwidth->step * (offset / s->bandwidth->step); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 16f7f26..a2e8f03 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -131,10 +131,10 @@ struct v4l2_ctrl { u32 id; const char *name; enum v4l2_ctrl_type type; - s32 minimum, maximum, default_value; + s64 minimum, maximum, default_value; union { - u32 step; - u32 menu_skip_mask; + u64 step; + u64 menu_skip_mask; }; union { const char * const *qmenu; @@ -216,9 +216,9 @@ struct v4l2_ctrl_handler { * @menu_skip_mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for - * menus with <= 32 menu items. There are no menus that come + * menus with <= 64 menu items. There are no menus that come * close to that number, so this is OK. Should we ever need more, - * then this will have to be extended to a u64 or a bit array. + * then this will have to be extended to a bit array. * @qmenu: A const char * array for all menu items. Array entries that are * empty strings ("") correspond to non-existing menu items (this * is in addition to the menu_skip_mask above). The last entry @@ -231,12 +231,12 @@ struct v4l2_ctrl_config { u32 id; const char *name; enum v4l2_ctrl_type type; - s32 min; - s32 max; - u32 step; - s32 def; + s64 min; + s64 max; + u64 step; + s64 def; u32 flags; - u32 menu_skip_mask; + u64 menu_skip_mask; const char * const *qmenu; const s64 *qmenu_int; unsigned int is_private:1; @@ -257,7 +257,7 @@ struct v4l2_ctrl_config { * control framework this function will no longer be exported. */ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, - s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags); + s64 *min, s64 *max, u64 *step, s64 *def, u32 *flags); /** v4l2_ctrl_handler_init_class() - Initialize the control handler. @@ -362,7 +362,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, */ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 min, s32 max, u32 step, s32 def); + u32 id, s64 min, s64 max, u64 step, s64 def); /** v4l2_ctrl_new_std_menu() - Allocate and initialize a new standard V4L2 menu control. * @hdl: The control handler. @@ -372,9 +372,9 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, * @mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for - * menus with <= 32 menu items. There are no menus that come + * menus with <= 64 menu items. There are no menus that come * close to that number, so this is OK. Should we ever need more, - * then this will have to be extended to a u64 or a bit array. + * then this will have to be extended to a bit array. * @def: The control's default value. * * Same as v4l2_ctrl_new_std(), but @min is set to 0 and the @mask value @@ -384,7 +384,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 mask, s32 def); + u32 id, u8 max, u64 mask, u8 def); /** v4l2_ctrl_new_std_menu_items() - Create a new standard V4L2 menu control * with driver specific menu. @@ -395,9 +395,9 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, * @mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for - * menus with <= 32 menu items. There are no menus that come + * menus with <= 64 menu items. There are no menus that come * close to that number, so this is OK. Should we ever need more, - * then this will have to be extended to a u64 or a bit array. + * then this will have to be extended to a bit array. * @def: The control's default value. * @qmenu: The new menu. * @@ -406,8 +406,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, * */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_ops *ops, u32 id, s32 max, - s32 mask, s32 def, const char * const *qmenu); + const struct v4l2_ctrl_ops *ops, u32 id, u8 max, + u64 mask, u8 def, const char * const *qmenu); /** v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control. * @hdl: The control handler. @@ -424,7 +424,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, */ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 def, const s64 *qmenu_int); + u32 id, u8 max, u8 def, const s64 *qmenu_int); /** v4l2_ctrl_add_ctrl() - Add a control from another handler to this handler. * @hdl: The control handler. @@ -560,7 +560,7 @@ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); * take the lock itself. */ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, - s32 min, s32 max, u32 step, s32 def); + s64 min, s64 max, u64 step, s64 def); /** v4l2_ctrl_lock() - Helper function to lock the handler * associated with the control. -- cgit v0.10.2 From 7eafbce91b50317ef92d43c61b30a46cebe49f03 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 16 Dec 2013 10:12:17 -0300 Subject: [media] v4l2-ctrls: use pr_info/cont instead of printk Codingstyle fix. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e132fa2..365884b 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2068,45 +2068,45 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl, if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS) return; - printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name); + pr_info("%s%s%s: ", prefix, colon, ctrl->name); switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - printk(KERN_CONT "%d", ctrl->cur.val); + pr_cont("%d", ctrl->cur.val); break; case V4L2_CTRL_TYPE_BOOLEAN: - printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false"); + pr_cont("%s", ctrl->cur.val ? "true" : "false"); break; case V4L2_CTRL_TYPE_MENU: - printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]); + pr_cont("%s", ctrl->qmenu[ctrl->cur.val]); break; case V4L2_CTRL_TYPE_INTEGER_MENU: - printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]); + pr_cont("%lld", ctrl->qmenu_int[ctrl->cur.val]); break; case V4L2_CTRL_TYPE_BITMASK: - printk(KERN_CONT "0x%08x", ctrl->cur.val); + pr_cont("0x%08x", ctrl->cur.val); break; case V4L2_CTRL_TYPE_INTEGER64: - printk(KERN_CONT "%lld", ctrl->cur.val64); + pr_cont("%lld", ctrl->cur.val64); break; case V4L2_CTRL_TYPE_STRING: - printk(KERN_CONT "%s", ctrl->cur.string); + pr_cont("%s", ctrl->cur.string); break; default: - printk(KERN_CONT "unknown type %d", ctrl->type); + pr_cont("unknown type %d", ctrl->type); break; } if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_GRABBED | V4L2_CTRL_FLAG_VOLATILE)) { if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) - printk(KERN_CONT " inactive"); + pr_cont(" inactive"); if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED) - printk(KERN_CONT " grabbed"); + pr_cont(" grabbed"); if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) - printk(KERN_CONT " volatile"); + pr_cont(" volatile"); } - printk(KERN_CONT "\n"); + pr_cont("\n"); } /* Log all controls owned by the handler */ -- cgit v0.10.2 From 7e6a68210784dcea8e39fd9d4c9966f9c733ba09 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 27 Mar 2014 11:25:45 -0300 Subject: [media] videodev2.h: add initial support for compound controls Compound controls are controls that can be used for compound and array types. This allows for more compound data structures to be used with the control framework. The existing V4L2_CTRL_FLAG_NEXT_CTRL flag will only enumerate non-compound controls, so a new V4L2_CTRL_FLAG_NEXT_COMPOUND flag is added to enumerate compound controls. Set both flags to enumerate any control (compound or not). Compound control types will start at V4L2_CTRL_COMPOUND_TYPES. In addition, any control that uses the new 'ptr' field or the existing 'string' field will have flag V4L2_CTRL_FLAG_HAS_PAYLOAD set. While not strictly necessary, adding that flag makes life for applications a lot simpler. If the flag is not set, then the control value is set through the value or value64 fields of struct v4l2_ext_control, otherwise a pointer points to the value. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 168ff50..438c4a6 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1254,6 +1254,7 @@ struct v4l2_ext_control { __s32 value; __s64 value64; char *string; + void *ptr; }; } __attribute__ ((packed)); @@ -1278,7 +1279,10 @@ enum v4l2_ctrl_type { V4L2_CTRL_TYPE_CTRL_CLASS = 6, V4L2_CTRL_TYPE_STRING = 7, V4L2_CTRL_TYPE_BITMASK = 8, - V4L2_CTRL_TYPE_INTEGER_MENU = 9, + V4L2_CTRL_TYPE_INTEGER_MENU = 9, + + /* Compound types are >= 0x0100 */ + V4L2_CTRL_COMPOUND_TYPES = 0x0100, }; /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ @@ -1314,9 +1318,11 @@ struct v4l2_querymenu { #define V4L2_CTRL_FLAG_SLIDER 0x0020 #define V4L2_CTRL_FLAG_WRITE_ONLY 0x0040 #define V4L2_CTRL_FLAG_VOLATILE 0x0080 +#define V4L2_CTRL_FLAG_HAS_PAYLOAD 0x0100 -/* Query flag, to be ORed with the control ID */ +/* Query flags, to be ORed with the control ID */ #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000 +#define V4L2_CTRL_FLAG_NEXT_COMPOUND 0x40000000 /* User-class control IDs defined by V4L2 */ #define V4L2_CID_MAX_CTRLS 1024 -- cgit v0.10.2 From 5082c2417841e64df975789011e182ce99a9dacd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 04:14:50 -0300 Subject: [media] videodev2.h: add struct v4l2_query_ext_ctrl and VIDIOC_QUERY_EXT_CTRL Add a new struct and ioctl to extend the amount of information you can get for a control. The range is now a s64 type, and array dimensions and element size can be reported through nr_of_dims/dims/elems/elem_size. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 438c4a6..0297980 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1269,6 +1269,7 @@ struct v4l2_ext_controls { #define V4L2_CTRL_ID_MASK (0x0fffffff) #define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) #define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000) +#define V4L2_CTRL_MAX_DIMS (4) enum v4l2_ctrl_type { V4L2_CTRL_TYPE_INTEGER = 1, @@ -1298,6 +1299,23 @@ struct v4l2_queryctrl { __u32 reserved[2]; }; +/* Used in the VIDIOC_QUERY_EXT_CTRL ioctl for querying extended controls */ +struct v4l2_query_ext_ctrl { + __u32 id; + __u32 type; + char name[32]; + __s64 minimum; + __s64 maximum; + __u64 step; + __s64 default_value; + __u32 flags; + __u32 elem_size; + __u32 elems; + __u32 nr_of_dims; + __u32 dims[V4L2_CTRL_MAX_DIMS]; + __u32 reserved[32]; +}; + /* Used in the VIDIOC_QUERYMENU ioctl for querying menu items */ struct v4l2_querymenu { __u32 id; @@ -2011,6 +2029,8 @@ struct v4l2_create_buffers { Never use these in applications! */ #define VIDIOC_DBG_G_CHIP_INFO _IOWR('V', 102, struct v4l2_dbg_chip_info) +#define VIDIOC_QUERY_EXT_CTRL _IOWR('V', 103, struct v4l2_query_ext_ctrl) + /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ -- cgit v0.10.2 From d9a2547150245f34a050f744ea46542c44792652 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 12 Jun 2014 07:54:16 -0300 Subject: [media] v4l2-ctrls: add support for compound types This patch implements initial support for compound types. The changes are fairly obvious: basic support for is_ptr types, the type_is_int function is replaced by a is_int bitfield, and v4l2_query_ext_ctrl is added. Note that this patch does not yet add support for N-dimensional arrays, that comes later. So v4l2_query_ext_ctrl just sets elems to 1 and nr_of_dims and dims[] are all zero. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 365884b..d5cd1aa 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1124,20 +1124,6 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, } EXPORT_SYMBOL(v4l2_ctrl_fill); -/* Helper function to determine whether the control type is compatible with - VIDIOC_G/S_CTRL. */ -static bool type_is_int(const struct v4l2_ctrl *ctrl) -{ - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER64: - case V4L2_CTRL_TYPE_STRING: - /* Nope, these need v4l2_ext_control */ - return false; - default: - return true; - } -} - static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes) { memset(ev->reserved, 0, sizeof(ev->reserved)); @@ -1146,7 +1132,7 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change ev->u.ctrl.changes = changes; ev->u.ctrl.type = ctrl->type; ev->u.ctrl.flags = ctrl->flags; - if (ctrl->type == V4L2_CTRL_TYPE_STRING) + if (ctrl->is_ptr) ev->u.ctrl.value64 = 0; else ev->u.ctrl.value64 = ctrl->cur.val64; @@ -1181,6 +1167,9 @@ static int cur_to_user(struct v4l2_ext_control *c, { u32 len; + if (ctrl->is_ptr && !ctrl->is_string) + return copy_to_user(c->ptr, ctrl->cur.p, ctrl->elem_size); + switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: len = strlen(ctrl->cur.string); @@ -1208,6 +1197,9 @@ static int user_to_new(struct v4l2_ext_control *c, u32 size; ctrl->is_new = 1; + if (ctrl->is_ptr && !ctrl->is_string) + return copy_from_user(ctrl->p, c->ptr, ctrl->elem_size); + switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER64: ctrl->val64 = c->value64; @@ -1242,6 +1234,9 @@ static int new_to_user(struct v4l2_ext_control *c, { u32 len; + if (ctrl->is_ptr && !ctrl->is_string) + return copy_to_user(c->ptr, ctrl->p, ctrl->elem_size); + switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: len = strlen(ctrl->string); @@ -1268,6 +1263,7 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) if (ctrl == NULL) return; + switch (ctrl->type) { case V4L2_CTRL_TYPE_BUTTON: changed = true; @@ -1282,8 +1278,13 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) ctrl->cur.val64 = ctrl->val64; break; default: - changed = ctrl->val != ctrl->cur.val; - ctrl->cur.val = ctrl->val; + if (ctrl->is_ptr) { + changed = memcmp(ctrl->p, ctrl->cur.p, ctrl->elem_size); + memcpy(ctrl->cur.p, ctrl->p, ctrl->elem_size); + } else { + changed = ctrl->val != ctrl->cur.val; + ctrl->cur.val = ctrl->val; + } break; } if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { @@ -1323,7 +1324,10 @@ static void cur_to_new(struct v4l2_ctrl *ctrl) ctrl->val64 = ctrl->cur.val64; break; default: - ctrl->val = ctrl->cur.val; + if (ctrl->is_ptr) + memcpy(ctrl->p, ctrl->cur.p, ctrl->elem_size); + else + ctrl->val = ctrl->cur.val; break; } } @@ -1536,7 +1540,7 @@ static struct v4l2_ctrl_ref *find_private_ref( VIDIOC_G/S_CTRL. */ if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER && V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) { - if (!type_is_int(ref->ctrl)) + if (!ref->ctrl->is_int) continue; if (id == 0) return ref; @@ -1606,8 +1610,12 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl, u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1; int bucket = id % hdl->nr_of_buckets; /* which bucket to use */ - /* Automatically add the control class if it is not yet present. */ - if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL) + /* + * Automatically add the control class if it is not yet present and + * the new control is not a compound control. + */ + if (ctrl->type < V4L2_CTRL_COMPOUND_TYPES && + id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL) if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0)) return hdl->error; @@ -1668,18 +1676,28 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const char *name, enum v4l2_ctrl_type type, s64 min, s64 max, u64 step, s64 def, + u32 elem_size, u32 flags, const char * const *qmenu, const s64 *qmenu_int, void *priv) { struct v4l2_ctrl *ctrl; - unsigned sz_extra = 0; + unsigned sz_extra; + void *data; int err; if (hdl->error) return NULL; + if (type == V4L2_CTRL_TYPE_INTEGER64) + elem_size = sizeof(s64); + else if (type == V4L2_CTRL_TYPE_STRING) + elem_size = max + 1; + else if (type < V4L2_CTRL_COMPOUND_TYPES) + elem_size = sizeof(s32); + /* Sanity checks */ if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE || + elem_size == 0 || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) { handler_set_err(hdl, -ERANGE); @@ -1695,12 +1713,13 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, return NULL; } + sz_extra = 0; if (type == V4L2_CTRL_TYPE_BUTTON) flags |= V4L2_CTRL_FLAG_WRITE_ONLY; else if (type == V4L2_CTRL_TYPE_CTRL_CLASS) flags |= V4L2_CTRL_FLAG_READ_ONLY; - else if (type == V4L2_CTRL_TYPE_STRING) - sz_extra += 2 * (max + 1); + else if (type == V4L2_CTRL_TYPE_STRING || type >= V4L2_CTRL_COMPOUND_TYPES) + sz_extra += 2 * elem_size; ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); if (ctrl == NULL) { @@ -1719,18 +1738,28 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; + ctrl->default_value = def; + ctrl->is_string = type == V4L2_CTRL_TYPE_STRING; + ctrl->is_ptr = type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; + ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; + ctrl->elem_size = elem_size; if (type == V4L2_CTRL_TYPE_MENU) ctrl->qmenu = qmenu; else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) ctrl->qmenu_int = qmenu_int; ctrl->priv = priv; - ctrl->cur.val = ctrl->val = ctrl->default_value = def; + ctrl->cur.val = ctrl->val = def; + data = &ctrl->cur + 1; + + if (ctrl->is_string) { + ctrl->string = data; + ctrl->cur.string = data + elem_size; - if (ctrl->type == V4L2_CTRL_TYPE_STRING) { - ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1); - ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1); if (ctrl->minimum) memset(ctrl->cur.string, ' ', ctrl->minimum); + } else if (ctrl->is_ptr) { + ctrl->p = data; + ctrl->cur.p = data + elem_size; } if (handler_new_ref(hdl, ctrl)) { kfree(ctrl); @@ -1778,7 +1807,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, - def, flags, qmenu, qmenu_int, priv); + def, cfg->elem_size, + flags, qmenu, qmenu_int, priv); if (ctrl) ctrl->is_private = cfg->is_private; return ctrl; @@ -1795,13 +1825,15 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type == V4L2_CTRL_TYPE_MENU - || type == V4L2_CTRL_TYPE_INTEGER_MENU) { + if (type == V4L2_CTRL_TYPE_MENU || + type == V4L2_CTRL_TYPE_INTEGER_MENU || + type >= V4L2_CTRL_COMPOUND_TYPES) { handler_set_err(hdl, -EINVAL); return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - min, max, step, def, flags, NULL, NULL, NULL); + min, max, step, def, 0, + flags, NULL, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -1833,7 +1865,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, mask, def, flags, qmenu, qmenu_int, NULL); + 0, max, mask, def, 0, + flags, qmenu, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -1864,7 +1897,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, 0, max, mask, def, - flags, qmenu, NULL, NULL); + 0, flags, qmenu, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items); @@ -1888,7 +1921,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, 0, def, flags, NULL, qmenu_int, NULL); + 0, max, 0, def, 0, + flags, NULL, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); @@ -2177,9 +2211,10 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) } EXPORT_SYMBOL(v4l2_ctrl_handler_setup); -/* Implement VIDIOC_QUERYCTRL */ -int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) +/* Implement VIDIOC_QUERY_EXT_CTRL */ +int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctrl *qc) { + const unsigned next_flags = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND; u32 id = qc->id & V4L2_CTRL_ID_MASK; struct v4l2_ctrl_ref *ref; struct v4l2_ctrl *ctrl; @@ -2192,7 +2227,20 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) /* Try to find it */ ref = find_ref(hdl, id); - if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) { + if ((qc->id & next_flags) && !list_empty(&hdl->ctrl_refs)) { + bool is_compound; + /* Match any control that is not hidden */ + unsigned mask = 1; + bool match = false; + + if ((qc->id & next_flags) == V4L2_CTRL_FLAG_NEXT_COMPOUND) { + /* Match any hidden control */ + match = true; + } else if ((qc->id & next_flags) == next_flags) { + /* Match any control, compound or not */ + mask = 0; + } + /* Find the next control with ID > qc->id */ /* Did we reach the end of the control list? */ @@ -2200,19 +2248,34 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) ref = NULL; /* Yes, so there is no next control */ } else if (ref) { /* We found a control with the given ID, so just get - the next one in the list. */ - ref = list_entry(ref->node.next, typeof(*ref), node); + the next valid one in the list. */ + list_for_each_entry_continue(ref, &hdl->ctrl_refs, node) { + is_compound = + ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES; + if (id < ref->ctrl->id && + (is_compound & mask) == match) + break; + } + if (&ref->node == &hdl->ctrl_refs) + ref = NULL; } else { /* No control with the given ID exists, so start searching for the next largest ID. We know there is one, otherwise the first 'if' above would have been true. */ - list_for_each_entry(ref, &hdl->ctrl_refs, node) - if (id < ref->ctrl->id) + list_for_each_entry(ref, &hdl->ctrl_refs, node) { + is_compound = + ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES; + if (id < ref->ctrl->id && + (is_compound & mask) == match) break; + } + if (&ref->node == &hdl->ctrl_refs) + ref = NULL; } } mutex_unlock(hdl->lock); + if (!ref) return -EINVAL; @@ -2223,6 +2286,12 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) else qc->id = ctrl->id; strlcpy(qc->name, ctrl->name, sizeof(qc->name)); + qc->flags = ctrl->flags; + qc->type = ctrl->type; + if (ctrl->is_ptr) + qc->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD; + qc->elem_size = ctrl->elem_size; + qc->elems = 1; qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; @@ -2231,15 +2300,50 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) qc->step = 1; else qc->step = ctrl->step; - qc->flags = ctrl->flags; - qc->type = ctrl->type; + return 0; +} +EXPORT_SYMBOL(v4l2_query_ext_ctrl); + +/* Implement VIDIOC_QUERYCTRL */ +int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) +{ + struct v4l2_query_ext_ctrl qec = { qc->id }; + int rc; + + rc = v4l2_query_ext_ctrl(hdl, &qec); + if (rc) + return rc; + + qc->id = qec.id; + qc->type = qec.type; + qc->flags = qec.flags; + strlcpy(qc->name, qec.name, sizeof(qc->name)); + switch (qc->type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_STRING: + case V4L2_CTRL_TYPE_BITMASK: + qc->minimum = qec.minimum; + qc->maximum = qec.maximum; + qc->step = qec.step; + qc->default_value = qec.default_value; + break; + default: + qc->minimum = 0; + qc->maximum = 0; + qc->step = 0; + qc->default_value = 0; + break; + } return 0; } EXPORT_SYMBOL(v4l2_queryctrl); int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { - if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) + if (qc->id & (V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND)) return -EINVAL; return v4l2_queryctrl(sd->ctrl_handler, qc); } @@ -2339,7 +2443,8 @@ EXPORT_SYMBOL(v4l2_subdev_querymenu); Find the controls in the control array and do some basic checks. */ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs, - struct v4l2_ctrl_helper *helpers) + struct v4l2_ctrl_helper *helpers, + bool get) { struct v4l2_ctrl_helper *h; bool have_clusters = false; @@ -2371,6 +2476,13 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, have_clusters = true; if (ctrl->cluster[0] != ctrl) ref = find_ref_lock(hdl, ctrl->cluster[0]->id); + if (ctrl->is_ptr && !ctrl->is_string && c->size < ctrl->elem_size) { + if (get) { + c->size = ctrl->elem_size; + return -ENOSPC; + } + return -EFAULT; + } /* Store the ref to the master control of the cluster */ h->mref = ref; h->ctrl = ctrl; @@ -2451,7 +2563,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs return -ENOMEM; } - ret = prepare_ext_ctrls(hdl, cs, helpers); + ret = prepare_ext_ctrls(hdl, cs, helpers, true); cs->error_idx = cs->count; for (i = 0; !ret && i < cs->count; i++) @@ -2513,11 +2625,11 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) int ret = 0; int i; - /* String controls are not supported. The new_to_user() and + /* Compound controls are not supported. The new_to_user() and * cur_to_user() calls below would need to be modified not to access * userspace memory when called from get_ctrl(). */ - if (ctrl->type == V4L2_CTRL_TYPE_STRING) + if (!ctrl->is_int) return -EINVAL; if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) @@ -2543,7 +2655,7 @@ int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control) struct v4l2_ext_control c; int ret; - if (ctrl == NULL || !type_is_int(ctrl)) + if (ctrl == NULL || !ctrl->is_int) return -EINVAL; ret = get_ctrl(ctrl, &c); control->value = c.value; @@ -2562,7 +2674,7 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(!type_is_int(ctrl)); + WARN_ON(!ctrl->is_int); c.value = 0; get_ctrl(ctrl, &c); return c.value; @@ -2698,7 +2810,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, if (!helpers) return -ENOMEM; } - ret = prepare_ext_ctrls(hdl, cs, helpers); + ret = prepare_ext_ctrls(hdl, cs, helpers, false); if (!ret) ret = validate_ctrls(cs, helpers, set); if (ret && set) @@ -2803,11 +2915,11 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, struct v4l2_ctrl *master = ctrl->cluster[0]; int i; - /* String controls are not supported. The user_to_new() and + /* Compound controls are not supported. The user_to_new() and * cur_to_user() calls below would need to be modified not to access * userspace memory when called from set_ctrl(). */ - if (ctrl->type == V4L2_CTRL_TYPE_STRING) + if (ctrl->is_ptr) return -EINVAL; /* Reset the 'is_new' flags of the cluster */ @@ -2849,7 +2961,7 @@ int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, struct v4l2_ext_control c; int ret; - if (ctrl == NULL || !type_is_int(ctrl)) + if (ctrl == NULL || !ctrl->is_int) return -EINVAL; if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) @@ -2873,7 +2985,7 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(!type_is_int(ctrl)); + WARN_ON(!ctrl->is_int); c.value = val; return set_ctrl_lock(NULL, ctrl, &c); } diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index a2e8f03..9024dae 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -73,6 +73,12 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * members are in 'automatic' mode or 'manual' mode. This is * used for autogain/gain type clusters. Drivers should never * set this flag directly. + * @is_int: If set, then this control has a simple integer value (i.e. it + * uses ctrl->val). + * @is_string: If set, then this control has type V4L2_CTRL_TYPE_STRING. + * @is_ptr: If set, then this control is an array and/or has type >= V4L2_CTRL_COMPOUND_TYPES + * and/or has type V4L2_CTRL_TYPE_STRING. In other words, struct + * v4l2_ext_control uses field p to point to the data. * @has_volatiles: If set, then one or more members of the cluster are volatile. * Drivers should never touch this flag. * @call_notify: If set, then call the handler's notify function whenever the @@ -90,6 +96,7 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @maximum: The control's maximum value. * @default_value: The control's default value. * @step: The control's step value for non-menu controls. + * @elem_size: The size in bytes of the control. * @menu_skip_mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for @@ -104,7 +111,6 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @cur: The control's current value. * @val: The control's new s32 value. * @val64: The control's new s64 value. - * @string: The control's new string value. * @priv: The control's private pointer. For use by the driver. It is * untouched by the control framework. Note that this pointer is * not freed when the control is deleted. Should this be needed @@ -123,6 +129,9 @@ struct v4l2_ctrl { unsigned int is_new:1; unsigned int is_private:1; unsigned int is_auto:1; + unsigned int is_int:1; + unsigned int is_string:1; + unsigned int is_ptr:1; unsigned int has_volatiles:1; unsigned int call_notify:1; unsigned int manual_mode_value:8; @@ -132,6 +141,7 @@ struct v4l2_ctrl { const char *name; enum v4l2_ctrl_type type; s64 minimum, maximum, default_value; + u32 elem_size; union { u64 step; u64 menu_skip_mask; @@ -141,17 +151,19 @@ struct v4l2_ctrl { const s64 *qmenu_int; }; unsigned long flags; + void *priv; union { s32 val; s64 val64; char *string; - } cur; + void *p; + }; union { s32 val; s64 val64; char *string; - }; - void *priv; + void *p; + } cur; }; /** struct v4l2_ctrl_ref - The control reference. @@ -212,6 +224,7 @@ struct v4l2_ctrl_handler { * @max: The control's maximum value. * @step: The control's step value for non-menu controls. * @def: The control's default value. + * @elem_size: The size in bytes of the control. * @flags: The control's flags. * @menu_skip_mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, @@ -235,6 +248,7 @@ struct v4l2_ctrl_config { s64 max; u64 step; s64 def; + u32 elem_size; u32 flags; u64 menu_skip_mask; const char * const *qmenu; @@ -659,6 +673,7 @@ unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait); /* Helpers for ioctl_ops. If hdl == NULL then they will all return -EINVAL. */ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc); +int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctrl *qc); int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm); int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl); int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, -- cgit v0.10.2 From e6bee3685e732df82f48698254a36754cf15f0b0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 04:22:06 -0300 Subject: [media] v4l2: integrate support for VIDIOC_QUERY_EXT_CTRL Add the v4l2 core plumbing for the new VIDIOC_QUERY_EXT_CTRL ioctl. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 35698aa..1cfdbdd 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -575,6 +575,8 @@ static void determine_valid_ioctls(struct video_device *vdev) be valid if the filehandle passed the control handler. */ if (vdev->ctrl_handler || ops->vidioc_queryctrl) set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_query_ext_ctrl) + set_bit(_IOC_NR(VIDIOC_QUERY_EXT_CTRL), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls) set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 8d4a25d..c38a620 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -525,6 +525,21 @@ static void v4l_print_queryctrl(const void *arg, bool write_only) p->step, p->default_value, p->flags); } +static void v4l_print_query_ext_ctrl(const void *arg, bool write_only) +{ + const struct v4l2_query_ext_ctrl *p = arg; + + pr_cont("id=0x%x, type=%d, name=%.*s, min/max=%lld/%lld, " + "step=%lld, default=%lld, flags=0x%08x, elem_size=%u, elems=%u, " + "nr_of_dims=%u, dims=%u,%u,%u,%u,%u,%u,%u,%u\n", + p->id, p->type, (int)sizeof(p->name), p->name, + p->minimum, p->maximum, + p->step, p->default_value, p->flags, + p->elem_size, p->elems, p->nr_of_dims, + p->dims[0], p->dims[1], p->dims[2], p->dims[3], + p->dims[4], p->dims[5], p->dims[6], p->dims[7]); +} + static void v4l_print_querymenu(const void *arg, bool write_only) { const struct v4l2_querymenu *p = arg; @@ -1561,6 +1576,23 @@ static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops, return -ENOTTY; } +static int v4l_query_ext_ctrl(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_query_ext_ctrl *p = arg; + struct v4l2_fh *vfh = + test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; + + if (vfh && vfh->ctrl_handler) + return v4l2_query_ext_ctrl(vfh->ctrl_handler, p); + if (vfd->ctrl_handler) + return v4l2_query_ext_ctrl(vfd->ctrl_handler, p); + if (ops->vidioc_query_ext_ctrl) + return ops->vidioc_query_ext_ctrl(file, fh, p); + return -ENOTTY; +} + static int v4l_querymenu(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -2121,6 +2153,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)), IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)), + IOCTL_INFO_FNC(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 058c1a6..b984f33d 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -139,6 +139,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_QUERYCTRL: return v4l2_queryctrl(vfh->ctrl_handler, arg); + case VIDIOC_QUERY_EXT_CTRL: + return v4l2_query_ext_ctrl(vfh->ctrl_handler, arg); + case VIDIOC_QUERYMENU: return v4l2_querymenu(vfh->ctrl_handler, arg); diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 50cf7c1..53605f0 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -158,6 +158,8 @@ struct v4l2_ioctl_ops { /* Control handling */ int (*vidioc_queryctrl) (struct file *file, void *fh, struct v4l2_queryctrl *a); + int (*vidioc_query_ext_ctrl) (struct file *file, void *fh, + struct v4l2_query_ext_ctrl *a); int (*vidioc_g_ctrl) (struct file *file, void *fh, struct v4l2_control *a); int (*vidioc_s_ctrl) (struct file *file, void *fh, -- cgit v0.10.2 From 0176077a813933a547b7a913377a87d615b7c108 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 27 Apr 2014 03:22:17 -0300 Subject: [media] v4l2-ctrls: create type_ops Since compound controls can have non-standard types we need to be able to do type-specific checks etc. In order to make that easy type operations are added. There are four operations: - equal: check if two values are equal - init: initialize a value - log: log the value - validate: validate a new value The v4l2_ctrl struct adds p_new and p_cur unions at the end of the struct. This union provides a standard way of accessing control types through a pointer, which greatly simplifies internal control processing. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index d5cd1aa..09e2c3a 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1161,6 +1161,149 @@ static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes) v4l2_event_queue_fh(sev->fh, &ev); } +static bool std_equal(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr1, + union v4l2_ctrl_ptr ptr2) +{ + switch (ctrl->type) { + case V4L2_CTRL_TYPE_BUTTON: + return false; + case V4L2_CTRL_TYPE_STRING: + /* strings are always 0-terminated */ + return !strcmp(ptr1.p_char, ptr2.p_char); + case V4L2_CTRL_TYPE_INTEGER64: + return *ptr1.p_s64 == *ptr2.p_s64; + default: + if (ctrl->is_ptr) + return !memcmp(ptr1.p, ptr2.p, ctrl->elem_size); + return *ptr1.p_s32 == *ptr2.p_s32; + } +} + +static void std_init(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr) +{ + switch (ctrl->type) { + case V4L2_CTRL_TYPE_STRING: + memset(ptr.p_char, ' ', ctrl->minimum); + ptr.p_char[ctrl->minimum] = '\0'; + break; + case V4L2_CTRL_TYPE_INTEGER64: + *ptr.p_s64 = ctrl->default_value; + break; + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_BITMASK: + case V4L2_CTRL_TYPE_BOOLEAN: + *ptr.p_s32 = ctrl->default_value; + break; + default: + break; + } +} + +static void std_log(const struct v4l2_ctrl *ctrl) +{ + union v4l2_ctrl_ptr ptr = ctrl->p_cur; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + pr_cont("%d", *ptr.p_s32); + break; + case V4L2_CTRL_TYPE_BOOLEAN: + pr_cont("%s", *ptr.p_s32 ? "true" : "false"); + break; + case V4L2_CTRL_TYPE_MENU: + pr_cont("%s", ctrl->qmenu[*ptr.p_s32]); + break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + pr_cont("%lld", ctrl->qmenu_int[*ptr.p_s32]); + break; + case V4L2_CTRL_TYPE_BITMASK: + pr_cont("0x%08x", *ptr.p_s32); + break; + case V4L2_CTRL_TYPE_INTEGER64: + pr_cont("%lld", *ptr.p_s64); + break; + case V4L2_CTRL_TYPE_STRING: + pr_cont("%s", ptr.p_char); + break; + default: + pr_cont("unknown type %d", ctrl->type); + break; + } +} + +/* Round towards the closest legal value */ +#define ROUND_TO_RANGE(val, offset_type, ctrl) \ +({ \ + offset_type offset; \ + val += (ctrl)->step / 2; \ + val = clamp_t(typeof(val), val, \ + (ctrl)->minimum, (ctrl)->maximum); \ + offset = (val) - (ctrl)->minimum; \ + offset = (ctrl)->step * (offset / (ctrl)->step); \ + val = (ctrl)->minimum + offset; \ + 0; \ +}) + +/* Validate a new control */ +static int std_validate(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr) +{ + size_t len; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + return ROUND_TO_RANGE(*ptr.p_s32, u32, ctrl); + case V4L2_CTRL_TYPE_INTEGER64: + return ROUND_TO_RANGE(*ptr.p_s64, u64, ctrl); + + case V4L2_CTRL_TYPE_BOOLEAN: + *ptr.p_s32 = !!*ptr.p_s32; + return 0; + + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + if (*ptr.p_s32 < ctrl->minimum || *ptr.p_s32 > ctrl->maximum) + return -ERANGE; + if (ctrl->menu_skip_mask & (1 << *ptr.p_s32)) + return -EINVAL; + if (ctrl->type == V4L2_CTRL_TYPE_MENU && + ctrl->qmenu[*ptr.p_s32][0] == '\0') + return -EINVAL; + return 0; + + case V4L2_CTRL_TYPE_BITMASK: + *ptr.p_s32 &= ctrl->maximum; + return 0; + + case V4L2_CTRL_TYPE_BUTTON: + case V4L2_CTRL_TYPE_CTRL_CLASS: + *ptr.p_s32 = 0; + return 0; + + case V4L2_CTRL_TYPE_STRING: + len = strlen(ptr.p_char); + if (len < ctrl->minimum) + return -ERANGE; + if ((len - ctrl->minimum) % ctrl->step) + return -ERANGE; + return 0; + + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_type_ops std_type_ops = { + .equal = std_equal, + .init = std_init, + .log = std_log, + .validate = std_validate, +}; + /* Helper function: copy the current control value back to the caller */ static int cur_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) @@ -1344,21 +1487,7 @@ static int cluster_changed(struct v4l2_ctrl *master) if (ctrl == NULL) continue; - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BUTTON: - /* Button controls are always 'different' */ - return 1; - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - diff = strcmp(ctrl->string, ctrl->cur.string); - break; - case V4L2_CTRL_TYPE_INTEGER64: - diff = ctrl->val64 != ctrl->cur.val64; - break; - default: - diff = ctrl->val != ctrl->cur.val; - break; - } + diff = !ctrl->type_ops->equal(ctrl, ctrl->p_cur, ctrl->p_new); } return diff; } @@ -1399,65 +1528,30 @@ static int check_range(enum v4l2_ctrl_type type, } } -/* Round towards the closest legal value */ -#define ROUND_TO_RANGE(val, offset_type, ctrl) \ -({ \ - offset_type offset; \ - val += (ctrl)->step / 2; \ - val = clamp_t(typeof(val), val, \ - (ctrl)->minimum, (ctrl)->maximum); \ - offset = (val) - (ctrl)->minimum; \ - offset = (ctrl)->step * (offset / (ctrl)->step); \ - val = (ctrl)->minimum + offset; \ - 0; \ -}) - /* Validate a new control */ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) { - size_t len; + union v4l2_ctrl_ptr ptr; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - return ROUND_TO_RANGE(*(s32 *)&c->value, u32, ctrl); - case V4L2_CTRL_TYPE_INTEGER64: - return ROUND_TO_RANGE(*(s64 *)&c->value64, u64, ctrl); - - case V4L2_CTRL_TYPE_BOOLEAN: - c->value = !!c->value; - return 0; - - case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: - if (c->value < ctrl->minimum || c->value > ctrl->maximum) - return -ERANGE; - if (ctrl->menu_skip_mask & (1 << c->value)) - return -EINVAL; - if (ctrl->type == V4L2_CTRL_TYPE_MENU && - ctrl->qmenu[c->value][0] == '\0') - return -EINVAL; - return 0; - + case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_BITMASK: - c->value &= ctrl->maximum; - return 0; - + case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: - c->value = 0; - return 0; + ptr.p_s32 = &c->value; + return ctrl->type_ops->validate(ctrl, ptr); - case V4L2_CTRL_TYPE_STRING: - len = strlen(c->string); - if (len < ctrl->minimum) - return -ERANGE; - if ((len - ctrl->minimum) % ctrl->step) - return -ERANGE; - return 0; + case V4L2_CTRL_TYPE_INTEGER64: + ptr.p_s64 = &c->value64; + return ctrl->type_ops->validate(ctrl, ptr); default: - return -EINVAL; + ptr.p = c->ptr; + return ctrl->type_ops->validate(ctrl, ptr); } } @@ -1674,6 +1768,7 @@ unlock: /* Add a new control */ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, + const struct v4l2_ctrl_type_ops *type_ops, u32 id, const char *name, enum v4l2_ctrl_type type, s64 min, s64 max, u64 step, s64 def, u32 elem_size, @@ -1731,6 +1826,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, INIT_LIST_HEAD(&ctrl->ev_subs); ctrl->handler = hdl; ctrl->ops = ops; + ctrl->type_ops = type_ops ? type_ops : &std_type_ops; ctrl->id = id; ctrl->name = name; ctrl->type = type; @@ -1751,16 +1847,16 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->cur.val = ctrl->val = def; data = &ctrl->cur + 1; - if (ctrl->is_string) { - ctrl->string = data; - ctrl->cur.string = data + elem_size; - - if (ctrl->minimum) - memset(ctrl->cur.string, ' ', ctrl->minimum); - } else if (ctrl->is_ptr) { - ctrl->p = data; - ctrl->cur.p = data + elem_size; + if (ctrl->is_ptr) { + ctrl->p = ctrl->p_new.p = data; + ctrl->p_cur.p = data + elem_size; + } else { + ctrl->p_new.p = &ctrl->val; + ctrl->p_cur.p = &ctrl->cur.val; } + ctrl->type_ops->init(ctrl, ctrl->p_cur); + ctrl->type_ops->init(ctrl, ctrl->p_new); + if (handler_new_ref(hdl, ctrl)) { kfree(ctrl); return NULL; @@ -1804,7 +1900,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, return NULL; } - ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, + ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->type_ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, def, cfg->elem_size, @@ -1831,7 +1927,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, min, max, step, def, 0, flags, NULL, NULL, NULL); } @@ -1864,7 +1960,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, 0, max, mask, def, 0, flags, qmenu, qmenu_int, NULL); } @@ -1896,7 +1992,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, 0, max, mask, def, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, + 0, max, mask, def, 0, flags, qmenu, NULL, NULL); } @@ -1920,7 +2017,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, 0, max, 0, def, 0, flags, NULL, qmenu_int, NULL); } @@ -2104,32 +2201,8 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl, pr_info("%s%s%s: ", prefix, colon, ctrl->name); - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER: - pr_cont("%d", ctrl->cur.val); - break; - case V4L2_CTRL_TYPE_BOOLEAN: - pr_cont("%s", ctrl->cur.val ? "true" : "false"); - break; - case V4L2_CTRL_TYPE_MENU: - pr_cont("%s", ctrl->qmenu[ctrl->cur.val]); - break; - case V4L2_CTRL_TYPE_INTEGER_MENU: - pr_cont("%lld", ctrl->qmenu_int[ctrl->cur.val]); - break; - case V4L2_CTRL_TYPE_BITMASK: - pr_cont("0x%08x", ctrl->cur.val); - break; - case V4L2_CTRL_TYPE_INTEGER64: - pr_cont("%lld", ctrl->cur.val64); - break; - case V4L2_CTRL_TYPE_STRING: - pr_cont("%s", ctrl->cur.string); - break; - default: - pr_cont("unknown type %d", ctrl->type); - break; - } + ctrl->type_ops->log(ctrl); + if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_GRABBED | V4L2_CTRL_FLAG_VOLATILE)) { diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index c38a620..96bc117 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -531,13 +531,12 @@ static void v4l_print_query_ext_ctrl(const void *arg, bool write_only) pr_cont("id=0x%x, type=%d, name=%.*s, min/max=%lld/%lld, " "step=%lld, default=%lld, flags=0x%08x, elem_size=%u, elems=%u, " - "nr_of_dims=%u, dims=%u,%u,%u,%u,%u,%u,%u,%u\n", + "nr_of_dims=%u, dims=%u,%u,%u,%u\n", p->id, p->type, (int)sizeof(p->name), p->name, p->minimum, p->maximum, p->step, p->default_value, p->flags, p->elem_size, p->elems, p->nr_of_dims, - p->dims[0], p->dims[1], p->dims[2], p->dims[3], - p->dims[4], p->dims[5], p->dims[6], p->dims[7]); + p->dims[0], p->dims[1], p->dims[2], p->dims[3]); } static void v4l_print_querymenu(const void *arg, bool write_only) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 9024dae..ddd9fdf 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -36,6 +36,19 @@ struct v4l2_subscribed_event; struct v4l2_fh; struct poll_table_struct; +/** union v4l2_ctrl_ptr - A pointer to a control value. + * @p_s32: Pointer to a 32-bit signed value. + * @p_s64: Pointer to a 64-bit signed value. + * @p_char: Pointer to a string. + * @p: Pointer to a compound value. + */ +union v4l2_ctrl_ptr { + s32 *p_s32; + s64 *p_s64; + char *p_char; + void *p; +}; + /** struct v4l2_ctrl_ops - The control operations that the driver has to provide. * @g_volatile_ctrl: Get a new value for this control. Generally only relevant * for volatile (and usually read-only) controls such as a control @@ -54,6 +67,23 @@ struct v4l2_ctrl_ops { int (*s_ctrl)(struct v4l2_ctrl *ctrl); }; +/** struct v4l2_ctrl_type_ops - The control type operations that the driver has to provide. + * @equal: return true if both values are equal. + * @init: initialize the value. + * @log: log the value. + * @validate: validate the value. Return 0 on success and a negative value otherwise. + */ +struct v4l2_ctrl_type_ops { + bool (*equal)(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr1, + union v4l2_ctrl_ptr ptr2); + void (*init)(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr); + void (*log)(const struct v4l2_ctrl *ctrl); + int (*validate)(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr); +}; + typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); /** struct v4l2_ctrl - The control structure. @@ -89,6 +119,7 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * value, then the whole cluster is in manual mode. Drivers should * never set this flag directly. * @ops: The control ops. + * @type_ops: The control type ops. * @id: The control ID. * @name: The control name. * @type: The control type. @@ -137,6 +168,7 @@ struct v4l2_ctrl { unsigned int manual_mode_value:8; const struct v4l2_ctrl_ops *ops; + const struct v4l2_ctrl_type_ops *type_ops; u32 id; const char *name; enum v4l2_ctrl_type type; @@ -164,6 +196,9 @@ struct v4l2_ctrl { char *string; void *p; } cur; + + union v4l2_ctrl_ptr p_new; + union v4l2_ctrl_ptr p_cur; }; /** struct v4l2_ctrl_ref - The control reference. @@ -217,6 +252,7 @@ struct v4l2_ctrl_handler { /** struct v4l2_ctrl_config - Control configuration structure. * @ops: The control ops. + * @type_ops: The control type ops. Only needed for compound controls. * @id: The control ID. * @name: The control name. * @type: The control type. @@ -241,6 +277,7 @@ struct v4l2_ctrl_handler { */ struct v4l2_ctrl_config { const struct v4l2_ctrl_ops *ops; + const struct v4l2_ctrl_type_ops *type_ops; u32 id; const char *name; enum v4l2_ctrl_type type; -- cgit v0.10.2 From 000e4f9a5bcf86fb52914c445ce5634b65e910a2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 27 Apr 2014 03:26:30 -0300 Subject: [media] v4l2-ctrls: rewrite copy routines to operate on union v4l2_ctrl_ptr In order to implement array support and (for the future) configuration stores we need to have more generic copy routines that all operate on the v4l2_ctrl_ptr union. So instead of e.g. using ctrl->cur.string it uses ptr.p_char. This makes e.g. cur_to_user generic so it can be used to copy any v4l2_ctrl_ptr value to userspace, not just the (hardcoded) current value. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 09e2c3a..e7e0bea 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1304,48 +1304,64 @@ static const struct v4l2_ctrl_type_ops std_type_ops = { .validate = std_validate, }; -/* Helper function: copy the current control value back to the caller */ -static int cur_to_user(struct v4l2_ext_control *c, - struct v4l2_ctrl *ctrl) +/* Helper function: copy the given control value back to the caller */ +static int ptr_to_user(struct v4l2_ext_control *c, + struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr) { u32 len; if (ctrl->is_ptr && !ctrl->is_string) - return copy_to_user(c->ptr, ctrl->cur.p, ctrl->elem_size); + return copy_to_user(c->ptr, ptr.p, ctrl->elem_size); switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: - len = strlen(ctrl->cur.string); + len = strlen(ptr.p_char); if (c->size < len + 1) { c->size = len + 1; return -ENOSPC; } - return copy_to_user(c->string, ctrl->cur.string, - len + 1) ? -EFAULT : 0; + return copy_to_user(c->string, ptr.p_char, len + 1) ? + -EFAULT : 0; case V4L2_CTRL_TYPE_INTEGER64: - c->value64 = ctrl->cur.val64; + c->value64 = *ptr.p_s64; break; default: - c->value = ctrl->cur.val; + c->value = *ptr.p_s32; break; } return 0; } -/* Helper function: copy the caller-provider value as the new control value */ -static int user_to_new(struct v4l2_ext_control *c, +/* Helper function: copy the current control value back to the caller */ +static int cur_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) { + return ptr_to_user(c, ctrl, ctrl->p_cur); +} + +/* Helper function: copy the new control value back to the caller */ +static int new_to_user(struct v4l2_ext_control *c, + struct v4l2_ctrl *ctrl) +{ + return ptr_to_user(c, ctrl, ctrl->p_new); +} + +/* Helper function: copy the caller-provider value to the given control value */ +static int user_to_ptr(struct v4l2_ext_control *c, + struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr) +{ int ret; u32 size; ctrl->is_new = 1; if (ctrl->is_ptr && !ctrl->is_string) - return copy_from_user(ctrl->p, c->ptr, ctrl->elem_size); + return copy_from_user(ptr.p, c->ptr, ctrl->elem_size); switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER64: - ctrl->val64 = c->value64; + *ptr.p_s64 = c->value64; break; case V4L2_CTRL_TYPE_STRING: size = c->size; @@ -1353,83 +1369,64 @@ static int user_to_new(struct v4l2_ext_control *c, return -ERANGE; if (size > ctrl->maximum + 1) size = ctrl->maximum + 1; - ret = copy_from_user(ctrl->string, c->string, size); + ret = copy_from_user(ptr.p_char, c->string, size); if (!ret) { - char last = ctrl->string[size - 1]; + char last = ptr.p_char[size - 1]; - ctrl->string[size - 1] = 0; + ptr.p_char[size - 1] = 0; /* If the string was longer than ctrl->maximum, then return an error. */ - if (strlen(ctrl->string) == ctrl->maximum && last) + if (strlen(ptr.p_char) == ctrl->maximum && last) return -ERANGE; } return ret ? -EFAULT : 0; default: - ctrl->val = c->value; + *ptr.p_s32 = c->value; break; } return 0; } -/* Helper function: copy the new control value back to the caller */ -static int new_to_user(struct v4l2_ext_control *c, +/* Helper function: copy the caller-provider value as the new control value */ +static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) { - u32 len; - - if (ctrl->is_ptr && !ctrl->is_string) - return copy_to_user(c->ptr, ctrl->p, ctrl->elem_size); + return user_to_ptr(c, ctrl, ctrl->p_new); +} +/* Copy the one value to another. */ +static void ptr_to_ptr(struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to) +{ + if (ctrl == NULL) + return; switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: - len = strlen(ctrl->string); - if (c->size < len + 1) { - c->size = ctrl->maximum + 1; - return -ENOSPC; - } - return copy_to_user(c->string, ctrl->string, - len + 1) ? -EFAULT : 0; + /* strings are always 0-terminated */ + strcpy(to.p_char, from.p_char); + break; case V4L2_CTRL_TYPE_INTEGER64: - c->value64 = ctrl->val64; + *to.p_s64 = *from.p_s64; break; default: - c->value = ctrl->val; + if (ctrl->is_ptr) + memcpy(to.p, from.p, ctrl->elem_size); + else + *to.p_s32 = *from.p_s32; break; } - return 0; } /* Copy the new value to the current value. */ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) { - bool changed = false; + bool changed; if (ctrl == NULL) return; + changed = !ctrl->type_ops->equal(ctrl, ctrl->p_cur, ctrl->p_new); + ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur); - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BUTTON: - changed = true; - break; - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - changed = strcmp(ctrl->string, ctrl->cur.string); - strcpy(ctrl->cur.string, ctrl->string); - break; - case V4L2_CTRL_TYPE_INTEGER64: - changed = ctrl->val64 != ctrl->cur.val64; - ctrl->cur.val64 = ctrl->val64; - break; - default: - if (ctrl->is_ptr) { - changed = memcmp(ctrl->p, ctrl->cur.p, ctrl->elem_size); - memcpy(ctrl->cur.p, ctrl->p, ctrl->elem_size); - } else { - changed = ctrl->val != ctrl->cur.val; - ctrl->cur.val = ctrl->val; - } - break; - } if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { /* Note: CH_FLAGS is only set for auto clusters. */ ctrl->flags &= @@ -1458,21 +1455,7 @@ static void cur_to_new(struct v4l2_ctrl *ctrl) { if (ctrl == NULL) return; - switch (ctrl->type) { - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - strcpy(ctrl->string, ctrl->cur.string); - break; - case V4L2_CTRL_TYPE_INTEGER64: - ctrl->val64 = ctrl->cur.val64; - break; - default: - if (ctrl->is_ptr) - memcpy(ctrl->p, ctrl->cur.p, ctrl->elem_size); - else - ctrl->val = ctrl->cur.val; - break; - } + ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new); } /* Return non-zero if one or more of the controls in the cluster has a new -- cgit v0.10.2 From 9ea1b7a4b66fddfab9e65e243b72d18371f8d9a5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 17 Jan 2014 08:25:26 -0300 Subject: [media] v4l2-ctrls: compare values only once When setting a control the control's new value is compared to the current value twice: once by new_to_cur(), once by cluster_changed(). Not a big deal when dealing with simple values, but it can be a problem when dealing with compound types or arrays. So fix this: cluster_changed() sets the has_changed flag, which is used by new_to_cur() instead of having to do another compare. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e7e0bea..1b4d37c 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1424,8 +1424,11 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) if (ctrl == NULL) return; - changed = !ctrl->type_ops->equal(ctrl, ctrl->p_cur, ctrl->p_new); - ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur); + + /* has_changed is set by cluster_changed */ + changed = ctrl->has_changed; + if (changed) + ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur); if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { /* Note: CH_FLAGS is only set for auto clusters. */ @@ -1462,17 +1465,19 @@ static void cur_to_new(struct v4l2_ctrl *ctrl) value that differs from the current value. */ static int cluster_changed(struct v4l2_ctrl *master) { - int diff = 0; + bool changed = false; int i; - for (i = 0; !diff && i < master->ncontrols; i++) { + for (i = 0; i < master->ncontrols; i++) { struct v4l2_ctrl *ctrl = master->cluster[i]; if (ctrl == NULL) continue; - diff = !ctrl->type_ops->equal(ctrl, ctrl->p_cur, ctrl->p_new); + ctrl->has_changed = !ctrl->type_ops->equal(ctrl, + ctrl->p_cur, ctrl->p_new); + changed |= ctrl->has_changed; } - return diff; + return changed; } /* Control range checking */ diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index ddd9fdf..a38bd55 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -96,6 +96,8 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @is_new: Set when the user specified a new value for this control. It * is also set when called from v4l2_ctrl_handler_setup. Drivers * should never set this flag. + * @has_changed: Set when the current value differs from the new value. Drivers + * should never use this flag. * @is_private: If set, then this control is private to its handler and it * will not be added to any other handlers. Drivers can set * this flag. @@ -158,6 +160,7 @@ struct v4l2_ctrl { unsigned int done:1; unsigned int is_new:1; + unsigned int has_changed:1; unsigned int is_private:1; unsigned int is_auto:1; unsigned int is_int:1; -- cgit v0.10.2 From 2a9ec3731137f973c6289698de6566a25418b96f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 27 Apr 2014 03:38:13 -0300 Subject: [media] v4l2-ctrls: use ptrs for all but the s32 type Rather than having two unions for all types just keep 'val' and 'cur.val' and use the p_cur and p_new unions to access all others. The only reason for keeping 'val' and 'cur.val' is that it is used all over, so converting this as well would be a huge job. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index 06cf3ac..c9ee9a7 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -362,7 +362,7 @@ will result in a deadlock since these helpers lock the handler as well. You can also take the handler lock yourself: mutex_lock(&state->ctrl_handler.lock); - printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string); + pr_info("String value is '%s'\n", ctrl1->p_cur.p_char); printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val); mutex_unlock(&state->ctrl_handler.lock); diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 65e6e1a..435e9e6 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -667,7 +667,7 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) break; freq = mt9v032->pdata->link_freqs[mt9v032->link_freq->val]; - mt9v032->pixel_rate->val64 = freq; + *mt9v032->pixel_rate->p_new.p_s64 = freq; mt9v032->sysclk = freq; break; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 72350f0..13411e9 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -297,8 +297,8 @@ static int smiapp_pll_update(struct smiapp_sensor *sensor) if (rval < 0) return rval; - sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz; - sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi; + *sensor->pixel_rate_parray->p_cur.p_s64 = pll->vt_pix_clk_freq_hz; + *sensor->pixel_rate_csi->p_cur.p_s64 = pll->pixel_rate_csi; return 0; } diff --git a/drivers/media/pci/ivtv/ivtv-controls.c b/drivers/media/pci/ivtv/ivtv-controls.c index c604246..2b0ab26 100644 --- a/drivers/media/pci/ivtv/ivtv-controls.c +++ b/drivers/media/pci/ivtv/ivtv-controls.c @@ -135,8 +135,8 @@ static int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl) /* V4L2_CID_MPEG_VIDEO_DEC_PTS and V4L2_CID_MPEG_VIDEO_DEC_FRAME control cluster */ case V4L2_CID_MPEG_VIDEO_DEC_PTS: - return ivtv_g_pts_frame(itv, &itv->ctrl_pts->val64, - &itv->ctrl_frame->val64); + return ivtv_g_pts_frame(itv, itv->ctrl_pts->p_new.p_s64, + itv->ctrl_frame->p_new.p_s64); } return 0; } diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index cd535b3..7542b5d 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -648,13 +648,13 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) gen_text(dev, vbuf, line++ * 16, 16, str); snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ", dev->int32->cur.val, - dev->int64->cur.val64, + *dev->int64->p_cur.p_s64, dev->bitmask->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ", dev->boolean->cur.val, dev->menu->qmenu[dev->menu->cur.val], - dev->string->cur.string); + dev->string->p_cur.p_char); gen_text(dev, vbuf, line++ * 16, 16, str); snprintf(str, sizeof(str), " integer_menu %lld, value %d ", dev->int_menu->qmenu_int[dev->int_menu->cur.val], diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 07d5153..dbe4726 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -1098,11 +1098,11 @@ static int si4713_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_RDS_TX_PS_NAME: - ret = si4713_set_rds_ps_name(sdev, ctrl->string); + ret = si4713_set_rds_ps_name(sdev, ctrl->p_new.p_char); break; case V4L2_CID_RDS_TX_RADIO_TEXT: - ret = si4713_set_rds_radio_text(sdev, ctrl->string); + ret = si4713_set_rds_radio_text(sdev, ctrl->p_new.p_char); break; case V4L2_CID_TUNE_ANTENNA_CAPACITOR: diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 1b4d37c..4b10571 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1135,7 +1135,7 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change if (ctrl->is_ptr) ev->u.ctrl.value64 = 0; else - ev->u.ctrl.value64 = ctrl->cur.val64; + ev->u.ctrl.value64 = *ctrl->p_cur.p_s64; ev->u.ctrl.minimum = ctrl->minimum; ev->u.ctrl.maximum = ctrl->maximum; if (ctrl->type == V4L2_CTRL_TYPE_MENU @@ -1801,7 +1801,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, flags |= V4L2_CTRL_FLAG_WRITE_ONLY; else if (type == V4L2_CTRL_TYPE_CTRL_CLASS) flags |= V4L2_CTRL_FLAG_READ_ONLY; - else if (type == V4L2_CTRL_TYPE_STRING || type >= V4L2_CTRL_COMPOUND_TYPES) + else if (type == V4L2_CTRL_TYPE_INTEGER64 || + type == V4L2_CTRL_TYPE_STRING || + type >= V4L2_CTRL_COMPOUND_TYPES) sz_extra += 2 * elem_size; ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); @@ -1833,10 +1835,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->qmenu_int = qmenu_int; ctrl->priv = priv; ctrl->cur.val = ctrl->val = def; - data = &ctrl->cur + 1; + data = &ctrl[1]; - if (ctrl->is_ptr) { - ctrl->p = ctrl->p_new.p = data; + if (!ctrl->is_int) { + ctrl->p_new.p = data; ctrl->p_cur.p = data + elem_size; } else { ctrl->p_new.p = &ctrl->val; @@ -3103,10 +3105,10 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, ctrl->maximum = max; ctrl->step = step; ctrl->default_value = def; - c.value = ctrl->cur.val; + c.value = *ctrl->p_cur.p_s32; if (validate_new(ctrl, &c)) c.value = def; - if (c.value != ctrl->cur.val) + if (c.value != *ctrl->p_cur.p_s32) ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE); else send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE); diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index bb2604e..37f7fd0 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -1131,7 +1131,7 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl) solo_motion_toggle(solo_enc, ctrl->val); return 0; case V4L2_CID_OSD_TEXT: - strcpy(solo_enc->osd_text, ctrl->string); + strcpy(solo_enc->osd_text, ctrl->p_new.p_char); err = solo_osd_print(solo_enc); return err; default: diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index a38bd55..eb69c52 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -187,17 +187,9 @@ struct v4l2_ctrl { }; unsigned long flags; void *priv; - union { - s32 val; - s64 val64; - char *string; - void *p; - }; - union { + s32 val; + struct { s32 val; - s64 val64; - char *string; - void *p; } cur; union v4l2_ctrl_ptr p_new; -- cgit v0.10.2 From 20d88eef66a86989ea3cffe2a4e0d16cbf2d4563 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 12 Jun 2014 07:55:21 -0300 Subject: [media] v4l2-ctrls: prepare for array support Add dims, nr_of_dims and elems fields to the core control structures in preparation for N-dimensional array support. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 4b10571..e6c98a7 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1759,18 +1759,27 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_type_ops *type_ops, u32 id, const char *name, enum v4l2_ctrl_type type, s64 min, s64 max, u64 step, s64 def, - u32 elem_size, + const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size, u32 flags, const char * const *qmenu, const s64 *qmenu_int, void *priv) { struct v4l2_ctrl *ctrl; unsigned sz_extra; + unsigned nr_of_dims = 0; + unsigned elems = 1; void *data; int err; if (hdl->error) return NULL; + while (dims && dims[nr_of_dims]) { + elems *= dims[nr_of_dims]; + nr_of_dims++; + if (nr_of_dims == V4L2_CTRL_MAX_DIMS) + break; + } + if (type == V4L2_CTRL_TYPE_INTEGER64) elem_size = sizeof(s64); else if (type == V4L2_CTRL_TYPE_STRING) @@ -1828,6 +1837,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->is_string = type == V4L2_CTRL_TYPE_STRING; ctrl->is_ptr = type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; + ctrl->elems = elems; + ctrl->nr_of_dims = nr_of_dims; + if (nr_of_dims) + memcpy(ctrl->dims, dims, nr_of_dims * sizeof(dims[0])); ctrl->elem_size = elem_size; if (type == V4L2_CTRL_TYPE_MENU) ctrl->qmenu = qmenu; @@ -1892,8 +1905,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->type_ops, cfg->id, name, type, min, max, - is_menu ? cfg->menu_skip_mask : step, - def, cfg->elem_size, + is_menu ? cfg->menu_skip_mask : step, def, + cfg->dims, cfg->elem_size, flags, qmenu, qmenu_int, priv); if (ctrl) ctrl->is_private = cfg->is_private; @@ -1918,7 +1931,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - min, max, step, def, 0, + min, max, step, def, NULL, 0, flags, NULL, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -1951,7 +1964,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, mask, def, 0, + 0, max, mask, def, NULL, 0, flags, qmenu, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -1983,8 +1996,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, mask, def, - 0, flags, qmenu, NULL, NULL); + 0, max, mask, def, NULL, 0, + flags, qmenu, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items); @@ -2008,7 +2021,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, 0, def, 0, + 0, max, 0, def, NULL, 0, flags, NULL, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); @@ -2354,7 +2367,9 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr if (ctrl->is_ptr) qc->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD; qc->elem_size = ctrl->elem_size; - qc->elems = 1; + qc->elems = ctrl->elems; + qc->nr_of_dims = ctrl->nr_of_dims; + memcpy(qc->dims, ctrl->dims, qc->nr_of_dims * sizeof(qc->dims[0])); qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index eb69c52..d30da09 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -129,7 +129,10 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @maximum: The control's maximum value. * @default_value: The control's default value. * @step: The control's step value for non-menu controls. + * @elems: The number of elements in the N-dimensional array. * @elem_size: The size in bytes of the control. + * @dims: The size of each dimension. + * @nr_of_dims:The number of dimensions in @dims. * @menu_skip_mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for @@ -176,7 +179,10 @@ struct v4l2_ctrl { const char *name; enum v4l2_ctrl_type type; s64 minimum, maximum, default_value; + u32 elems; u32 elem_size; + u32 dims[V4L2_CTRL_MAX_DIMS]; + u32 nr_of_dims; union { u64 step; u64 menu_skip_mask; @@ -255,6 +261,7 @@ struct v4l2_ctrl_handler { * @max: The control's maximum value. * @step: The control's step value for non-menu controls. * @def: The control's default value. + * @dims: The size of each dimension. * @elem_size: The size in bytes of the control. * @flags: The control's flags. * @menu_skip_mask: The control's skip mask for menu controls. This makes it @@ -280,6 +287,7 @@ struct v4l2_ctrl_config { s64 max; u64 step; s64 def; + u32 dims[V4L2_CTRL_MAX_DIMS]; u32 elem_size; u32 flags; u64 menu_skip_mask; -- cgit v0.10.2 From 998e7659150760c0f4871ee20de2ef2276e3f80a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:55:00 -0300 Subject: [media] v4l2-ctrls: prepare for array support Add core support for N-dimensional arrays. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e6c98a7..6ed2d56 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1161,7 +1161,7 @@ static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes) v4l2_event_queue_fh(sev->fh, &ev); } -static bool std_equal(const struct v4l2_ctrl *ctrl, +static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2) { @@ -1180,7 +1180,7 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, } } -static void std_init(const struct v4l2_ctrl *ctrl, +static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) { switch (ctrl->type) { @@ -1207,6 +1207,14 @@ static void std_log(const struct v4l2_ctrl *ctrl) { union v4l2_ctrl_ptr ptr = ctrl->p_cur; + if (ctrl->is_array) { + unsigned i; + + for (i = 0; i < ctrl->nr_of_dims; i++) + pr_cont("[%u]", ctrl->dims[i]); + pr_cont(" "); + } + switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: pr_cont("%d", *ptr.p_s32); @@ -1249,7 +1257,7 @@ static void std_log(const struct v4l2_ctrl *ctrl) }) /* Validate a new control */ -static int std_validate(const struct v4l2_ctrl *ctrl, +static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) { size_t len; @@ -1473,8 +1481,8 @@ static int cluster_changed(struct v4l2_ctrl *master) if (ctrl == NULL) continue; - ctrl->has_changed = !ctrl->type_ops->equal(ctrl, - ctrl->p_cur, ctrl->p_new); + ctrl->has_changed = !ctrl->type_ops->equal(ctrl, 0, + ctrl->p_cur, ctrl->p_new); changed |= ctrl->has_changed; } return changed; @@ -1531,15 +1539,15 @@ static int validate_new(const struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: ptr.p_s32 = &c->value; - return ctrl->type_ops->validate(ctrl, ptr); + return ctrl->type_ops->validate(ctrl, 0, ptr); case V4L2_CTRL_TYPE_INTEGER64: ptr.p_s64 = &c->value64; - return ctrl->type_ops->validate(ctrl, ptr); + return ctrl->type_ops->validate(ctrl, 0, ptr); default: ptr.p = c->ptr; - return ctrl->type_ops->validate(ctrl, ptr); + return ctrl->type_ops->validate(ctrl, 0, ptr); } } @@ -1767,6 +1775,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, unsigned sz_extra; unsigned nr_of_dims = 0; unsigned elems = 1; + bool is_array; + unsigned tot_ctrl_size; void *data; int err; @@ -1779,6 +1789,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, if (nr_of_dims == V4L2_CTRL_MAX_DIMS) break; } + is_array = nr_of_dims > 0; if (type == V4L2_CTRL_TYPE_INTEGER64) elem_size = sizeof(s64); @@ -1786,10 +1797,11 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, elem_size = max + 1; else if (type < V4L2_CTRL_COMPOUND_TYPES) elem_size = sizeof(s32); + tot_ctrl_size = elem_size * elems; /* Sanity checks */ - if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE || - elem_size == 0 || + if (id == 0 || name == NULL || !elem_size || + id >= V4L2_CID_PRIVATE_BASE || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) { handler_set_err(hdl, -ERANGE); @@ -1804,6 +1816,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -ERANGE); return NULL; } + if (is_array && + (type == V4L2_CTRL_TYPE_BUTTON || + type == V4L2_CTRL_TYPE_CTRL_CLASS)) { + handler_set_err(hdl, -EINVAL); + return NULL; + } sz_extra = 0; if (type == V4L2_CTRL_TYPE_BUTTON) @@ -1812,8 +1830,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, flags |= V4L2_CTRL_FLAG_READ_ONLY; else if (type == V4L2_CTRL_TYPE_INTEGER64 || type == V4L2_CTRL_TYPE_STRING || - type >= V4L2_CTRL_COMPOUND_TYPES) - sz_extra += 2 * elem_size; + type >= V4L2_CTRL_COMPOUND_TYPES || + is_array) + sz_extra += 2 * tot_ctrl_size; ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); if (ctrl == NULL) { @@ -1834,9 +1853,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->maximum = max; ctrl->step = step; ctrl->default_value = def; - ctrl->is_string = type == V4L2_CTRL_TYPE_STRING; - ctrl->is_ptr = type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; + ctrl->is_string = !is_array && type == V4L2_CTRL_TYPE_STRING; + ctrl->is_ptr = is_array || type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; + ctrl->is_array = is_array; ctrl->elems = elems; ctrl->nr_of_dims = nr_of_dims; if (nr_of_dims) @@ -1852,13 +1872,13 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, if (!ctrl->is_int) { ctrl->p_new.p = data; - ctrl->p_cur.p = data + elem_size; + ctrl->p_cur.p = data + tot_ctrl_size; } else { ctrl->p_new.p = &ctrl->val; ctrl->p_cur.p = &ctrl->cur.val; } - ctrl->type_ops->init(ctrl, ctrl->p_cur); - ctrl->type_ops->init(ctrl, ctrl->p_new); + ctrl->type_ops->init(ctrl, 0, ctrl->p_cur); + ctrl->type_ops->init(ctrl, 0, ctrl->p_new); if (handler_new_ref(hdl, ctrl)) { kfree(ctrl); @@ -2764,7 +2784,7 @@ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64); + WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); c.value = 0; get_ctrl(ctrl, &c); return c.value; @@ -3074,7 +3094,7 @@ int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64); + WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); c.value64 = val; return set_ctrl_lock(NULL, ctrl, &c); } diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index d30da09..7915b1125 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -74,13 +74,13 @@ struct v4l2_ctrl_ops { * @validate: validate the value. Return 0 on success and a negative value otherwise. */ struct v4l2_ctrl_type_ops { - bool (*equal)(const struct v4l2_ctrl *ctrl, + bool (*equal)(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2); - void (*init)(const struct v4l2_ctrl *ctrl, + void (*init)(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr); void (*log)(const struct v4l2_ctrl *ctrl); - int (*validate)(const struct v4l2_ctrl *ctrl, + int (*validate)(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr); }; @@ -111,6 +111,7 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @is_ptr: If set, then this control is an array and/or has type >= V4L2_CTRL_COMPOUND_TYPES * and/or has type V4L2_CTRL_TYPE_STRING. In other words, struct * v4l2_ext_control uses field p to point to the data. + * @is_array: If set, then this control contains an N-dimensional array. * @has_volatiles: If set, then one or more members of the cluster are volatile. * Drivers should never touch this flag. * @call_notify: If set, then call the handler's notify function whenever the @@ -169,6 +170,7 @@ struct v4l2_ctrl { unsigned int is_int:1; unsigned int is_string:1; unsigned int is_ptr:1; + unsigned int is_array:1; unsigned int has_volatiles:1; unsigned int call_notify:1; unsigned int manual_mode_value:8; -- cgit v0.10.2 From 265c7f8a72e972a707246a2bb2c492524aa95ea4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 5 Jan 2014 11:06:05 -0300 Subject: [media] v4l2-ctrls: type_ops can handle array elements Extend the control type operations to handle N-dimensional array elements. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 6ed2d56..f6ac927 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1169,14 +1169,16 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_BUTTON: return false; case V4L2_CTRL_TYPE_STRING: + idx *= ctrl->elem_size; /* strings are always 0-terminated */ - return !strcmp(ptr1.p_char, ptr2.p_char); + return !strcmp(ptr1.p_char + idx, ptr2.p_char + idx); case V4L2_CTRL_TYPE_INTEGER64: - return *ptr1.p_s64 == *ptr2.p_s64; + return ptr1.p_s64[idx] == ptr2.p_s64[idx]; default: - if (ctrl->is_ptr) - return !memcmp(ptr1.p, ptr2.p, ctrl->elem_size); - return *ptr1.p_s32 == *ptr2.p_s32; + if (ctrl->is_int) + return ptr1.p_s32[idx] == ptr2.p_s32[idx]; + idx *= ctrl->elem_size; + return !memcmp(ptr1.p + idx, ptr2.p + idx, ctrl->elem_size); } } @@ -1185,18 +1187,19 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, { switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: - memset(ptr.p_char, ' ', ctrl->minimum); - ptr.p_char[ctrl->minimum] = '\0'; + idx *= ctrl->elem_size; + memset(ptr.p_char + idx, ' ', ctrl->minimum); + ptr.p_char[idx + ctrl->minimum] = '\0'; break; case V4L2_CTRL_TYPE_INTEGER64: - *ptr.p_s64 = ctrl->default_value; + ptr.p_s64[idx] = ctrl->default_value; break; case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_BOOLEAN: - *ptr.p_s32 = ctrl->default_value; + ptr.p_s32[idx] = ctrl->default_value; break; default: break; @@ -1264,36 +1267,37 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - return ROUND_TO_RANGE(*ptr.p_s32, u32, ctrl); + return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl); case V4L2_CTRL_TYPE_INTEGER64: - return ROUND_TO_RANGE(*ptr.p_s64, u64, ctrl); + return ROUND_TO_RANGE(ptr.p_s64[idx], u64, ctrl); case V4L2_CTRL_TYPE_BOOLEAN: - *ptr.p_s32 = !!*ptr.p_s32; + ptr.p_s32[idx] = !!ptr.p_s32[idx]; return 0; case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: - if (*ptr.p_s32 < ctrl->minimum || *ptr.p_s32 > ctrl->maximum) + if (ptr.p_s32[idx] < ctrl->minimum || ptr.p_s32[idx] > ctrl->maximum) return -ERANGE; - if (ctrl->menu_skip_mask & (1 << *ptr.p_s32)) + if (ctrl->menu_skip_mask & (1 << ptr.p_s32[idx])) return -EINVAL; if (ctrl->type == V4L2_CTRL_TYPE_MENU && - ctrl->qmenu[*ptr.p_s32][0] == '\0') + ctrl->qmenu[ptr.p_s32[idx]][0] == '\0') return -EINVAL; return 0; case V4L2_CTRL_TYPE_BITMASK: - *ptr.p_s32 &= ctrl->maximum; + ptr.p_s32[idx] &= ctrl->maximum; return 0; case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: - *ptr.p_s32 = 0; + ptr.p_s32[idx] = 0; return 0; case V4L2_CTRL_TYPE_STRING: - len = strlen(ptr.p_char); + idx *= ctrl->elem_size; + len = strlen(ptr.p_char + idx); if (len < ctrl->minimum) return -ERANGE; if ((len - ctrl->minimum) % ctrl->step) -- cgit v0.10.2 From 302ab7ce2daba8cdd82a6809adb42d117a683f06 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:06:50 -0300 Subject: [media] v4l2-ctrls: add array support Finish the userspace-facing array support. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index f6ac927..b3ab8a9 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1202,6 +1202,8 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, ptr.p_s32[idx] = ctrl->default_value; break; default: + idx *= ctrl->elem_size; + memset(ptr.p + idx, 0, ctrl->elem_size); break; } } @@ -1324,7 +1326,7 @@ static int ptr_to_user(struct v4l2_ext_control *c, u32 len; if (ctrl->is_ptr && !ctrl->is_string) - return copy_to_user(c->ptr, ptr.p, ctrl->elem_size); + return copy_to_user(c->ptr, ptr.p, c->size); switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: @@ -1368,8 +1370,16 @@ static int user_to_ptr(struct v4l2_ext_control *c, u32 size; ctrl->is_new = 1; - if (ctrl->is_ptr && !ctrl->is_string) - return copy_from_user(ptr.p, c->ptr, ctrl->elem_size); + if (ctrl->is_ptr && !ctrl->is_string) { + unsigned idx; + + ret = copy_from_user(ptr.p, c->ptr, c->size); + if (ret || !ctrl->is_array) + return ret; + for (idx = c->size / ctrl->elem_size; idx < ctrl->elems; idx++) + ctrl->type_ops->init(ctrl, idx, ptr); + return 0; + } switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER64: @@ -1412,21 +1422,7 @@ static void ptr_to_ptr(struct v4l2_ctrl *ctrl, { if (ctrl == NULL) return; - switch (ctrl->type) { - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - strcpy(to.p_char, from.p_char); - break; - case V4L2_CTRL_TYPE_INTEGER64: - *to.p_s64 = *from.p_s64; - break; - default: - if (ctrl->is_ptr) - memcpy(to.p, from.p, ctrl->elem_size); - else - *to.p_s32 = *from.p_s32; - break; - } + memcpy(to.p, from.p, ctrl->elems * ctrl->elem_size); } /* Copy the new value to the current value. */ @@ -1478,15 +1474,19 @@ static void cur_to_new(struct v4l2_ctrl *ctrl) static int cluster_changed(struct v4l2_ctrl *master) { bool changed = false; + unsigned idx; int i; for (i = 0; i < master->ncontrols; i++) { struct v4l2_ctrl *ctrl = master->cluster[i]; + bool ctrl_changed = false; if (ctrl == NULL) continue; - ctrl->has_changed = !ctrl->type_ops->equal(ctrl, 0, + for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++) + ctrl_changed = !ctrl->type_ops->equal(ctrl, idx, ctrl->p_cur, ctrl->p_new); + ctrl->has_changed = ctrl_changed; changed |= ctrl->has_changed; } return changed; @@ -1533,26 +1533,32 @@ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) { union v4l2_ctrl_ptr ptr; - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER: - case V4L2_CTRL_TYPE_INTEGER_MENU: - case V4L2_CTRL_TYPE_MENU: - case V4L2_CTRL_TYPE_BITMASK: - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_BUTTON: - case V4L2_CTRL_TYPE_CTRL_CLASS: - ptr.p_s32 = &c->value; - return ctrl->type_ops->validate(ctrl, 0, ptr); - - case V4L2_CTRL_TYPE_INTEGER64: - ptr.p_s64 = &c->value64; - return ctrl->type_ops->validate(ctrl, 0, ptr); - - default: - ptr.p = c->ptr; - return ctrl->type_ops->validate(ctrl, 0, ptr); + unsigned idx; + int err = 0; + + if (!ctrl->is_ptr) { + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_BITMASK: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_BUTTON: + case V4L2_CTRL_TYPE_CTRL_CLASS: + ptr.p_s32 = &c->value; + return ctrl->type_ops->validate(ctrl, 0, ptr); + + case V4L2_CTRL_TYPE_INTEGER64: + ptr.p_s64 = &c->value64; + return ctrl->type_ops->validate(ctrl, 0, ptr); + default: + break; + } } + ptr.p = c->ptr; + for (idx = 0; !err && idx < c->size / ctrl->elem_size; idx++) + err = ctrl->type_ops->validate(ctrl, idx, ptr); + return err; } static inline u32 node2id(struct list_head *node) @@ -1781,6 +1787,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, unsigned elems = 1; bool is_array; unsigned tot_ctrl_size; + unsigned idx; void *data; int err; @@ -1881,8 +1888,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->p_new.p = &ctrl->val; ctrl->p_cur.p = &ctrl->cur.val; } - ctrl->type_ops->init(ctrl, 0, ctrl->p_cur); - ctrl->type_ops->init(ctrl, 0, ctrl->p_new); + for (idx = 0; idx < elems; idx++) { + ctrl->type_ops->init(ctrl, idx, ctrl->p_cur); + ctrl->type_ops->init(ctrl, idx, ctrl->p_new); + } if (handler_new_ref(hdl, ctrl)) { kfree(ctrl); @@ -2578,12 +2587,17 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, have_clusters = true; if (ctrl->cluster[0] != ctrl) ref = find_ref_lock(hdl, ctrl->cluster[0]->id); - if (ctrl->is_ptr && !ctrl->is_string && c->size < ctrl->elem_size) { - if (get) { - c->size = ctrl->elem_size; - return -ENOSPC; + if (ctrl->is_ptr && !ctrl->is_string) { + unsigned tot_size = ctrl->elems * ctrl->elem_size; + + if (c->size < tot_size) { + if (get) { + c->size = tot_size; + return -ENOSPC; + } + return -EFAULT; } - return -EFAULT; + c->size = tot_size; } /* Store the ref to the master control of the cluster */ h->mref = ref; @@ -3123,7 +3137,7 @@ EXPORT_SYMBOL(v4l2_ctrl_notify); int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, s64 min, s64 max, u64 step, s64 def) { - int ret = check_range(ctrl->type, min, max, step, def); + int ret; struct v4l2_ext_control c; switch (ctrl->type) { @@ -3133,6 +3147,9 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_BITMASK: + if (ctrl->is_array) + return -EINVAL; + ret = check_range(ctrl->type, min, max, step, def); if (ret) return ret; break; -- cgit v0.10.2 From c336f75e1e822658122b96d874da01f5f4df994d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 18 Jan 2014 06:06:01 -0300 Subject: [media] v4l2-ctrls: return elem_size instead of strlen When getting a string and the size given by the application is too short return the max length the string can have (elem_size) instead of the string length + 1. That makes more sense. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index b3ab8a9..e6e33b3 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1332,7 +1332,7 @@ static int ptr_to_user(struct v4l2_ext_control *c, case V4L2_CTRL_TYPE_STRING: len = strlen(ptr.p_char); if (c->size < len + 1) { - c->size = len + 1; + c->size = ctrl->elem_size; return -ENOSPC; } return copy_to_user(c->string, ptr.p_char, len + 1) ? -- cgit v0.10.2 From 40265bbe42933920684136ff759e6340eaf72774 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:07:38 -0300 Subject: [media] v4l2-ctrl: fix error return of copy_to/from_user copy_to/from_user returns the number of bytes not copied, it does not return a 'normal' linux error code. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e6e33b3..1086ae3 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1326,7 +1326,8 @@ static int ptr_to_user(struct v4l2_ext_control *c, u32 len; if (ctrl->is_ptr && !ctrl->is_string) - return copy_to_user(c->ptr, ptr.p, c->size); + return copy_to_user(c->ptr, ptr.p, c->size) ? + -EFAULT : 0; switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: @@ -1336,7 +1337,7 @@ static int ptr_to_user(struct v4l2_ext_control *c, return -ENOSPC; } return copy_to_user(c->string, ptr.p_char, len + 1) ? - -EFAULT : 0; + -EFAULT : 0; case V4L2_CTRL_TYPE_INTEGER64: c->value64 = *ptr.p_s64; break; @@ -1373,7 +1374,7 @@ static int user_to_ptr(struct v4l2_ext_control *c, if (ctrl->is_ptr && !ctrl->is_string) { unsigned idx; - ret = copy_from_user(ptr.p, c->ptr, c->size); + ret = copy_from_user(ptr.p, c->ptr, c->size) ? -EFAULT : 0; if (ret || !ctrl->is_array) return ret; for (idx = c->size / ctrl->elem_size; idx < ctrl->elems; idx++) @@ -1391,7 +1392,7 @@ static int user_to_ptr(struct v4l2_ext_control *c, return -ERANGE; if (size > ctrl->maximum + 1) size = ctrl->maximum + 1; - ret = copy_from_user(ptr.p_char, c->string, size); + ret = copy_from_user(ptr.p_char, c->string, size) ? -EFAULT : 0; if (!ret) { char last = ptr.p_char[size - 1]; @@ -1401,7 +1402,7 @@ static int user_to_ptr(struct v4l2_ext_control *c, if (strlen(ptr.p_char) == ctrl->maximum && last) return -ERANGE; } - return ret ? -EFAULT : 0; + return ret; default: *ptr.p_s32 = c->value; break; -- cgit v0.10.2 From 715bfb3e1f0ca0f6c6bd7805d90f31325fb61b72 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:21:04 -0300 Subject: [media] DocBook media: document VIDIOC_QUERY_EXT_CTRL Document the new VIDIOC_QUERY_EXT_CTRL ioctl. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml index e6645b9..04589e5 100644 --- a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml +++ b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml @@ -1,11 +1,12 @@ - ioctl VIDIOC_QUERYCTRL, VIDIOC_QUERYMENU + ioctl VIDIOC_QUERYCTRL, VIDIOC_QUERY_EXT_CTRL, VIDIOC_QUERYMENU &manvol; VIDIOC_QUERYCTRL + VIDIOC_QUERY_EXT_CTRL VIDIOC_QUERYMENU Enumerate controls and menu control items @@ -24,6 +25,14 @@ int ioctl int fd int request + struct v4l2_query_ext_ctrl *argp + + + + + int ioctl + int fd + int request struct v4l2_querymenu *argp @@ -42,7 +51,7 @@ request - VIDIOC_QUERYCTRL, VIDIOC_QUERYMENU + VIDIOC_QUERYCTRL, VIDIOC_QUERY_EXT_CTRL, VIDIOC_QUERYMENU @@ -89,9 +98,23 @@ prematurely end the enumeration). When the application ORs id with V4L2_CTRL_FLAG_NEXT_CTRL the driver returns the -next supported control, or EINVAL if there is -none. Drivers which do not support this flag yet always return -EINVAL. +next supported non-compound control, or EINVAL +if there is none. In addition, the V4L2_CTRL_FLAG_NEXT_COMPOUND +flag can be specified to enumerate all compound controls (i.e. controls +with type ≥ V4L2_CTRL_COMPOUND_TYPES). Specify both +V4L2_CTRL_FLAG_NEXT_CTRL and +V4L2_CTRL_FLAG_NEXT_COMPOUND in order to enumerate +all controls, compound or not. Drivers which do not support these flags yet +always return EINVAL. + + The VIDIOC_QUERY_EXT_CTRL ioctl was +introduced in order to better support controls that can use compound +types, and to expose additional control information that cannot be +returned in &v4l2-queryctrl; since that structure is full. + + VIDIOC_QUERY_EXT_CTRL is used in the +same way as VIDIOC_QUERYCTRL, except that the +reserved array must be zeroed as well. Additional information is required for menu controls: the names of the menu items. To query them applications set the @@ -142,38 +165,23 @@ string. This information is intended for the user. __s32 minimum Minimum value, inclusive. This field gives a lower -bound for V4L2_CTRL_TYPE_INTEGER controls and the -lowest valid index for V4L2_CTRL_TYPE_MENU controls. -For V4L2_CTRL_TYPE_STRING controls the minimum value -gives the minimum length of the string. This length does not include the terminating -zero. It may not be valid for any other type of control, including -V4L2_CTRL_TYPE_INTEGER64 controls. Note that this is a -signed value. +bound for the control. See &v4l2-ctrl-type; how the minimum value is to +be used for each possible control type. Note that this a signed 32-bit value. __s32 maximum Maximum value, inclusive. This field gives an upper -bound for V4L2_CTRL_TYPE_INTEGER controls and the -highest valid index for V4L2_CTRL_TYPE_MENU -controls. For V4L2_CTRL_TYPE_BITMASK controls it is the -set of usable bits. -For V4L2_CTRL_TYPE_STRING controls the maximum value -gives the maximum length of the string. This length does not include the terminating -zero. It may not be valid for any other type of control, including -V4L2_CTRL_TYPE_INTEGER64 controls. Note that this is a -signed value. +bound for the control. See &v4l2-ctrl-type; how the maximum value is to +be used for each possible control type. Note that this a signed 32-bit value. __s32 step - This field gives a step size for -V4L2_CTRL_TYPE_INTEGER controls. For -V4L2_CTRL_TYPE_STRING controls this field refers to -the string length that has to be a multiple of this step size. -It may not be valid for any other type of control, including -V4L2_CTRL_TYPE_INTEGER64 -controls.Generally drivers should not scale hardware + This field gives a step size for the control. +See &v4l2-ctrl-type; how the step value is to be used for each possible +control type. Note that this an unsigned 32-bit value. +Generally drivers should not scale hardware control values. It may be necessary for example when the name or id imply a particular unit and the hardware actually accepts only multiples of @@ -192,10 +200,11 @@ be always positive. default_value The default value of a V4L2_CTRL_TYPE_INTEGER, -_BOOLEAN or _MENU control. -Not valid for other types of controls. Drivers reset controls only -when the driver is loaded, not later, in particular not when the -func-open; is called. +_BOOLEAN, _BITMASK, +_MENU or _INTEGER_MENU control. +Not valid for other types of controls. +Note that drivers reset controls to their default value only when the +driver is first loaded, never afterwards. __u32 @@ -213,6 +222,125 @@ the array to zero. + + struct <structname>v4l2_query_ext_ctrl</structname> + + &cs-str; + + + __u32 + id + Identifies the control, set by the application. See + for predefined IDs. When the ID is ORed +with V4L2_CTRL_FLAG_NEXT_CTRL the driver clears the +flag and returns the first non-compound control with a higher ID. When the +ID is ORed with V4L2_CTRL_FLAG_NEXT_COMPOUND the driver +clears the flag and returns the first compound control with a higher ID. +Set both to get the first control (compound or not) with a higher ID. + + + __u32 + type + Type of control, see . + + + char + name[32] + Name of the control, a NUL-terminated ASCII +string. This information is intended for the user. + + + __s64 + minimum + Minimum value, inclusive. This field gives a lower +bound for the control. See &v4l2-ctrl-type; how the minimum value is to +be used for each possible control type. Note that this a signed 64-bit value. + + + __s64 + maximum + Maximum value, inclusive. This field gives an upper +bound for the control. See &v4l2-ctrl-type; how the maximum value is to +be used for each possible control type. Note that this a signed 64-bit value. + + + __u64 + step + This field gives a step size for the control. +See &v4l2-ctrl-type; how the step value is to be used for each possible +control type. Note that this an unsigned 64-bit value. +Generally drivers should not scale hardware +control values. It may be necessary for example when the +name or id imply +a particular unit and the hardware actually accepts only multiples of +said unit. If so, drivers must take care values are properly rounded +when scaling, such that errors will not accumulate on repeated +read-write cycles.This field gives the smallest change of +an integer control actually affecting hardware. Often the information +is needed when the user can change controls by keyboard or GUI +buttons, rather than a slider. When for example a hardware register +accepts values 0-511 and the driver reports 0-65535, step should be +128. + + + __s64 + default_value + The default value of a +V4L2_CTRL_TYPE_INTEGER, _INTEGER64, +_BOOLEAN, _BITMASK, +_MENU or _INTEGER_MENU control. +Not valid for other types of controls. +Note that drivers reset controls to their default value only when the +driver is first loaded, never afterwards. + + + + __u32 + flags + Control flags, see . + + + __u32 + elem_size + The size in bytes of a single element of the array. +Given a char pointer p to a 3-dimensional array you can find the +position of cell (z, y, x) as follows: +p + ((z * dims[1] + y) * dims[0] + x) * elem_size. elem_size +is always valid, also when the control isn't an array. For string controls +elem_size is equal to maximum + 1. + + + + __u32 + elems + The number of elements in the N-dimensional array. If this control +is not an array, then elems is 1. The elems +field can never be 0. + + + __u32 + nr_of_dims + The number of dimension in the N-dimensional array. If this control +is not an array, then this field is 0. + + + __u32 + dims[V4L2_CTRL_MAX_DIMS] + The size of each dimension. The first nr_of_dims +elements of this array must be non-zero, all remaining elements must be zero. + + + __u32 + reserved[32] + Reserved for future extensions. Applications and drivers +must set the array to zero. + + + +
+ struct <structname>v4l2_querymenu</structname> @@ -347,11 +475,14 @@ Drivers must ignore the value passed with V4L2_CTRL_TYPE_INTEGER64 - n/a - n/a - n/a + any + any + any A 64-bit integer valued control. Minimum, maximum -and step size cannot be queried. +and step size cannot be queried using VIDIOC_QUERYCTRL. +Only VIDIOC_QUERY_EXT_CTRL can retrieve the 64-bit +min/max/step values, they should be interpreted as n/a when using +VIDIOC_QUERYCTRL. V4L2_CTRL_TYPE_STRING @@ -450,6 +581,14 @@ is in auto-gain mode. In such a case the hardware calculates the gain value base the lighting conditions which can change over time. Note that setting a new value for a volatile control will have no effect. The new value will just be ignored. + + V4L2_CTRL_FLAG_HAS_PAYLOAD + 0x0100 + This control has a pointer type, so its value has to be accessed +using one of the pointer fields of &v4l2-ext-control;. This flag is set for controls +that are an array, string, or have a compound type. In all cases you have to set a +pointer to memory containing the payload of the control. +
-- cgit v0.10.2 From a4c8c262f8ae71a842585d00db9fc10014061ddf Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 4 Apr 2014 08:14:55 -0300 Subject: [media] DocBook media: update VIDIOC_G/S/TRY_EXT_CTRLS Document the support for the new compound type controls. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml index e9f6735..2a157b3 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml @@ -72,23 +72,30 @@ initialize the id, size and reserved2 fields of each &v4l2-ext-control; and call the VIDIOC_G_EXT_CTRLS ioctl. String controls controls -must also set the string field.
+must also set the string field. Controls +of compound types (V4L2_CTRL_FLAG_HAS_PAYLOAD is set) +must set the ptr field. If the size is too small to receive the control result (only relevant for pointer-type controls like strings), then the driver will set size to a valid value and return an &ENOSPC;. You should re-allocate the -string memory to this new size and try again. It is possible that the -same issue occurs again if the string has grown in the meantime. It is +memory to this new size and try again. For the string type it is possible that +the same issue occurs again if the string has grown in the meantime. It is recommended to call &VIDIOC-QUERYCTRL; first and use maximum+1 as the new size value. It is guaranteed that that is sufficient memory. + N-dimensional arrays are set and retrieved row-by-row. You cannot set a partial +array, all elements have to be set or retrieved. The total size is calculated +as elems * elem_size. +These values can be obtained by calling &VIDIOC-QUERY-EXT-CTRL;. + To change the value of a set of controls applications initialize the id, size, reserved2 and -value/string fields of each &v4l2-ext-control; and +value/value64/string/ptr fields of each &v4l2-ext-control; and call the VIDIOC_S_EXT_CTRLS ioctl. The controls will only be set if all control values are valid. @@ -96,7 +103,7 @@ valid. To check if a set of controls have correct values applications initialize the id, size, reserved2 and -value/string fields of each &v4l2-ext-control; and +value/value64/string/ptr fields of each &v4l2-ext-control; and call the VIDIOC_TRY_EXT_CTRLS ioctl. It is up to the driver whether wrong values are automatically adjusted to a valid value or if an error is returned. @@ -158,19 +165,33 @@ applications must set the array to zero. __s32 value - New value or current value. + New value or current value. Valid if this control is not of +type V4L2_CTRL_TYPE_INTEGER64 and +V4L2_CTRL_FLAG_HAS_PAYLOAD is not set. __s64 value64 - New value or current value. + New value or current value. Valid if this control is of +type V4L2_CTRL_TYPE_INTEGER64 and +V4L2_CTRL_FLAG_HAS_PAYLOAD is not set. char * string - A pointer to a string. + A pointer to a string. Valid if this control is of +type V4L2_CTRL_TYPE_STRING. + + + + void * + ptr + A pointer to a compound type which can be an N-dimensional array and/or a +compound type (the control's type is >= V4L2_CTRL_COMPOUND_TYPES). +Valid if V4L2_CTRL_FLAG_HAS_PAYLOAD is set for this control. + -- cgit v0.10.2 From 10fa813de336673c02d62a7b90528b50cdb6f3e7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 27 Jan 2014 06:11:08 -0300 Subject: [media] DocBook media: fix coding style in the control example code Use the proper kernel coding style in these examples. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 47198ee..00cf0a7 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -441,61 +441,60 @@ more menu type controls. &v4l2-queryctrl; queryctrl; &v4l2-querymenu; querymenu; -static void -enumerate_menu (void) +static void enumerate_menu(void) { - printf (" Menu items:\n"); + printf(" Menu items:\n"); - memset (&querymenu, 0, sizeof (querymenu)); + memset(&querymenu, 0, sizeof(querymenu)); querymenu.id = queryctrl.id; for (querymenu.index = queryctrl.minimum; querymenu.index <= queryctrl.maximum; - querymenu.index++) { - if (0 == ioctl (fd, &VIDIOC-QUERYMENU;, &querymenu)) { - printf (" %s\n", querymenu.name); + querymenu.index++) { + if (0 == ioctl(fd, &VIDIOC-QUERYMENU;, &querymenu)) { + printf(" %s\n", querymenu.name); } } } -memset (&queryctrl, 0, sizeof (queryctrl)); +memset(&queryctrl, 0, sizeof(queryctrl)); for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++) { - if (0 == ioctl (fd, &VIDIOC-QUERYCTRL;, &queryctrl)) { + if (0 == ioctl(fd, &VIDIOC-QUERYCTRL;, &queryctrl)) { if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) continue; - printf ("Control %s\n", queryctrl.name); + printf("Control %s\n", queryctrl.name); if (queryctrl.type == V4L2_CTRL_TYPE_MENU) - enumerate_menu (); + enumerate_menu(); } else { if (errno == EINVAL) continue; - perror ("VIDIOC_QUERYCTRL"); - exit (EXIT_FAILURE); + perror("VIDIOC_QUERYCTRL"); + exit(EXIT_FAILURE); } } for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; queryctrl.id++) { - if (0 == ioctl (fd, &VIDIOC-QUERYCTRL;, &queryctrl)) { + if (0 == ioctl(fd, &VIDIOC-QUERYCTRL;, &queryctrl)) { if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) continue; - printf ("Control %s\n", queryctrl.name); + printf("Control %s\n", queryctrl.name); if (queryctrl.type == V4L2_CTRL_TYPE_MENU) - enumerate_menu (); + enumerate_menu(); } else { if (errno == EINVAL) break; - perror ("VIDIOC_QUERYCTRL"); - exit (EXIT_FAILURE); + perror("VIDIOC_QUERYCTRL"); + exit(EXIT_FAILURE); } } @@ -508,53 +507,53 @@ for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; &v4l2-queryctrl; queryctrl; &v4l2-control; control; -memset (&queryctrl, 0, sizeof (queryctrl)); +memset(&queryctrl, 0, sizeof(queryctrl)); queryctrl.id = V4L2_CID_BRIGHTNESS; -if (-1 == ioctl (fd, &VIDIOC-QUERYCTRL;, &queryctrl)) { +if (-1 == ioctl(fd, &VIDIOC-QUERYCTRL;, &queryctrl)) { if (errno != EINVAL) { - perror ("VIDIOC_QUERYCTRL"); - exit (EXIT_FAILURE); + perror("VIDIOC_QUERYCTRL"); + exit(EXIT_FAILURE); } else { - printf ("V4L2_CID_BRIGHTNESS is not supported\n"); + printf("V4L2_CID_BRIGHTNESS is not supported\n"); } } else if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) { - printf ("V4L2_CID_BRIGHTNESS is not supported\n"); + printf("V4L2_CID_BRIGHTNESS is not supported\n"); } else { - memset (&control, 0, sizeof (control)); + memset(&control, 0, sizeof (control)); control.id = V4L2_CID_BRIGHTNESS; control.value = queryctrl.default_value; - if (-1 == ioctl (fd, &VIDIOC-S-CTRL;, &control)) { - perror ("VIDIOC_S_CTRL"); - exit (EXIT_FAILURE); + if (-1 == ioctl(fd, &VIDIOC-S-CTRL;, &control)) { + perror("VIDIOC_S_CTRL"); + exit(EXIT_FAILURE); } } -memset (&control, 0, sizeof (control)); +memset(&control, 0, sizeof(control)); control.id = V4L2_CID_CONTRAST; -if (0 == ioctl (fd, &VIDIOC-G-CTRL;, &control)) { +if (0 == ioctl(fd, &VIDIOC-G-CTRL;, &control)) { control.value += 1; /* The driver may clamp the value or return ERANGE, ignored here */ - if (-1 == ioctl (fd, &VIDIOC-S-CTRL;, &control) + if (-1 == ioctl(fd, &VIDIOC-S-CTRL;, &control) && errno != ERANGE) { - perror ("VIDIOC_S_CTRL"); - exit (EXIT_FAILURE); + perror("VIDIOC_S_CTRL"); + exit(EXIT_FAILURE); } /* Ignore if V4L2_CID_CONTRAST is unsupported */ } else if (errno != EINVAL) { - perror ("VIDIOC_G_CTRL"); - exit (EXIT_FAILURE); + perror("VIDIOC_G_CTRL"); + exit(EXIT_FAILURE); } control.id = V4L2_CID_AUDIO_MUTE; -control.value = TRUE; /* silence */ +control.value = 1; /* silence */ /* Errors ignored */ -ioctl (fd, VIDIOC_S_CTRL, &control); +ioctl(fd, VIDIOC_S_CTRL, &control); @@ -675,12 +674,12 @@ control class is found: qctrl.id = V4L2_CTRL_CLASS_MPEG | V4L2_CTRL_FLAG_NEXT_CTRL; -while (0 == ioctl (fd, &VIDIOC-QUERYCTRL;, &qctrl)) { - if (V4L2_CTRL_ID2CLASS (qctrl.id) != V4L2_CTRL_CLASS_MPEG) +while (0 == ioctl(fd, &VIDIOC-QUERYCTRL;, &qctrl)) { + if (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_MPEG) break; /* ... */ - qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; - } + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; +} -- cgit v0.10.2 From 48e393a263d3a20b27f4db9a169f4f1874777e6b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 5 May 2014 10:16:49 -0300 Subject: [media] DocBook media: improve control section Improve the control section: - Clarify the handling of private controls - Explain the V4L2_CTRL_FLAG_INACTIVE flag - Remove obsolete text regarding missing control event (we have them today) and the incorrect V4L2_CTRL_FLAG_DISABLED reference. - Add a code example on how to enumerate over user controls. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 00cf0a7..73bae13 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -13,6 +13,19 @@ correctly with any device. All controls are accessed using an ID value. V4L2 defines several IDs for specific purposes. Drivers can also implement their own custom controls using V4L2_CID_PRIVATE_BASE +The use of V4L2_CID_PRIVATE_BASE +is problematic because different drivers may use the same +V4L2_CID_PRIVATE_BASE ID for different controls. +This makes it hard to programatically set such controls since the meaning +of the control with that ID is driver dependent. In order to resolve this +drivers use unique IDs and the V4L2_CID_PRIVATE_BASE +IDs are mapped to those unique IDs by the kernel. Consider these +V4L2_CID_PRIVATE_BASE IDs as aliases to the real +IDs. +Many applications today still use the V4L2_CID_PRIVATE_BASE +IDs instead of using &VIDIOC-QUERYCTRL; with the V4L2_CTRL_FLAG_NEXT_CTRL +flag to enumerate all IDs, so support for V4L2_CID_PRIVATE_BASE +is still around. and higher values. The pre-defined control IDs have the prefix V4L2_CID_, and are listed in . The ID is used when querying the attributes of @@ -31,25 +44,22 @@ the current video input or output, tuner or modulator, or audio input or output. Different in the sense of other bounds, another default and current value, step size or other menu items. A control with a certain custom ID can also change name and -type. - It will be more convenient for applications if drivers -make use of the V4L2_CTRL_FLAG_DISABLED flag, but -that was never required. - Control values are stored globally, they do not +type. + + If a control is not applicable to the current configuration +of the device (for example, it doesn't apply to the current video input) +drivers set the V4L2_CTRL_FLAG_INACTIVE flag. + + Control values are stored globally, they do not change when switching except to stay within the reported bounds. They also do not change ⪚ when the device is opened or closed, when the tuner radio frequency is changed or generally never without -application request. Since V4L2 specifies no event mechanism, panel -applications intended to cooperate with other panel applications (be -they built into a larger application, as a TV viewer) may need to -regularly poll control values to update their user -interface. - Applications could call an ioctl to request events. -After another process called &VIDIOC-S-CTRL; or another ioctl changing -shared properties the &func-select; function would indicate -readability until any ioctl (querying the properties) is -called. - +application request. + + V4L2 specifies an event mechanism to notify applications +when controls change value (see &VIDIOC-SUBSCRIBE-EVENT;, event +V4L2_EVENT_CTRL), panel applications might want to make +use of that in order to always reflect the correct control value. All controls use machine endianness. @@ -434,8 +444,8 @@ Drivers must implement VIDIOC_QUERYCTRL, controls, VIDIOC_QUERYMENU when it has one or more menu type controls. - - Enumerating all controls + + Enumerating all user controls &v4l2-queryctrl; queryctrl; @@ -501,6 +511,32 @@ for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; + Enumerating all user controls (alternative) + +memset(&queryctrl, 0, sizeof(queryctrl)); + +queryctrl.id = V4L2_CTRL_CLASS_USER | V4L2_CTRL_FLAG_NEXT_CTRL; +while (0 == ioctl(fd, &VIDIOC-QUERYCTRL;, &queryctrl)) { + if (V4L2_CTRL_ID2CLASS(queryctrl.id) != V4L2_CTRL_CLASS_USER) + break; + if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) + continue; + + printf("Control %s\n", queryctrl.name); + + if (queryctrl.type == V4L2_CTRL_TYPE_MENU) + enumerate_menu(); + + queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; +} +if (errno != EINVAL) { + perror("VIDIOC_QUERYCTRL"); + exit(EXIT_FAILURE); +} + + + + Changing controls @@ -699,7 +735,7 @@ ID based on a control ID. VIDIOC_QUERYCTRL will fail when used in combination with V4L2_CTRL_FLAG_NEXT_CTRL. In that case the old method of enumerating control should be used (see -1.8). But if it is supported, then it is guaranteed to enumerate over +). But if it is supported, then it is guaranteed to enumerate over all controls, including driver-private controls. -- cgit v0.10.2 From 680a513968b691d4ae013b7d6e2df0da3632b51f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 5 May 2014 10:16:49 -0300 Subject: [media] DocBook media: update control section Document the support for compound types in controls. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 73bae13..e7b8b72 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -660,16 +660,29 @@ supported. &v4l2-control;, except for the fact that it also allows for 64-bit values and pointers to be passed. + Since the &v4l2-ext-control; supports pointers it is now +also possible to have controls with compound types such as N-dimensional arrays +and/or structures. You need to specify the V4L2_CTRL_FLAG_NEXT_COMPOUND +when enumerating controls to actually be able to see such compound controls. +In other words, these controls with compound types should only be used +programmatically. + + Since such compound controls need to expose more information +about themselves than is possible with &VIDIOC-QUERYCTRL; the +&VIDIOC-QUERY-EXT-CTRL; ioctl was added. In particular, this ioctl gives +the dimensions of the N-dimensional array if this control consists of more than +one element. + It is important to realize that due to the flexibility of controls it is necessary to check whether the control you want to set actually is supported in the driver and what the valid range of values -is. So use the &VIDIOC-QUERYCTRL; and &VIDIOC-QUERYMENU; ioctls to -check this. Also note that it is possible that some of the menu -indices in a control of type V4L2_CTRL_TYPE_MENU -may not be supported (VIDIOC_QUERYMENU will -return an error). A good example is the list of supported MPEG audio -bitrates. Some drivers only support one or two bitrates, others -support a wider range. +is. So use the &VIDIOC-QUERYCTRL; (or &VIDIOC-QUERY-EXT-CTRL;) and +&VIDIOC-QUERYMENU; ioctls to check this. Also note that it is possible +that some of the menu indices in a control of type +V4L2_CTRL_TYPE_MENU may not be supported +(VIDIOC_QUERYMENU will return an error). A good +example is the list of supported MPEG audio bitrates. Some drivers only +support one or two bitrates, others support a wider range. All controls use machine endianness. -- cgit v0.10.2 From 04d8b04e50c87cd858effe865e82a9012b6eaba0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jan 2014 07:21:31 -0300 Subject: [media] v4l2-controls.txt: update to the new way of accessing controls The way current and new values are accessed has changed. Update the document to bring it up to date with the code. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index c9ee9a7..0f84ce8 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -77,9 +77,9 @@ Basic usage for V4L2 and sub-device drivers Where foo->v4l2_dev is of type struct v4l2_device. - Finally, remove all control functions from your v4l2_ioctl_ops: - vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl, - vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls. + Finally, remove all control functions from your v4l2_ioctl_ops (if any): + vidioc_queryctrl, vidioc_query_ext_ctrl, vidioc_querymenu, vidioc_g_ctrl, + vidioc_s_ctrl, vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls. Those are now no longer needed. 1.3.2) For sub-device drivers do this: @@ -258,8 +258,8 @@ The new control value has already been validated, so all you need to do is to actually update the hardware registers. You're done! And this is sufficient for most of the drivers we have. No need -to do any validation of control values, or implement QUERYCTRL/QUERYMENU. And -G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported. +to do any validation of control values, or implement QUERYCTRL, QUERY_EXT_CTRL +and QUERYMENU. And G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported. ============================================================================== @@ -288,30 +288,45 @@ of v4l2_device. Accessing Control Values ======================== -The v4l2_ctrl struct contains these two unions: +The following union is used inside the control framework to access control +values: - /* The current control value. */ - union { +union v4l2_ctrl_ptr { + s32 *p_s32; + s64 *p_s64; + char *p_char; + void *p; +}; + +The v4l2_ctrl struct contains these fields that can be used to access both +current and new values: + + s32 val; + struct { s32 val; - s64 val64; - char *string; } cur; - /* The new control value. */ - union { - s32 val; - s64 val64; - char *string; - }; -Within the control ops you can freely use these. The val and val64 speak for -themselves. The string pointers point to character buffers of length + union v4l2_ctrl_ptr p_new; + union v4l2_ctrl_ptr p_cur; + +If the control has a simple s32 type type, then: + + &ctrl->val == ctrl->p_new.p_s32 + &ctrl->cur.val == ctrl->p_cur.p_s32 + +For all other types use ctrl->p_cur.p. Basically the val +and cur.val fields can be considered an alias since these are used so often. + +Within the control ops you can freely use these. The val and cur.val speak for +themselves. The p_char pointers point to character buffers of length ctrl->maximum + 1, and are always 0-terminated. -In most cases 'cur' contains the current cached control value. When you create -a new control this value is made identical to the default value. After calling -v4l2_ctrl_handler_setup() this value is passed to the hardware. It is generally -a good idea to call this function. +Unless the control is marked volatile the p_cur field points to the the +current cached control value. When you create a new control this value is made +identical to the default value. After calling v4l2_ctrl_handler_setup() this +value is passed to the hardware. It is generally a good idea to call this +function. Whenever a new value is set that new value is automatically cached. This means that most drivers do not need to implement the g_volatile_ctrl() op. The @@ -363,7 +378,7 @@ You can also take the handler lock yourself: mutex_lock(&state->ctrl_handler.lock); pr_info("String value is '%s'\n", ctrl1->p_cur.p_char); - printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val); + pr_info("Integer value is '%s'\n", ctrl2->cur.val); mutex_unlock(&state->ctrl_handler.lock); -- cgit v0.10.2 From dda4a4d5ea245591b788b70116fb52b0d145fb33 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:30:04 -0300 Subject: [media] v4l2-ctrls/videodev2.h: add u8 and u16 types These are needed by the upcoming patches for the motion detection matrices. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 1086ae3..adf5485 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1174,6 +1174,10 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, return !strcmp(ptr1.p_char + idx, ptr2.p_char + idx); case V4L2_CTRL_TYPE_INTEGER64: return ptr1.p_s64[idx] == ptr2.p_s64[idx]; + case V4L2_CTRL_TYPE_U8: + return ptr1.p_u8[idx] == ptr2.p_u8[idx]; + case V4L2_CTRL_TYPE_U16: + return ptr1.p_u16[idx] == ptr2.p_u16[idx]; default: if (ctrl->is_int) return ptr1.p_s32[idx] == ptr2.p_s32[idx]; @@ -1201,6 +1205,12 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_BOOLEAN: ptr.p_s32[idx] = ctrl->default_value; break; + case V4L2_CTRL_TYPE_U8: + ptr.p_u8[idx] = ctrl->default_value; + break; + case V4L2_CTRL_TYPE_U16: + ptr.p_u16[idx] = ctrl->default_value; + break; default: idx *= ctrl->elem_size; memset(ptr.p + idx, 0, ctrl->elem_size); @@ -1242,6 +1252,12 @@ static void std_log(const struct v4l2_ctrl *ctrl) case V4L2_CTRL_TYPE_STRING: pr_cont("%s", ptr.p_char); break; + case V4L2_CTRL_TYPE_U8: + pr_cont("%u", (unsigned)*ptr.p_u8); + break; + case V4L2_CTRL_TYPE_U16: + pr_cont("%u", (unsigned)*ptr.p_u16); + break; default: pr_cont("unknown type %d", ctrl->type); break; @@ -1272,6 +1288,10 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl); case V4L2_CTRL_TYPE_INTEGER64: return ROUND_TO_RANGE(ptr.p_s64[idx], u64, ctrl); + case V4L2_CTRL_TYPE_U8: + return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl); + case V4L2_CTRL_TYPE_U16: + return ROUND_TO_RANGE(ptr.p_u16[idx], u16, ctrl); case V4L2_CTRL_TYPE_BOOLEAN: ptr.p_s32[idx] = !!ptr.p_s32[idx]; @@ -1502,6 +1522,8 @@ static int check_range(enum v4l2_ctrl_type type, if (step != 1 || max > 1 || min < 0) return -ERANGE; /* fall through */ + case V4L2_CTRL_TYPE_U8: + case V4L2_CTRL_TYPE_U16: case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER64: if (step == 0 || min > max || def < min || def > max) @@ -1803,12 +1825,25 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } is_array = nr_of_dims > 0; - if (type == V4L2_CTRL_TYPE_INTEGER64) + /* Prefill elem_size for all types handled by std_type_ops */ + switch (type) { + case V4L2_CTRL_TYPE_INTEGER64: elem_size = sizeof(s64); - else if (type == V4L2_CTRL_TYPE_STRING) + break; + case V4L2_CTRL_TYPE_STRING: elem_size = max + 1; - else if (type < V4L2_CTRL_COMPOUND_TYPES) - elem_size = sizeof(s32); + break; + case V4L2_CTRL_TYPE_U8: + elem_size = sizeof(u8); + break; + case V4L2_CTRL_TYPE_U16: + elem_size = sizeof(u16); + break; + default: + if (type < V4L2_CTRL_COMPOUND_TYPES) + elem_size = sizeof(s32); + break; + } tot_ctrl_size = elem_size * elems; /* Sanity checks */ @@ -3148,6 +3183,8 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_BITMASK: + case V4L2_CTRL_TYPE_U8: + case V4L2_CTRL_TYPE_U16: if (ctrl->is_array) return -EINVAL; ret = check_range(ctrl->type, min, max, step, def); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 7915b1125..c630345 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -39,12 +39,16 @@ struct poll_table_struct; /** union v4l2_ctrl_ptr - A pointer to a control value. * @p_s32: Pointer to a 32-bit signed value. * @p_s64: Pointer to a 64-bit signed value. + * @p_u8: Pointer to a 8-bit unsigned value. + * @p_u16: Pointer to a 16-bit unsigned value. * @p_char: Pointer to a string. * @p: Pointer to a compound value. */ union v4l2_ctrl_ptr { s32 *p_s32; s64 *p_s64; + u8 *p_u8; + u16 *p_u16; char *p_char; void *p; }; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 0297980..632de96 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1254,6 +1254,8 @@ struct v4l2_ext_control { __s32 value; __s64 value64; char *string; + __u8 *p_u8; + __u16 *p_u16; void *ptr; }; } __attribute__ ((packed)); @@ -1284,6 +1286,8 @@ enum v4l2_ctrl_type { /* Compound types are >= 0x0100 */ V4L2_CTRL_COMPOUND_TYPES = 0x0100, + V4L2_CTRL_TYPE_U8 = 0x0100, + V4L2_CTRL_TYPE_U16 = 0x0101, }; /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ -- cgit v0.10.2 From 5e6ec6b0f74f226f705e7b32b57c409dd6ee7a68 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:34:31 -0300 Subject: [media] DocBook media: document new u8 and u16 control types These types are needed for the upcoming Motion Detection matrix controls, so document them. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml index 2a157b3..c5bdbfc 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml @@ -186,6 +186,20 @@ type V4L2_CTRL_TYPE_STRING. + __u8 * + p_u8 + A pointer to a matrix control of unsigned 8-bit values. +Valid if this control is of type V4L2_CTRL_TYPE_U8. + + + + __u16 * + p_u16 + A pointer to a matrix control of unsigned 16-bit values. +Valid if this control is of type V4L2_CTRL_TYPE_U16. + + + void * ptr A pointer to a compound type which can be an N-dimensional array and/or a diff --git a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml index 04589e5..d9a3f23 100644 --- a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml +++ b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml @@ -289,7 +289,8 @@ accepts values 0-511 and the driver reports 0-65535, step should be The default value of a V4L2_CTRL_TYPE_INTEGER, _INTEGER64, _BOOLEAN, _BITMASK, -_MENU or _INTEGER_MENU control. +_MENU, _INTEGER_MENU, +_U8 or _U16 control. Not valid for other types of controls. Note that drivers reset controls to their default value only when the driver is first loaded, never afterwards. @@ -510,6 +511,26 @@ ioctl returns the name of the control class and this control type. Older drivers which do not support this feature return an &EINVAL;. + + V4L2_CTRL_TYPE_U8 + any + any + any + An unsigned 8-bit valued control ranging from minimum to +maximum inclusive. The step value indicates the increment between +values which are actually different on the hardware. This type is only used +in array controls. + + + V4L2_CTRL_TYPE_U16 + any + any + any + An unsigned 16-bit valued control ranging from minimum to +maximum inclusive. The step value indicates the increment between +values which are actually different on the hardware. This type is only used +in array controls. + -- cgit v0.10.2 From 59253f29c0f3a8b2f6e5108610037150acade9e9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 27 Jan 2014 11:03:34 -0300 Subject: [media] v4l2-ctrls: fix comments Various comments referred to videodev2.h, but the control definitions have been moved to v4l2-controls.h. Also add the same reminder message to each class of controls. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index adf5485..5aaf15e 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -592,7 +592,7 @@ const char *v4l2_ctrl_get_name(u32 id) { switch (id) { /* USER controls */ - /* Keep the order of the 'case's the same as in videodev2.h! */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_USER_CLASS: return "User Controls"; case V4L2_CID_BRIGHTNESS: return "Brightness"; case V4L2_CID_CONTRAST: return "Contrast"; @@ -754,7 +754,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: return "VPX Profile"; /* CAMERA controls */ - /* Keep the order of the 'case's the same as in videodev2.h! */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure"; case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute"; @@ -788,8 +788,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_AUTO_FOCUS_STATUS: return "Auto Focus, Status"; case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range"; - /* FM Radio Modulator control */ - /* Keep the order of the 'case's the same as in videodev2.h! */ + /* FM Radio Modulator controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls"; case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation"; case V4L2_CID_RDS_TX_PI: return "RDS Program ID"; @@ -812,6 +812,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor"; /* Flash controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_FLASH_CLASS: return "Flash Controls"; case V4L2_CID_FLASH_LED_MODE: return "LED Mode"; case V4L2_CID_FLASH_STROBE_SOURCE: return "Strobe Source"; @@ -827,7 +828,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_FLASH_READY: return "Ready to Strobe"; /* JPEG encoder controls */ - /* Keep the order of the 'case's the same as in videodev2.h! */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_JPEG_CLASS: return "JPEG Compression Controls"; case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return "Chroma Subsampling"; case V4L2_CID_JPEG_RESTART_INTERVAL: return "Restart Interval"; @@ -835,18 +836,21 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers"; /* Image source controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_IMAGE_SOURCE_CLASS: return "Image Source Controls"; case V4L2_CID_VBLANK: return "Vertical Blanking"; case V4L2_CID_HBLANK: return "Horizontal Blanking"; case V4L2_CID_ANALOGUE_GAIN: return "Analogue Gain"; /* Image processing controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_IMAGE_PROC_CLASS: return "Image Processing Controls"; case V4L2_CID_LINK_FREQ: return "Link Frequency"; case V4L2_CID_PIXEL_RATE: return "Pixel Rate"; case V4L2_CID_TEST_PATTERN: return "Test Pattern"; /* DV controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_DV_CLASS: return "Digital Video Controls"; case V4L2_CID_DV_TX_HOTPLUG: return "Hotplug Present"; case V4L2_CID_DV_TX_RXSENSE: return "RxSense Present"; -- cgit v0.10.2 From a77b4fc0bc328b0989f83220aba1c268e087107f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 28 Mar 2014 13:00:08 -0300 Subject: [media] v4l2-ctrls/v4l2-controls.h: add MD controls Add the 'Detect' control class and the new motion detection controls. Those controls will be used by the solo6x10 and go7007 drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5aaf15e..5c3b8de 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -462,6 +462,13 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "RGB full range (0-255)", NULL, }; + static const char * const detect_md_mode[] = { + "Disabled", + "Global", + "Threshold Grid", + "Region Grid", + NULL, + }; switch (id) { @@ -553,6 +560,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) case V4L2_CID_DV_TX_RGB_RANGE: case V4L2_CID_DV_RX_RGB_RANGE: return dv_rgb_range; + case V4L2_CID_DETECT_MD_MODE: + return detect_md_mode; default: return NULL; @@ -874,6 +883,15 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: return "Bandwidth, Auto"; case V4L2_CID_RF_TUNER_BANDWIDTH: return "Bandwidth"; case V4L2_CID_RF_TUNER_PLL_LOCK: return "PLL Lock"; + + /* Detection controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ + case V4L2_CID_DETECT_CLASS: return "Detection Controls"; + case V4L2_CID_DETECT_MD_MODE: return "Motion Detection Mode"; + case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: return "MD Global Threshold"; + case V4L2_CID_DETECT_MD_THRESHOLD_GRID: return "MD Threshold Grid"; + case V4L2_CID_DETECT_MD_REGION_GRID: return "MD Region Grid"; + default: return NULL; } @@ -992,6 +1010,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_TEST_PATTERN: case V4L2_CID_TUNE_DEEMPHASIS: case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: + case V4L2_CID_DETECT_MD_MODE: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: @@ -1018,6 +1037,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DV_CLASS: case V4L2_CID_FM_RX_CLASS: case V4L2_CID_RF_TUNER_CLASS: + case V4L2_CID_DETECT_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -1063,6 +1083,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *type = V4L2_CTRL_TYPE_INTEGER64; *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; + case V4L2_CID_DETECT_MD_REGION_GRID: + *type = V4L2_CTRL_TYPE_U8; + break; + case V4L2_CID_DETECT_MD_THRESHOLD_GRID: + *type = V4L2_CTRL_TYPE_U16; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; @@ -1103,6 +1129,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RF_TUNER_MIXER_GAIN: case V4L2_CID_RF_TUNER_IF_GAIN: case V4L2_CID_RF_TUNER_BANDWIDTH: + case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: *flags |= V4L2_CTRL_FLAG_SLIDER; break; case V4L2_CID_PAN_RELATIVE: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 2ac5597..db526d1 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -61,6 +61,7 @@ #define V4L2_CTRL_CLASS_DV 0x00a00000 /* Digital Video controls */ #define V4L2_CTRL_CLASS_FM_RX 0x00a10000 /* FM Receiver controls */ #define V4L2_CTRL_CLASS_RF_TUNER 0x00a20000 /* RF tuner controls */ +#define V4L2_CTRL_CLASS_DETECT 0x00a30000 /* Detection controls */ /* User-class control IDs */ @@ -914,4 +915,20 @@ enum v4l2_deemphasis { #define V4L2_CID_RF_TUNER_IF_GAIN (V4L2_CID_RF_TUNER_CLASS_BASE + 62) #define V4L2_CID_RF_TUNER_PLL_LOCK (V4L2_CID_RF_TUNER_CLASS_BASE + 91) + +/* Detection-class control IDs defined by V4L2 */ +#define V4L2_CID_DETECT_CLASS_BASE (V4L2_CTRL_CLASS_DETECT | 0x900) +#define V4L2_CID_DETECT_CLASS (V4L2_CTRL_CLASS_DETECT | 1) + +#define V4L2_CID_DETECT_MD_MODE (V4L2_CID_DETECT_CLASS_BASE + 1) +enum v4l2_detect_md_mode { + V4L2_DETECT_MD_MODE_DISABLED = 0, + V4L2_DETECT_MD_MODE_GLOBAL = 1, + V4L2_DETECT_MD_MODE_THRESHOLD_GRID = 2, + V4L2_DETECT_MD_MODE_REGION_GRID = 3, +}; +#define V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD (V4L2_CID_DETECT_CLASS_BASE + 2) +#define V4L2_CID_DETECT_MD_THRESHOLD_GRID (V4L2_CID_DETECT_CLASS_BASE + 3) +#define V4L2_CID_DETECT_MD_REGION_GRID (V4L2_CID_DETECT_CLASS_BASE + 4) + #endif -- cgit v0.10.2 From 407d3a87e314f813d1a12bcffb9d12d1eff83379 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 28 Mar 2014 13:01:42 -0300 Subject: [media] DocBook media: document new motion detection controls Document the 'Detect' control class and the new Motion Detection controls. Those controls will be used by the solo6x10 and go7007 drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index e7b8b72..cc0087e 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -5055,6 +5055,102 @@ defines possible values for de-emphasis. Here they are: + + +
+ Detect Control Reference + + The Detect class includes controls for common features of + various motion or object detection capable devices. + + + Detect Control IDs + + + + + + + + + + + ID + Type + Description + + + + + + V4L2_CID_DETECT_CLASS  + class + The Detect class +descriptor. Calling &VIDIOC-QUERYCTRL; for this control will return a +description of this control class. + + + V4L2_CID_DETECT_MD_MODE  + menu + Sets the motion detection mode. + + + + + + V4L2_DETECT_MD_MODE_DISABLED + Disable motion detection. + + + V4L2_DETECT_MD_MODE_GLOBAL + Use a single motion detection threshold. + + + V4L2_DETECT_MD_MODE_THRESHOLD_GRID + The image is divided into a grid, each cell with its own + motion detection threshold. These thresholds are set through the + V4L2_CID_DETECT_MD_THRESHOLD_GRID matrix control. + + + V4L2_DETECT_MD_MODE_REGION_GRID + The image is divided into a grid, each cell with its own + region value that specifies which per-region motion detection thresholds + should be used. Each region has its own thresholds. How these per-region + thresholds are set up is driver-specific. The region values for the grid are set + through the V4L2_CID_DETECT_MD_REGION_GRID matrix + control. + + + + + + V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD  + integer + + Sets the global motion detection threshold to be + used with the V4L2_DETECT_MD_MODE_GLOBAL motion detection mode. + + + V4L2_CID_DETECT_MD_THRESHOLD_GRID  + __u16 matrix + + Sets the motion detection thresholds for each cell in the grid. + To be used with the V4L2_DETECT_MD_MODE_THRESHOLD_GRID + motion detection mode. Matrix element (0, 0) represents the cell at the top-left of the + grid. + + + V4L2_CID_DETECT_MD_REGION_GRID  + __u8 matrix + + Sets the motion detection region value for each cell in the grid. + To be used with the V4L2_DETECT_MD_MODE_REGION_GRID + motion detection mode. Matrix element (0, 0) represents the cell at the top-left of the + grid. + + + +
-- cgit v0.10.2 From 78ea611385f08da6e52ff2ea15f0a26e89acb3ab Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 27 Jan 2014 09:26:23 -0300 Subject: [media] v4l2: add a motion detection event Add a new MOTION_DET event to signal when motion is detected. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 632de96..1477abe 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1799,6 +1799,7 @@ struct v4l2_streamparm { #define V4L2_EVENT_CTRL 3 #define V4L2_EVENT_FRAME_SYNC 4 #define V4L2_EVENT_SOURCE_CHANGE 5 +#define V4L2_EVENT_MOTION_DET 6 #define V4L2_EVENT_PRIVATE_START 0x08000000 /* Payload for V4L2_EVENT_VSYNC */ @@ -1836,6 +1837,21 @@ struct v4l2_event_src_change { __u32 changes; }; +#define V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ (1 << 0) + +/** + * struct v4l2_event_motion_det - motion detection event + * @flags: if V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ is set, then the + * frame_sequence field is valid. + * @frame_sequence: the frame sequence number associated with this event. + * @region_mask: which regions detected motion. + */ +struct v4l2_event_motion_det { + __u32 flags; + __u32 frame_sequence; + __u32 region_mask; +}; + struct v4l2_event { __u32 type; union { @@ -1843,6 +1859,7 @@ struct v4l2_event { struct v4l2_event_ctrl ctrl; struct v4l2_event_frame_sync frame_sync; struct v4l2_event_src_change src_change; + struct v4l2_event_motion_det motion_det; __u8 data[64]; } u; __u32 pending; -- cgit v0.10.2 From 6d823a2e9aa78addf5d8bb37ec1d7dc1708eaffd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 31 Jan 2014 06:22:58 -0300 Subject: [media] DocBook: document new v4l motion detection event Document the new motion detection event. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml index 820f86e..f3767fb 100644 --- a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml +++ b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml @@ -94,6 +94,12 @@ + &v4l2-event-motion-det; + motion_det + Event data for event V4L2_EVENT_MOTION_DET. + + + __u8 data[64] Event data. Defined by the event type. The union @@ -258,6 +264,44 @@ + + struct <structname>v4l2_event_motion_det</structname> + + &cs-str; + + + __u32 + flags + + Currently only one flag is available: if V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ + is set, then the frame_sequence field is valid, + otherwise that field should be ignored. + + + + __u32 + frame_sequence + + The sequence number of the frame being received. Only valid if the + V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ flag was set. + + + + __u32 + region_mask + + The bitmask of the regions that reported motion. There is at least one + region. If this field is 0, then no motion was detected at all. + If there is no V4L2_CID_DETECT_MD_REGION_GRID control + (see ) to assign a different region + to each cell in the motion detection grid, then that all cells + are automatically assigned to the default region 0. + + + + +
+ Changes diff --git a/Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml b/Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml index 17efa87..9f60956 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml @@ -175,6 +175,14 @@ + V4L2_EVENT_MOTION_DET + 5 + + Triggered whenever the motion detection state for one or more of the regions + changes. This event has a &v4l2-event-motion-det; associated with it. + + + V4L2_EVENT_PRIVATE_START 0x08000000 Base event number for driver-private events. -- cgit v0.10.2 From 4063a3c781071e46aaf08e79c99ea822cbc0f089 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:37:30 -0300 Subject: [media] solo6x10: implement the new motion detection controls Replace the custom ioctls to set motion detection thresholds by standard matrix controls. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/solo6x10/solo6x10-disp.c b/drivers/staging/media/solo6x10/solo6x10-disp.c index 145295a..44d98b8 100644 --- a/drivers/staging/media/solo6x10/solo6x10-disp.c +++ b/drivers/staging/media/solo6x10/solo6x10-disp.c @@ -211,7 +211,7 @@ int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) } int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, - const struct solo_motion_thresholds *thresholds) + const u16 *thresholds) { u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2; u16 buf[64]; @@ -221,7 +221,7 @@ int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, memset(buf, 0, sizeof(buf)); for (y = 0; y < SOLO_MOTION_SZ; y++) { for (x = 0; x < SOLO_MOTION_SZ; x++) - buf[x] = cpu_to_le16(thresholds->thresholds[y][x]); + buf[x] = cpu_to_le16(thresholds[y * SOLO_MOTION_SZ + x]); ret |= solo_p2m_dma(solo_dev, 1, buf, SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * sizeof(buf), sizeof(buf), 0, 0); diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index 37f7fd0..f7ec94c 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -1068,31 +1068,6 @@ static int solo_s_parm(struct file *file, void *priv, return solo_g_parm(file, priv, sp); } -static long solo_enc_default(struct file *file, void *fh, - bool valid_prio, unsigned int cmd, void *arg) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct solo_motion_thresholds *thresholds = arg; - - switch (cmd) { - case SOLO_IOC_G_MOTION_THRESHOLDS: - *thresholds = solo_enc->motion_thresholds; - return 0; - - case SOLO_IOC_S_MOTION_THRESHOLDS: - if (!valid_prio) - return -EBUSY; - solo_enc->motion_thresholds = *thresholds; - if (solo_enc->motion_enabled && !solo_enc->motion_global) - return solo_set_motion_block(solo_dev, solo_enc->ch, - &solo_enc->motion_thresholds); - return 0; - default: - return -ENOTTY; - } -} - static int solo_s_ctrl(struct v4l2_ctrl *ctrl) { struct solo_enc_dev *solo_enc = @@ -1111,25 +1086,30 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_GOP_SIZE: solo_enc->gop = ctrl->val; return 0; - case V4L2_CID_MOTION_THRESHOLD: - solo_enc->motion_thresh = ctrl->val; + case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: + solo_enc->motion_thresh = ctrl->val << 8; if (!solo_enc->motion_global || !solo_enc->motion_enabled) return 0; return solo_set_motion_threshold(solo_dev, solo_enc->ch, - ctrl->val); - case V4L2_CID_MOTION_MODE: - solo_enc->motion_global = ctrl->val == 1; - solo_enc->motion_enabled = ctrl->val > 0; + solo_enc->motion_thresh); + case V4L2_CID_DETECT_MD_MODE: + solo_enc->motion_global = ctrl->val == V4L2_DETECT_MD_MODE_GLOBAL; + solo_enc->motion_enabled = ctrl->val > V4L2_DETECT_MD_MODE_DISABLED; if (ctrl->val) { if (solo_enc->motion_global) - solo_set_motion_threshold(solo_dev, - solo_enc->ch, solo_enc->motion_thresh); + solo_set_motion_threshold(solo_dev, solo_enc->ch, + solo_enc->motion_thresh); else solo_set_motion_block(solo_dev, solo_enc->ch, - &solo_enc->motion_thresholds); + solo_enc->md_thresholds->p_cur.p_u16); } solo_motion_toggle(solo_enc, ctrl->val); return 0; + case V4L2_CID_DETECT_MD_THRESHOLD_GRID: + if (solo_enc->motion_enabled && !solo_enc->motion_global) + return solo_set_motion_block(solo_dev, solo_enc->ch, + solo_enc->md_thresholds->p_new.p_u16); + break; case V4L2_CID_OSD_TEXT: strcpy(solo_enc->osd_text, ctrl->p_new.p_char); err = solo_osd_print(solo_enc); @@ -1181,7 +1161,6 @@ static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - .vidioc_default = solo_enc_default, }; static const struct video_device solo_enc_template = { @@ -1197,33 +1176,6 @@ static const struct v4l2_ctrl_ops solo_ctrl_ops = { .s_ctrl = solo_s_ctrl, }; -static const struct v4l2_ctrl_config solo_motion_threshold_ctrl = { - .ops = &solo_ctrl_ops, - .id = V4L2_CID_MOTION_THRESHOLD, - .name = "Motion Detection Threshold", - .type = V4L2_CTRL_TYPE_INTEGER, - .max = 0xffff, - .def = SOLO_DEF_MOT_THRESH, - .step = 1, - .flags = V4L2_CTRL_FLAG_SLIDER, -}; - -static const char * const solo_motion_mode_menu[] = { - "Disabled", - "Global Threshold", - "Regional Threshold", - NULL -}; - -static const struct v4l2_ctrl_config solo_motion_enable_ctrl = { - .ops = &solo_ctrl_ops, - .id = V4L2_CID_MOTION_MODE, - .name = "Motion Detection Mode", - .type = V4L2_CTRL_TYPE_MENU, - .qmenu = solo_motion_mode_menu, - .max = 2, -}; - static const struct v4l2_ctrl_config solo_osd_text_ctrl = { .ops = &solo_ctrl_ops, .id = V4L2_CID_OSD_TEXT, @@ -1233,13 +1185,22 @@ static const struct v4l2_ctrl_config solo_osd_text_ctrl = { .step = 1, }; +/* Motion Detection Threshold matrix */ +static const struct v4l2_ctrl_config solo_md_thresholds = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_DETECT_MD_THRESHOLD_GRID, + .dims = { SOLO_MOTION_SZ, SOLO_MOTION_SZ }, + .def = SOLO_DEF_MOT_THRESH, + .max = 65535, + .step = 1, +}; + static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, u8 ch, unsigned nr) { struct solo_enc_dev *solo_enc; struct v4l2_ctrl_handler *hdl; int ret; - int x, y; solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL); if (!solo_enc) @@ -1260,9 +1221,16 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, V4L2_CID_SHARPNESS, 0, 15, 1, 0); v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, solo_dev->fps); - v4l2_ctrl_new_custom(hdl, &solo_motion_threshold_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &solo_motion_enable_ctrl, NULL); + v4l2_ctrl_new_std_menu(hdl, &solo_ctrl_ops, + V4L2_CID_DETECT_MD_MODE, + V4L2_DETECT_MD_MODE_THRESHOLD_GRID, 0, + V4L2_DETECT_MD_MODE_DISABLED); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD, 0, 0xff, 1, + SOLO_DEF_MOT_THRESH >> 8); v4l2_ctrl_new_custom(hdl, &solo_osd_text_ctrl, NULL); + solo_enc->md_thresholds = + v4l2_ctrl_new_custom(hdl, &solo_md_thresholds, NULL); if (hdl->error) { ret = hdl->error; goto hdl_free; @@ -1283,11 +1251,6 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, solo_enc->mode = SOLO_ENC_MODE_CIF; solo_enc->motion_global = true; solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; - for (y = 0; y < SOLO_MOTION_SZ; y++) - for (x = 0; x < SOLO_MOTION_SZ; x++) - solo_enc->motion_thresholds.thresholds[y][x] = - SOLO_DEF_MOT_THRESH; - solo_enc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; solo_enc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; solo_enc->vidq.ops = &solo_enc_video_qops; diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h index 8964f8b..19cb56b 100644 --- a/drivers/staging/media/solo6x10/solo6x10.h +++ b/drivers/staging/media/solo6x10/solo6x10.h @@ -102,8 +102,6 @@ #endif #define SOLO_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) -#define V4L2_CID_MOTION_MODE (SOLO_CID_CUSTOM_BASE+0) -#define V4L2_CID_MOTION_THRESHOLD (SOLO_CID_CUSTOM_BASE+1) #define V4L2_CID_MOTION_TRACE (SOLO_CID_CUSTOM_BASE+2) #define V4L2_CID_OSD_TEXT (SOLO_CID_CUSTOM_BASE+3) @@ -113,19 +111,10 @@ * effect, 44x30 samples are used for NTSC, and 44x36 for PAL. * The 5th sample on the 10th row is (10*64)+5 = 645. * - * Using a 64x64 array will result in a problem on some architectures like - * the powerpc where the size of the argument is limited to 13 bits. - * Since both PAL and NTSC do not use the full table anyway I've chosen - * to limit the array to 45x45 (45*16 = 720, which is the maximum PAL/NTSC - * width). + * Internally it is stored as a 45x45 array (45*16 = 720, which is the + * maximum PAL/NTSC width). */ #define SOLO_MOTION_SZ (45) -struct solo_motion_thresholds { - __u16 thresholds[SOLO_MOTION_SZ][SOLO_MOTION_SZ]; -}; - -#define SOLO_IOC_G_MOTION_THRESHOLDS _IOR('V', BASE_VIDIOC_PRIVATE+0, struct solo_motion_thresholds) -#define SOLO_IOC_S_MOTION_THRESHOLDS _IOW('V', BASE_VIDIOC_PRIVATE+1, struct solo_motion_thresholds) enum SOLO_I2C_STATE { IIC_STATE_IDLE, @@ -168,6 +157,7 @@ struct solo_enc_dev { struct solo_dev *solo_dev; /* V4L2 Items */ struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *md_thresholds; struct video_device *vfd; /* General accounting */ struct mutex lock; @@ -176,7 +166,6 @@ struct solo_enc_dev { u8 mode, gop, qp, interlaced, interval; u8 bw_weight; u16 motion_thresh; - struct solo_motion_thresholds motion_thresholds; bool motion_global; bool motion_enabled; u16 width; @@ -404,7 +393,7 @@ void solo_update_mode(struct solo_enc_dev *solo_enc); /* Set the threshold for motion detection */ int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val); int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, - const struct solo_motion_thresholds *thresholds); + const u16 *thresholds); #define SOLO_DEF_MOT_THRESH 0x0300 /* Write text on OSD */ -- cgit v0.10.2 From 316d9e84a72069e04e483de0d5934c1d75f6a44c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 27 Jan 2014 11:16:05 -0300 Subject: [media] solo6x10: implement the motion detection event Use the new motion detection event. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index f7ec94c..6078aeb 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -243,6 +243,8 @@ static int solo_enc_on(struct solo_enc_dev *solo_enc) if (solo_enc->bw_weight > solo_dev->enc_bw_remain) return -EBUSY; solo_enc->sequence = 0; + solo_enc->motion_last_state = false; + solo_enc->frames_since_last_motion = 0; solo_dev->enc_bw_remain -= solo_enc->bw_weight; if (solo_enc->type == SOLO_ENC_TYPE_EXT) @@ -544,15 +546,6 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, const vop_header *vh = enc_buf->vh; int ret; - /* Check for motion flags */ - vb->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_MOTION_ON | - V4L2_BUF_FLAG_MOTION_DETECTED); - if (solo_is_motion_on(solo_enc)) { - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_ON; - if (enc_buf->motion) - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_DETECTED; - } - switch (solo_enc->fmt) { case V4L2_PIX_FMT_MPEG4: case V4L2_PIX_FMT_H264: @@ -564,9 +557,49 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, } if (!ret) { + bool send_event = false; + vb->v4l2_buf.sequence = solo_enc->sequence++; vb->v4l2_buf.timestamp.tv_sec = vop_sec(vh); vb->v4l2_buf.timestamp.tv_usec = vop_usec(vh); + + /* Check for motion flags */ + if (solo_is_motion_on(solo_enc)) { + /* It takes a few frames for the hardware to detect + * motion. Once it does it clears the motion detection + * register and it takes again a few frames before + * motion is seen. This means in practice that when the + * motion field is 1, it will go back to 0 for the next + * frame. This leads to motion detection event being + * sent all the time, which is not what we want. + * Instead wait a few frames before deciding that the + * motion has halted. After some experimentation it + * turns out that waiting for 5 frames works well. + */ + if (enc_buf->motion == 0 && + solo_enc->motion_last_state && + solo_enc->frames_since_last_motion++ > 5) + send_event = true; + else if (enc_buf->motion) { + solo_enc->frames_since_last_motion = 0; + send_event = !solo_enc->motion_last_state; + } + } + + if (send_event) { + struct v4l2_event ev = { + .type = V4L2_EVENT_MOTION_DET, + .u.motion_det = { + .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, + .frame_sequence = vb->v4l2_buf.sequence, + .region_mask = enc_buf->motion ? 1 : 0, + }, + }; + + solo_enc->motion_last_state = enc_buf->motion; + solo_enc->frames_since_last_motion = 0; + v4l2_event_queue(solo_enc->vfd, &ev); + } } vb2_buffer_done(vb, ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); @@ -1121,6 +1154,21 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl) return 0; } +static int solo_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + case V4L2_EVENT_MOTION_DET: + /* Allow for up to 30 events (1 second for NTSC) to be + * stored. */ + return v4l2_event_subscribe(fh, sub, 30, NULL); + } + return -EINVAL; +} + static const struct v4l2_file_operations solo_enc_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, @@ -1159,7 +1207,7 @@ static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { .vidioc_g_parm = solo_g_parm, /* Logging and events */ .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_subscribe_event = solo_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h index 19cb56b..35f9486 100644 --- a/drivers/staging/media/solo6x10/solo6x10.h +++ b/drivers/staging/media/solo6x10/solo6x10.h @@ -96,11 +96,6 @@ #define SOLO_DEFAULT_QP 3 -#ifndef V4L2_BUF_FLAG_MOTION_ON -#define V4L2_BUF_FLAG_MOTION_ON 0x10000 -#define V4L2_BUF_FLAG_MOTION_DETECTED 0x20000 -#endif - #define SOLO_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) #define V4L2_CID_MOTION_TRACE (SOLO_CID_CUSTOM_BASE+2) #define V4L2_CID_OSD_TEXT (SOLO_CID_CUSTOM_BASE+3) @@ -168,6 +163,8 @@ struct solo_enc_dev { u16 motion_thresh; bool motion_global; bool motion_enabled; + bool motion_last_state; + u8 frames_since_last_motion; u16 width; u16 height; -- cgit v0.10.2 From 761f9aa23f2ac187aa1bed58215f09f3c8456295 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Feb 2014 14:51:24 -0300 Subject: [media] solo6x10: fix 'dma from stack' warning Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/solo6x10/solo6x10-disp.c b/drivers/staging/media/solo6x10/solo6x10-disp.c index 44d98b8..b529a96 100644 --- a/drivers/staging/media/solo6x10/solo6x10-disp.c +++ b/drivers/staging/media/solo6x10/solo6x10-disp.c @@ -213,19 +213,21 @@ int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, const u16 *thresholds) { + const unsigned size = sizeof(u16) * 64; u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2; - u16 buf[64]; + u16 *buf; int x, y; int ret = 0; - memset(buf, 0, sizeof(buf)); + buf = kzalloc(size, GFP_KERNEL); for (y = 0; y < SOLO_MOTION_SZ; y++) { for (x = 0; x < SOLO_MOTION_SZ; x++) buf[x] = cpu_to_le16(thresholds[y * SOLO_MOTION_SZ + x]); ret |= solo_p2m_dma(solo_dev, 1, buf, - SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * sizeof(buf), - sizeof(buf), 0, 0); + SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * size, + size, 0, 0); } + kfree(buf); return ret; } -- cgit v0.10.2 From c5b250ad3593656811c31a1d480745270b47658b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 15 Feb 2014 09:15:53 -0300 Subject: [media] solo6x10: check dma_map_sg() return value The dma_map_sg() function can fail, so check for the return value. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index 6078aeb..968666a 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -478,8 +478,9 @@ static int solo_fill_jpeg(struct solo_enc_dev *solo_enc, vb2_set_plane_payload(vb, 0, vop_jpeg_size(vh) + solo_enc->jpeg_len); /* may discard all previous data in vbuf->sgl */ - dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, - DMA_FROM_DEVICE); + if (!dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, + DMA_FROM_DEVICE)) + return -ENOMEM; ret = solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf, vop_jpeg_offset(vh) - SOLO_JPEG_EXT_ADDR(solo_dev), frame_size, SOLO_JPEG_EXT_ADDR(solo_dev), @@ -525,8 +526,9 @@ static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, frame_size = ALIGN(vop_mpeg_size(vh) + skip, DMA_ALIGN); /* may discard all previous data in vbuf->sgl */ - dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, - DMA_FROM_DEVICE); + if (!dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, + DMA_FROM_DEVICE)) + return -ENOMEM; ret = solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size, SOLO_MP4E_EXT_ADDR(solo_dev), SOLO_MP4E_EXT_SIZE(solo_dev)); -- cgit v0.10.2 From 0ee58f84871997e85c99dfc6357b0671a5a6b706 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:39:04 -0300 Subject: [media] go7007: add motion detection support This patch adds motion detection support to the go7007 driver using the new motion detection controls, events. The global motion detection works fine, but the regional motion detection support probably needs more work. There seems to be some interaction between regions that makes setting correct thresholds difficult. The exact meaning of the thresholds isn't entirely clear either. I do not have any documentation, the only information I have is the custom code in the driver and a modet.c application. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index 6f1beca..c200601 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "go7007-priv.h" @@ -332,20 +333,33 @@ EXPORT_SYMBOL(go7007_register_encoder); int go7007_start_encoder(struct go7007 *go) { u8 *fw; - int fw_len, rv = 0, i; + int fw_len, rv = 0, i, x, y; u16 intr_val, intr_data; go->modet_enable = 0; - if (!go->dvd_mode) - for (i = 0; i < 4; ++i) { - if (go->modet[i].enable) { - go->modet_enable = 1; - continue; + for (i = 0; i < 4; i++) + go->modet[i].enable = 0; + + switch (v4l2_ctrl_g_ctrl(go->modet_mode)) { + case V4L2_DETECT_MD_MODE_GLOBAL: + memset(go->modet_map, 0, sizeof(go->modet_map)); + go->modet[0].enable = 1; + go->modet_enable = 1; + break; + case V4L2_DETECT_MD_MODE_REGION_GRID: + for (y = 0; y < go->height / 16; y++) { + for (x = 0; x < go->width / 16; x++) { + int idx = y * go->width / 16 + x; + + go->modet[go->modet_map[idx]].enable = 1; } - go->modet[i].pixel_threshold = 32767; - go->modet[i].motion_threshold = 32767; - go->modet[i].mb_threshold = 32767; } + go->modet_enable = 1; + break; + } + + if (go->dvd_mode) + go->modet_enable = 0; if (go7007_construct_fw_image(go, &fw, &fw_len) < 0) return -1; @@ -383,44 +397,89 @@ static inline void store_byte(struct go7007_buffer *vb, u8 byte) } } +static void go7007_set_motion_regions(struct go7007 *go, struct go7007_buffer *vb, + u32 motion_regions) +{ + if (motion_regions != go->modet_event_status) { + struct v4l2_event ev = { + .type = V4L2_EVENT_MOTION_DET, + .u.motion_det = { + .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, + .frame_sequence = vb->vb.v4l2_buf.sequence, + .region_mask = motion_regions, + }, + }; + + v4l2_event_queue(&go->vdev, &ev); + go->modet_event_status = motion_regions; + } +} + /* - * Deliver the last video buffer and get a new one to start writing to. + * Determine regions with motion and send a motion detection event + * in case of changes. */ -static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buffer *vb) +static void go7007_motion_regions(struct go7007 *go, struct go7007_buffer *vb) { - struct go7007_buffer *vb_tmp = NULL; u32 *bytesused = &vb->vb.v4l2_planes[0].bytesused; + unsigned motion[4] = { 0, 0, 0, 0 }; + u32 motion_regions = 0; + unsigned stride = (go->width + 7) >> 3; + unsigned x, y; int i; - if (vb) { - if (vb->modet_active) { - if (*bytesused + 216 < GO7007_BUF_SIZE) { - for (i = 0; i < 216; ++i) - store_byte(vb, go->active_map[i]); - *bytesused -= 216; - } else - vb->modet_active = 0; + for (i = 0; i < 216; ++i) + store_byte(vb, go->active_map[i]); + for (y = 0; y < go->height / 16; y++) { + for (x = 0; x < go->width / 16; x++) { + if (!(go->active_map[y * stride + (x >> 3)] & (1 << (x & 7)))) + continue; + motion[go->modet_map[y * (go->width / 16) + x]]++; } - vb->vb.v4l2_buf.sequence = go->next_seq++; - v4l2_get_timestamp(&vb->vb.v4l2_buf.timestamp); - vb_tmp = vb; + } + motion_regions = ((motion[0] > 0) << 0) | + ((motion[1] > 0) << 1) | + ((motion[2] > 0) << 2) | + ((motion[3] > 0) << 3); + *bytesused -= 216; + go7007_set_motion_regions(go, vb, motion_regions); +} + +/* + * Deliver the last video buffer and get a new one to start writing to. + */ +static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buffer *vb) +{ + u32 *bytesused = &vb->vb.v4l2_planes[0].bytesused; + struct go7007_buffer *vb_tmp = NULL; + + if (vb == NULL) { spin_lock(&go->spinlock); - list_del(&vb->list); - if (list_empty(&go->vidq_active)) - vb = NULL; - else - vb = list_first_entry(&go->vidq_active, struct go7007_buffer, list); - go->active_buf = vb; + if (!list_empty(&go->vidq_active)) + vb = go->active_buf = + list_first_entry(&go->vidq_active, struct go7007_buffer, list); spin_unlock(&go->spinlock); - vb2_buffer_done(&vb_tmp->vb, VB2_BUF_STATE_DONE); + go->next_seq++; return vb; } + + vb->vb.v4l2_buf.sequence = go->next_seq++; + if (vb->modet_active && *bytesused + 216 < GO7007_BUF_SIZE) + go7007_motion_regions(go, vb); + else + go7007_set_motion_regions(go, vb, 0); + + v4l2_get_timestamp(&vb->vb.v4l2_buf.timestamp); + vb_tmp = vb; spin_lock(&go->spinlock); - if (!list_empty(&go->vidq_active)) - vb = go->active_buf = - list_first_entry(&go->vidq_active, struct go7007_buffer, list); + list_del(&vb->list); + if (list_empty(&go->vidq_active)) + vb = NULL; + else + vb = list_first_entry(&go->vidq_active, struct go7007_buffer, list); + go->active_buf = vb; spin_unlock(&go->spinlock); - go->next_seq++; + vb2_buffer_done(&vb_tmp->vb, VB2_BUF_STATE_DONE); return vb; } diff --git a/drivers/staging/media/go7007/go7007-fw.c b/drivers/staging/media/go7007/go7007-fw.c index 814ce08..f60640b 100644 --- a/drivers/staging/media/go7007/go7007-fw.c +++ b/drivers/staging/media/go7007/go7007-fw.c @@ -1432,22 +1432,26 @@ static int audio_to_package(struct go7007 *go, __le16 *code, int space) static int modet_to_package(struct go7007 *go, __le16 *code, int space) { + bool has_modet0 = go->modet[0].enable; + bool has_modet1 = go->modet[1].enable; + bool has_modet2 = go->modet[2].enable; + bool has_modet3 = go->modet[3].enable; int ret, mb, i, addr, cnt = 0; u16 pack[32]; u16 thresholds[] = { 0x200e, 0, - 0xbf82, go->modet[0].pixel_threshold, - 0xbf83, go->modet[1].pixel_threshold, - 0xbf84, go->modet[2].pixel_threshold, - 0xbf85, go->modet[3].pixel_threshold, - 0xbf86, go->modet[0].motion_threshold, - 0xbf87, go->modet[1].motion_threshold, - 0xbf88, go->modet[2].motion_threshold, - 0xbf89, go->modet[3].motion_threshold, - 0xbf8a, go->modet[0].mb_threshold, - 0xbf8b, go->modet[1].mb_threshold, - 0xbf8c, go->modet[2].mb_threshold, - 0xbf8d, go->modet[3].mb_threshold, + 0xbf82, has_modet0 ? go->modet[0].pixel_threshold : 32767, + 0xbf83, has_modet1 ? go->modet[1].pixel_threshold : 32767, + 0xbf84, has_modet2 ? go->modet[2].pixel_threshold : 32767, + 0xbf85, has_modet3 ? go->modet[3].pixel_threshold : 32767, + 0xbf86, has_modet0 ? go->modet[0].motion_threshold : 32767, + 0xbf87, has_modet1 ? go->modet[1].motion_threshold : 32767, + 0xbf88, has_modet2 ? go->modet[2].motion_threshold : 32767, + 0xbf89, has_modet3 ? go->modet[3].motion_threshold : 32767, + 0xbf8a, has_modet0 ? go->modet[0].mb_threshold : 32767, + 0xbf8b, has_modet1 ? go->modet[1].mb_threshold : 32767, + 0xbf8c, has_modet2 ? go->modet[2].mb_threshold : 32767, + 0xbf8d, has_modet3 ? go->modet[3].mb_threshold : 32767, 0xbf8e, 0, 0xbf8f, 0, 0, 0, diff --git a/drivers/staging/media/go7007/go7007-priv.h b/drivers/staging/media/go7007/go7007-priv.h index 6e16af7..a8aefed 100644 --- a/drivers/staging/media/go7007/go7007-priv.h +++ b/drivers/staging/media/go7007/go7007-priv.h @@ -75,6 +75,20 @@ struct go7007; #define GO7007_AUDIO_I2S_MASTER (1<<16) #define GO7007_AUDIO_OKI_MODE (1<<17) +#define GO7007_CID_CUSTOM_BASE (V4L2_CID_DETECT_CLASS_BASE + 0x1000) +#define V4L2_CID_PIXEL_THRESHOLD0 (GO7007_CID_CUSTOM_BASE+1) +#define V4L2_CID_MOTION_THRESHOLD0 (GO7007_CID_CUSTOM_BASE+2) +#define V4L2_CID_MB_THRESHOLD0 (GO7007_CID_CUSTOM_BASE+3) +#define V4L2_CID_PIXEL_THRESHOLD1 (GO7007_CID_CUSTOM_BASE+4) +#define V4L2_CID_MOTION_THRESHOLD1 (GO7007_CID_CUSTOM_BASE+5) +#define V4L2_CID_MB_THRESHOLD1 (GO7007_CID_CUSTOM_BASE+6) +#define V4L2_CID_PIXEL_THRESHOLD2 (GO7007_CID_CUSTOM_BASE+7) +#define V4L2_CID_MOTION_THRESHOLD2 (GO7007_CID_CUSTOM_BASE+8) +#define V4L2_CID_MB_THRESHOLD2 (GO7007_CID_CUSTOM_BASE+9) +#define V4L2_CID_PIXEL_THRESHOLD3 (GO7007_CID_CUSTOM_BASE+10) +#define V4L2_CID_MOTION_THRESHOLD3 (GO7007_CID_CUSTOM_BASE+11) +#define V4L2_CID_MB_THRESHOLD3 (GO7007_CID_CUSTOM_BASE+12) + struct go7007_board_info { unsigned int flags; int hpi_buffer_cap; @@ -168,6 +182,7 @@ struct go7007 { struct v4l2_ctrl *mpeg_video_aspect_ratio; struct v4l2_ctrl *mpeg_video_b_frames; struct v4l2_ctrl *mpeg_video_rep_seqheader; + struct v4l2_ctrl *modet_mode; enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status; spinlock_t spinlock; struct mutex hw_lock; @@ -216,6 +231,7 @@ struct go7007 { } modet[4]; unsigned char modet_map[1624]; unsigned char active_map[216]; + u32 modet_event_status; /* Video streaming */ struct mutex queue_lock; diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index ecb5336..4bf143f 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -36,7 +36,6 @@ #include #include -#include "go7007.h" #include "go7007-priv.h" #define call_all(dev, o, f, args...) \ @@ -189,7 +188,7 @@ static void set_formatting(struct go7007 *go) static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) { int sensor_height = 0, sensor_width = 0; - int width, height, i; + int width, height; if (fmt != NULL && !valid_pixelformat(fmt->fmt.pix.pixelformat)) return -EINVAL; @@ -253,10 +252,6 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) go->height = height; go->encoder_h_offset = go->board_info->sensor_h_offset; go->encoder_v_offset = go->board_info->sensor_v_offset; - for (i = 0; i < 4; ++i) - go->modet[i].enable = 0; - for (i = 0; i < 1624; ++i) - go->modet_map[i] = 0; if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { struct v4l2_mbus_framefmt mbus_fmt; @@ -286,64 +281,6 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) return 0; } -#if 0 -static int clip_to_modet_map(struct go7007 *go, int region, - struct v4l2_clip *clip_list) -{ - struct v4l2_clip clip, *clip_ptr; - int x, y, mbnum; - - /* Check if coordinates are OK and if any macroblocks are already - * used by other regions (besides 0) */ - clip_ptr = clip_list; - while (clip_ptr) { - if (copy_from_user(&clip, clip_ptr, sizeof(clip))) - return -EFAULT; - if (clip.c.left < 0 || (clip.c.left & 0xF) || - clip.c.width <= 0 || (clip.c.width & 0xF)) - return -EINVAL; - if (clip.c.left + clip.c.width > go->width) - return -EINVAL; - if (clip.c.top < 0 || (clip.c.top & 0xF) || - clip.c.height <= 0 || (clip.c.height & 0xF)) - return -EINVAL; - if (clip.c.top + clip.c.height > go->height) - return -EINVAL; - for (y = 0; y < clip.c.height; y += 16) - for (x = 0; x < clip.c.width; x += 16) { - mbnum = (go->width >> 4) * - ((clip.c.top + y) >> 4) + - ((clip.c.left + x) >> 4); - if (go->modet_map[mbnum] != 0 && - go->modet_map[mbnum] != region) - return -EBUSY; - } - clip_ptr = clip.next; - } - - /* Clear old region macroblocks */ - for (mbnum = 0; mbnum < 1624; ++mbnum) - if (go->modet_map[mbnum] == region) - go->modet_map[mbnum] = 0; - - /* Claim macroblocks in this list */ - clip_ptr = clip_list; - while (clip_ptr) { - if (copy_from_user(&clip, clip_ptr, sizeof(clip))) - return -EFAULT; - for (y = 0; y < clip.c.height; y += 16) - for (x = 0; x < clip.c.width; x += 16) { - mbnum = (go->width >> 4) * - ((clip.c.top + y) >> 4) + - ((clip.c.left + x) >> 4); - go->modet_map[mbnum] = region; - } - clip_ptr = clip.next; - } - return 0; -} -#endif - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -495,6 +432,7 @@ static int go7007_start_streaming(struct vb2_queue *q, unsigned int count) mutex_lock(&go->hw_lock); go->next_seq = 0; go->active_buf = NULL; + go->modet_event_status = 0; q->streaming = 1; if (go7007_start_encoder(go) < 0) ret = -EIO; @@ -850,41 +788,76 @@ static int vidioc_log_status(struct file *file, void *priv) return call_all(&go->v4l2_dev, core, log_status); } -/* FIXME: - Those ioctls are private, and not needed, since several standard - extended controls already provide streaming control. - So, those ioctls should be converted into vidioc_g_ext_ctrls() - and vidioc_s_ext_ctrls() - */ - -#if 0 - case GO7007IOC_S_MD_PARAMS: - { - struct go7007_md_params *mdp = arg; +static int vidioc_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ - if (mdp->region > 3) - return -EINVAL; - if (mdp->trigger > 0) { - go->modet[mdp->region].pixel_threshold = - mdp->pixel_threshold >> 1; - go->modet[mdp->region].motion_threshold = - mdp->motion_threshold >> 1; - go->modet[mdp->region].mb_threshold = - mdp->trigger >> 1; - go->modet[mdp->region].enable = 1; - } else - go->modet[mdp->region].enable = 0; - /* fall-through */ + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + case V4L2_EVENT_MOTION_DET: + /* Allow for up to 30 events (1 second for NTSC) to be + * stored. */ + return v4l2_event_subscribe(fh, sub, 30, NULL); } - case GO7007IOC_S_MD_REGION: - { - struct go7007_md_region *region = arg; + return -EINVAL; +} - if (region->region < 1 || region->region > 3) - return -EINVAL; - return clip_to_modet_map(go, region->region, region->clips); + +static int go7007_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct go7007 *go = + container_of(ctrl->handler, struct go7007, hdl); + unsigned y; + u8 *mt; + + switch (ctrl->id) { + case V4L2_CID_PIXEL_THRESHOLD0: + go->modet[0].pixel_threshold = ctrl->val; + break; + case V4L2_CID_MOTION_THRESHOLD0: + go->modet[0].motion_threshold = ctrl->val; + break; + case V4L2_CID_MB_THRESHOLD0: + go->modet[0].mb_threshold = ctrl->val; + break; + case V4L2_CID_PIXEL_THRESHOLD1: + go->modet[1].pixel_threshold = ctrl->val; + break; + case V4L2_CID_MOTION_THRESHOLD1: + go->modet[1].motion_threshold = ctrl->val; + break; + case V4L2_CID_MB_THRESHOLD1: + go->modet[1].mb_threshold = ctrl->val; + break; + case V4L2_CID_PIXEL_THRESHOLD2: + go->modet[2].pixel_threshold = ctrl->val; + break; + case V4L2_CID_MOTION_THRESHOLD2: + go->modet[2].motion_threshold = ctrl->val; + break; + case V4L2_CID_MB_THRESHOLD2: + go->modet[2].mb_threshold = ctrl->val; + break; + case V4L2_CID_PIXEL_THRESHOLD3: + go->modet[3].pixel_threshold = ctrl->val; + break; + case V4L2_CID_MOTION_THRESHOLD3: + go->modet[3].motion_threshold = ctrl->val; + break; + case V4L2_CID_MB_THRESHOLD3: + go->modet[3].mb_threshold = ctrl->val; + break; + case V4L2_CID_DETECT_MD_REGION_GRID: + mt = go->modet_map; + for (y = 0; y < go->height / 16; y++, mt += go->width / 16) + memcpy(mt, ctrl->p_new.p_u8 + y * (720 / 16), go->width / 16); + break; + default: + return -EINVAL; } -#endif + return 0; +} static struct v4l2_file_operations go7007_fops = { .owner = THIS_MODULE, @@ -926,7 +899,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_enum_frameintervals = vidioc_enum_frameintervals, .vidioc_log_status = vidioc_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_subscribe_event = vidioc_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; @@ -938,12 +911,144 @@ static struct video_device go7007_template = { .tvnorms = V4L2_STD_ALL, }; +static const struct v4l2_ctrl_ops go7007_ctrl_ops = { + .s_ctrl = go7007_s_ctrl, +}; + +static const struct v4l2_ctrl_config go7007_pixel_threshold0_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_PIXEL_THRESHOLD0, + .name = "Pixel Threshold Region 0", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 20, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_motion_threshold0_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MOTION_THRESHOLD0, + .name = "Motion Threshold Region 0", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 80, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_mb_threshold0_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MB_THRESHOLD0, + .name = "MB Threshold Region 0", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 200, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_pixel_threshold1_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_PIXEL_THRESHOLD1, + .name = "Pixel Threshold Region 1", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 20, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_motion_threshold1_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MOTION_THRESHOLD1, + .name = "Motion Threshold Region 1", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 80, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_mb_threshold1_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MB_THRESHOLD1, + .name = "MB Threshold Region 1", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 200, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_pixel_threshold2_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_PIXEL_THRESHOLD2, + .name = "Pixel Threshold Region 2", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 20, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_motion_threshold2_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MOTION_THRESHOLD2, + .name = "Motion Threshold Region 2", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 80, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_mb_threshold2_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MB_THRESHOLD2, + .name = "MB Threshold Region 2", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 200, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_pixel_threshold3_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_PIXEL_THRESHOLD3, + .name = "Pixel Threshold Region 3", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 20, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_motion_threshold3_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MOTION_THRESHOLD3, + .name = "Motion Threshold Region 3", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 80, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_mb_threshold3_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MB_THRESHOLD3, + .name = "MB Threshold Region 3", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 200, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_mb_regions_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_DETECT_MD_REGION_GRID, + .dims = { 576 / 16, 720 / 16 }, + .max = 3, + .step = 1, +}; + int go7007_v4l2_ctrl_init(struct go7007 *go) { struct v4l2_ctrl_handler *hdl = &go->hdl; struct v4l2_ctrl *ctrl; - v4l2_ctrl_handler_init(hdl, 13); + v4l2_ctrl_handler_init(hdl, 22); go->mpeg_video_gop_size = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, 34, 1, 15); go->mpeg_video_gop_closure = v4l2_ctrl_new_std(hdl, NULL, @@ -968,6 +1073,24 @@ int go7007_v4l2_ctrl_init(struct go7007 *go) V4L2_JPEG_ACTIVE_MARKER_DHT); if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold0_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold0_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold0_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold1_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold1_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold1_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold2_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold2_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold2_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold3_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold3_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold3_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_mb_regions_ctrl, NULL); + go->modet_mode = v4l2_ctrl_new_std_menu(hdl, NULL, + V4L2_CID_DETECT_MD_MODE, + V4L2_DETECT_MD_MODE_REGION_GRID, + 1 << V4L2_DETECT_MD_MODE_THRESHOLD_GRID, + V4L2_DETECT_MD_MODE_DISABLED); if (hdl->error) { int rv = hdl->error; diff --git a/drivers/staging/media/go7007/go7007.h b/drivers/staging/media/go7007/go7007.h deleted file mode 100644 index 54b9897..0000000 --- a/drivers/staging/media/go7007/go7007.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and the associated README documentation file (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -struct go7007_md_params { - __u16 region; - __u16 trigger; - __u16 pixel_threshold; - __u16 motion_threshold; - __u32 reserved[8]; -}; - -struct go7007_md_region { - __u16 region; - __u16 flags; - struct v4l2_clip *clips; - __u32 reserved[8]; -}; - -#define GO7007IOC_S_MD_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 6, \ - struct go7007_md_params) -#define GO7007IOC_G_MD_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 7, \ - struct go7007_md_params) -#define GO7007IOC_S_MD_REGION _IOW('V', BASE_VIDIOC_PRIVATE + 8, \ - struct go7007_md_region) diff --git a/drivers/staging/media/go7007/saa7134-go7007.c b/drivers/staging/media/go7007/saa7134-go7007.c index e40f7fb..df5cb1c 100644 --- a/drivers/staging/media/go7007/saa7134-go7007.c +++ b/drivers/staging/media/go7007/saa7134-go7007.c @@ -33,7 +33,6 @@ #include "saa7134.h" #include "saa7134-reg.h" -#include "go7007.h" #include "go7007-priv.h" /*#define GO7007_HPI_DEBUG*/ -- cgit v0.10.2 From 028e2b4fb69f03a294a69b27c99f05002b8ac021 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jul 2014 10:47:56 -0300 Subject: [media] DocBook improvement for U8 and U16 control types Removed the "This type is only used-in array controls." sentence in DocBook which was thought to only confuse. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml index d9a3f23..62163d9 100644 --- a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml +++ b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml @@ -518,8 +518,8 @@ Older drivers which do not support this feature return an any An unsigned 8-bit valued control ranging from minimum to maximum inclusive. The step value indicates the increment between -values which are actually different on the hardware. This type is only used -in array controls. +values which are actually different on the hardware. + V4L2_CTRL_TYPE_U16 @@ -528,8 +528,8 @@ in array controls. any An unsigned 16-bit valued control ranging from minimum to maximum inclusive. The step value indicates the increment between -values which are actually different on the hardware. This type is only used -in array controls. +values which are actually different on the hardware. + -- cgit v0.10.2 From 0d5e8c4313c83dc2d60519a219d517a13ba8a432 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jul 2014 12:31:23 -0300 Subject: [media] Fix 64-bit division fall-out from 64-bit control ranges Commit 0ba2aeb6dab80920edd9cf5b93b1ea4d6913b8f3 increased the internal control ranges to 64 bit, but that caused problems in drivers that use the minimum/maximum/step/default_value control values in a division or modulus operations since not all architectures support those natively. Luckily, in almost all cases it is possible to just cast to 32 bits (the control value is known to be 32 bits, so it is safe to cast). Only in v4l2-ctrls.c was it necessary to use do_div in one function. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index df97033..dbd8c14 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -403,7 +403,7 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) if (ctrl->val <= ctrl->default_value) { /* Pack it into 0..1 step 0.125, register values 0..8 */ unsigned long range = ctrl->default_value - ctrl->minimum; - data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range; + data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; dev_dbg(&client->dev, "Setting gain %d\n", data); data = reg_write(client, MT9M001_GLOBAL_GAIN, data); @@ -413,7 +413,7 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) /* Pack it into 1.125..15 variable step, register values 9..67 */ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ unsigned long range = ctrl->maximum - ctrl->default_value - 1; - unsigned long gain = ((ctrl->val - ctrl->default_value - 1) * + unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * 111 + range / 2) / range + 9; if (gain <= 32) @@ -434,7 +434,7 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_EXPOSURE_AUTO: if (ctrl->val == V4L2_EXPOSURE_MANUAL) { unsigned long range = exp->maximum - exp->minimum; - unsigned long shutter = ((exp->val - exp->minimum) * 1048 + + unsigned long shutter = ((exp->val - (s32)exp->minimum) * 1048 + range / 2) / range + 1; dev_dbg(&client->dev, diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index ee7bb0f..f8358c4 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -474,7 +474,7 @@ static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl) if (ctrl->val <= ctrl->default_value) { /* Pack it into 0..1 step 0.125, register values 0..8 */ unsigned long range = ctrl->default_value - ctrl->minimum; - data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range; + data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; dev_dbg(&client->dev, "Setting gain %d\n", data); data = reg_write(client, MT9T031_GLOBAL_GAIN, data); @@ -485,7 +485,7 @@ static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl) /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ unsigned long range = ctrl->maximum - ctrl->default_value - 1; /* calculated gain: map 65..127 to 9..1024 step 0.125 */ - unsigned long gain = ((ctrl->val - ctrl->default_value - 1) * + unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * 1015 + range / 2) / range + 9; if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ @@ -507,7 +507,7 @@ static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_EXPOSURE_AUTO: if (ctrl->val == V4L2_EXPOSURE_MANUAL) { unsigned int range = exp->maximum - exp->minimum; - unsigned int shutter = ((exp->val - exp->minimum) * 1048 + + unsigned int shutter = ((exp->val - (s32)exp->minimum) * 1048 + range / 2) / range + 1; u32 old; diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index f9f95f8..99022c8 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -583,7 +583,7 @@ static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) /* mt9v022 has minimum == default */ unsigned long range = gain->maximum - gain->minimum; /* Valid values 16 to 64, 32 to 64 must be even. */ - unsigned long gain_val = ((gain->val - gain->minimum) * + unsigned long gain_val = ((gain->val - (s32)gain->minimum) * 48 + range / 2) / range + 16; if (gain_val >= 32) @@ -608,7 +608,7 @@ static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) } else { struct v4l2_ctrl *exp = mt9v022->exposure; unsigned long range = exp->maximum - exp->minimum; - unsigned long shutter = ((exp->val - exp->minimum) * + unsigned long shutter = ((exp->val - (s32)exp->minimum) * 479 + range / 2) / range + 1; /* diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index 67ac72e..0c5d2db 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -265,7 +265,7 @@ static int keene_s_ctrl(struct v4l2_ctrl *ctrl) return keene_cmd_set(radio); case V4L2_CID_AUDIO_COMPRESSION_GAIN: - radio->tx = db2tx[(ctrl->val - ctrl->minimum) / ctrl->step]; + radio->tx = db2tx[(ctrl->val - (s32)ctrl->minimum) / (s32)ctrl->step]; return keene_cmd_set(radio); } return -EINVAL; diff --git a/drivers/media/usb/gspca/autogain_functions.c b/drivers/media/usb/gspca/autogain_functions.c index 67db674..0e9ee8b 100644 --- a/drivers/media/usb/gspca/autogain_functions.c +++ b/drivers/media/usb/gspca/autogain_functions.c @@ -121,9 +121,9 @@ int gspca_coarse_grained_expo_autogain( orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure); - gain_low = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / + gain_low = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) / 5 * 2 + gspca_dev->gain->minimum; - gain_high = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / + gain_high = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) / 5 * 4 + gspca_dev->gain->minimum; /* If we are of a multiple of deadzone, do multiple steps to reach the diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c index 2fd1c5e..a7b7a99 100644 --- a/drivers/media/usb/gspca/pac7302.c +++ b/drivers/media/usb/gspca/pac7302.c @@ -394,9 +394,9 @@ static void setbrightcont(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ for (i = 0; i < 10; i++) { v = max[i]; - v += (sd->brightness->val - sd->brightness->maximum) - * 150 / sd->brightness->maximum; /* 200 ? */ - v -= delta[i] * sd->contrast->val / sd->contrast->maximum; + v += (sd->brightness->val - (s32)sd->brightness->maximum) + * 150 / (s32)sd->brightness->maximum; /* 200 ? */ + v -= delta[i] * sd->contrast->val / (s32)sd->contrast->maximum; if (v < 0) v = 0; else if (v > 0xff) @@ -419,7 +419,7 @@ static void setcolors(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x11, 0x01); reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ for (i = 0; i < 9; i++) { - v = a[i] * sd->saturation->val / sd->saturation->maximum; + v = a[i] * sd->saturation->val / (s32)sd->saturation->maximum; v += b[i]; reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); reg_w(gspca_dev, 0x0f + 2 * i + 1, v); diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c index ecbcb39..6696b2e 100644 --- a/drivers/media/usb/gspca/sonixb.c +++ b/drivers/media/usb/gspca/sonixb.c @@ -913,7 +913,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) desired_avg_lum, deadzone)) sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; } else { - int gain_knee = gspca_dev->gain->maximum * 9 / 10; + int gain_knee = (s32)gspca_dev->gain->maximum * 9 / 10; if (gspca_expo_autogain(gspca_dev, avg_lum, desired_avg_lum, deadzone, gain_knee, sd->exposure_knee)) sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5c3b8de..8552c83 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1303,7 +1303,7 @@ static void std_log(const struct v4l2_ctrl *ctrl) val = clamp_t(typeof(val), val, \ (ctrl)->minimum, (ctrl)->maximum); \ offset = (val) - (ctrl)->minimum; \ - offset = (ctrl)->step * (offset / (ctrl)->step); \ + offset = (ctrl)->step * (offset / (s32)(ctrl)->step); \ val = (ctrl)->minimum + offset; \ 0; \ }) @@ -1313,12 +1313,24 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) { size_t len; + u64 offset; + s64 val; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl); case V4L2_CTRL_TYPE_INTEGER64: - return ROUND_TO_RANGE(ptr.p_s64[idx], u64, ctrl); + /* + * We can't use the ROUND_TO_RANGE define here due to + * the u64 divide that needs special care. + */ + val = ptr.p_s64[idx]; + val += ctrl->step / 2; + val = clamp_t(s64, val, ctrl->minimum, ctrl->maximum); + offset = val - ctrl->minimum; + do_div(offset, ctrl->step); + ptr.p_s64[idx] = ctrl->minimum + offset * ctrl->step; + return 0; case V4L2_CTRL_TYPE_U8: return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl); case V4L2_CTRL_TYPE_U16: @@ -1353,7 +1365,7 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, len = strlen(ptr.p_char + idx); if (len < ctrl->minimum) return -ERANGE; - if ((len - ctrl->minimum) % ctrl->step) + if ((len - (u32)ctrl->minimum) % (u32)ctrl->step) return -ERANGE; return 0; -- cgit v0.10.2 From 977ff0e4fb3460df9f3dd27de92d60786be28645 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 May 2014 22:07:55 -0300 Subject: [media] v4l: Add ARGB and XRGB pixel formats The existing RGB pixel formats are ill-defined in respect to their alpha bits and their meaning is driver dependent. Create new standard ARGB and XRGB variants with clearly defined meanings and make the existing variants deprecated. The new pixel formats 4CC values have been selected to match the DRM 4CCs for the same in-memory formats. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml b/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml index e1c4f8b..5f1602f 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml @@ -130,9 +130,9 @@ colorspace V4L2_COLORSPACE_SRGB. b1 b0 - - V4L2_PIX_FMT_RGB444 - 'R444' + + V4L2_PIX_FMT_ARGB444 + 'AR12' g3 g2 @@ -152,9 +152,31 @@ colorspace V4L2_COLORSPACE_SRGB. r1 r0 - - V4L2_PIX_FMT_RGB555 - 'RGBO' + + V4L2_PIX_FMT_XRGB444 + 'XR12' + + g3 + g2 + g1 + g0 + b3 + b2 + b1 + b0 + + - + - + - + - + r3 + r2 + r1 + r0 + + + V4L2_PIX_FMT_ARGB555 + 'AR15' g2 g1 @@ -174,6 +196,28 @@ colorspace V4L2_COLORSPACE_SRGB. g4 g3 + + V4L2_PIX_FMT_XRGB555 + 'XR15' + + g2 + g1 + g0 + b4 + b3 + b2 + b1 + b0 + + - + r4 + r3 + r2 + r1 + r0 + g4 + g3 + V4L2_PIX_FMT_RGB565 'RGBP' @@ -341,9 +385,9 @@ colorspace V4L2_COLORSPACE_SRGB. b1 b0 - - V4L2_PIX_FMT_BGR32 - 'BGR4' + + V4L2_PIX_FMT_ABGR32 + 'AR24' b7 b6 @@ -381,9 +425,49 @@ colorspace V4L2_COLORSPACE_SRGB. a1 a0 - - V4L2_PIX_FMT_RGB32 - 'RGB4' + + V4L2_PIX_FMT_XBGR32 + 'XR24' + + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + + - + - + - + - + - + - + - + - + + + V4L2_PIX_FMT_ARGB32 + 'AX24' a7 a6 @@ -421,18 +505,76 @@ colorspace V4L2_COLORSPACE_SRGB. b1 b0 + + V4L2_PIX_FMT_XRGB32 + 'BX24' + + - + - + - + - + - + - + - + - + + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 +
- Bit 7 is the most significant bit. The value of the a = alpha -bits is undefined when reading from the driver, ignored when writing -to the driver, except when alpha blending has been negotiated for a -Video Overlay or -Video Output Overlay or when the alpha component has been configured -for a Video Capture by means of V4L2_CID_ALPHA_COMPONENT - control. + Bit 7 is the most significant bit. + + The usage and value of the alpha bits (a) in the ARGB and ABGR formats + (collectively referred to as alpha formats) depend on the device type and + hardware operation. Capture devices + (including capture queues of mem-to-mem devices) fill the alpha component in + memory. When the device outputs an alpha channel the alpha component will + have a meaningful value. Otherwise, when the device doesn't output an alpha + channel but can set the alpha bit to a user-configurable value, the V4L2_CID_ALPHA_COMPONENT + control is used to specify that alpha value, and the alpha component + of all pixels will be set to the value specified by that control. Otherwise + a corresponding format without an alpha component (XRGB or XBGR) must be + used instead of an alpha format. + + Output devices (including output queues + of mem-to-mem devices and video output overlay + devices) read the alpha component from memory. When the device processes the + alpha channel the alpha component must be filled with meaningful values by + applications. Otherwise a corresponding format without an alpha component + (XRGB or XBGR) must be used instead of an alpha format. + + The XRGB and XBGR formats contain undefined bits (-). Applications, + devices and drivers must ignore those bits, for both capture and output + devices. <constant>V4L2_PIX_FMT_BGR24</constant> 4 × 4 pixel @@ -512,6 +654,239 @@ image + Formats defined in are + deprecated and must not be used by new drivers. They are documented here for + reference. The meaning of their alpha bits (a) is ill-defined and + interpreted as in either the corresponding ARGB or XRGB format, depending on + the driver. + + + Deprecated Packed RGB Image Formats + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Identifier + Code +   + Byte 0 in memory + Byte 1 + Byte 2 + Byte 3 + + +   +   + Bit + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 +   + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 +   + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 +   + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 + + + + + V4L2_PIX_FMT_RGB444 + 'R444' + + g3 + g2 + g1 + g0 + b3 + b2 + b1 + b0 + + a3 + a2 + a1 + a0 + r3 + r2 + r1 + r0 + + + V4L2_PIX_FMT_RGB555 + 'RGBO' + + g2 + g1 + g0 + b4 + b3 + b2 + b1 + b0 + + a + r4 + r3 + r2 + r1 + r0 + g4 + g3 + + + V4L2_PIX_FMT_BGR32 + 'BGR4' + + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + + a7 + a6 + a5 + a4 + a3 + a2 + a1 + a0 + + + V4L2_PIX_FMT_RGB32 + 'RGB4' + + a7 + a6 + a5 + a4 + a3 + a2 + a1 + a0 + + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + + +
+ A test utility to determine which RGB formats a driver actually supports is available from the LinuxTV v4l-dvb repository. See &v4l-dvb; for access instructions. diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 1477abe..a498d8b 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -294,7 +294,11 @@ struct v4l2_pix_format { /* RGB formats */ #define V4L2_PIX_FMT_RGB332 v4l2_fourcc('R', 'G', 'B', '1') /* 8 RGB-3-3-2 */ #define V4L2_PIX_FMT_RGB444 v4l2_fourcc('R', '4', '4', '4') /* 16 xxxxrrrr ggggbbbb */ +#define V4L2_PIX_FMT_ARGB444 v4l2_fourcc('A', 'R', '1', '2') /* 16 aaaarrrr ggggbbbb */ +#define V4L2_PIX_FMT_XRGB444 v4l2_fourcc('X', 'R', '1', '2') /* 16 xxxxrrrr ggggbbbb */ #define V4L2_PIX_FMT_RGB555 v4l2_fourcc('R', 'G', 'B', 'O') /* 16 RGB-5-5-5 */ +#define V4L2_PIX_FMT_ARGB555 v4l2_fourcc('A', 'R', '1', '5') /* 16 ARGB-1-5-5-5 */ +#define V4L2_PIX_FMT_XRGB555 v4l2_fourcc('X', 'R', '1', '5') /* 16 XRGB-1-5-5-5 */ #define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R', 'G', 'B', 'P') /* 16 RGB-5-6-5 */ #define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16 RGB-5-5-5 BE */ #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */ @@ -302,7 +306,11 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */ #define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */ #define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B', 'G', 'R', '4') /* 32 BGR-8-8-8-8 */ +#define V4L2_PIX_FMT_ABGR32 v4l2_fourcc('A', 'R', '2', '4') /* 32 BGRA-8-8-8-8 */ +#define V4L2_PIX_FMT_XBGR32 v4l2_fourcc('X', 'R', '2', '4') /* 32 BGRX-8-8-8-8 */ #define V4L2_PIX_FMT_RGB32 v4l2_fourcc('R', 'G', 'B', '4') /* 32 RGB-8-8-8-8 */ +#define V4L2_PIX_FMT_ARGB32 v4l2_fourcc('B', 'A', '2', '4') /* 32 ARGB-8-8-8-8 */ +#define V4L2_PIX_FMT_XRGB32 v4l2_fourcc('B', 'X', '2', '4') /* 32 XRGB-8-8-8-8 */ /* Grey formats */ #define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */ -- cgit v0.10.2 From b04ef7c075e8806fa18bc849e32d6a3163150cec Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 May 2014 19:16:01 -0300 Subject: [media] DocBook: media: Document ALPHA_COMPONENT control usage on output devices Extend the V4L2_CID_ALPHA_COMPONENT control for use on output devices, to set the alpha component value when the output format doesn't have an alpha channel. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index cc0087e..74f5755 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -408,14 +408,17 @@ to work.
V4L2_CID_ALPHA_COMPONENT integer - Sets the alpha color component on the capture device or on - the capture buffer queue of a mem-to-mem device. When a mem-to-mem - device produces frame format that includes an alpha component + Sets the alpha color component. When a capture device (or + capture queue of a mem-to-mem device) produces a frame format that + includes an alpha component (e.g. packed RGB image formats) - and the alpha value is not defined by the mem-to-mem input data - this control lets you select the alpha component value of all - pixels. It is applicable to any pixel format that contains an alpha - component. + and the alpha value is not defined by the device or the mem-to-mem + input data this control lets you select the alpha component value of + all pixels. When an output device (or output queue of a mem-to-mem + device) consumes a frame format that doesn't include an alpha + component and the device supports alpha channel processing this + control lets you set the alpha component value of all pixels for + further processing in the device. -- cgit v0.10.2 From d52e23813672c3c72f92e7b39c7408d4b9a40a96 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 May 2014 09:41:05 -0300 Subject: [media] v4l: Support extending the v4l2_pix_format structure The v4l2_pix_format structure has no reserved field. It is embedded in the v4l2_framebuffer structure which has no reserved fields either, and in the v4l2_format structure which has reserved fields that were not previously required to be zeroed out by applications. To allow extending v4l2_pix_format, inline it in the v4l2_framebuffer structure, and use the priv field as a magic value to indicate that the application has set all v4l2_pix_format extended fields and zeroed all reserved fields following the v4l2_pix_format field in the v4l2_format structure. The availability of this API extension is reported to userspace through the new V4L2_CAP_EXT_PIX_FORMAT capability flag. Just checking that the priv field is still set to the magic value at [GS]_FMT return wouldn't be enough, as older kernels don't zero the priv field on return. To simplify the internal API towards drivers zero the extended fields and set the priv field to the magic value for applications not aware of the extensions. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile index 639e748..df2962d 100644 --- a/Documentation/DocBook/media/Makefile +++ b/Documentation/DocBook/media/Makefile @@ -174,7 +174,7 @@ FILENAME = \ DOCUMENTED = \ -e "s/\(enum *\)v4l2_mpeg_cx2341x_video_\([a-z]*_spatial_filter_type\)/\1v4l2_mpeg_cx2341x_video_\2<\/link>/g" \ -e "s/\(\(enum\|struct\) *\)\(v4l2_[a-zA-Z0-9_]*\)/\1\3<\/link>/g" \ - -e "s/\(V4L2_PIX_FMT_[A-Z0-9_]\+\) /\1<\/link> /g" \ + -e "s/\(V4L2_PIX_FMT_[A-Z0-9_]\+\)\(\s\+v4l2_fourcc\)/\1<\/link>\2/g" \ -e ":a;s/\(linkend=\".*\)_\(.*\">\)/\1-\2/;ta" \ -e "s/v4l2\-mpeg\-vbi\-ITV0/v4l2-mpeg-vbi-itv0-1/g" diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index 91dcbc8..bb36b38 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -112,9 +112,28 @@ see . __u32 priv - Reserved for custom (driver defined) additional -information about formats. When not used drivers and applications must -set this field to zero. + This field indicates whether the remaining fields of the +v4l2_pix_format structure, also called the extended +fields, are valid. When set to V4L2_PIX_FMT_PRIV_MAGIC, it +indicates that the extended fields have been correctly initialized. When set to +any other value it indicates that the extended fields contain undefined values. + +Applications that wish to use the pixel format extended fields must first +ensure that the feature is supported by querying the device for the +V4L2_CAP_EXT_PIX_FORMAT +capability. If the capability isn't set the pixel format extended fields are not +supported and using the extended fields will lead to undefined results. +To use the extended fields, applications must set the +priv field to +V4L2_PIX_FMT_PRIV_MAGIC, initialize all the extended fields +and zero the unused bytes of the v4l2_format +raw_data field. +When the priv field isn't set to +V4L2_PIX_FMT_PRIV_MAGIC drivers must act as if all the +extended fields were set to zero. On return drivers must set the +priv field to +V4L2_PIX_FMT_PRIV_MAGIC and all the extended fields to +applicable values. diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index b445161..d0a48be 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -152,6 +152,14 @@ structs, ioctls) must be noted in more detail in the history chapter applications. --> + 3.16 + 2014-05-27 + lp + Extended &v4l2-pix-format;. + + + + 3.15 2014-02-03 hv, ap diff --git a/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml b/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml index 7c63815..2046073 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml @@ -152,13 +152,10 @@ a valid base address, so applications can find the corresponding Linux framebuffer device (see ). - &v4l2-pix-format; + struct fmt - Layout of the frame buffer. The -v4l2_pix_format structure is defined in , for clarification the fields and acceptable values - are listed below: + Layout of the frame buffer. @@ -276,9 +273,8 @@ see . __u32 priv - Reserved for additional information about custom -(driver defined) formats. When not used drivers and applications must -set this field to zero. + Reserved. Drivers and applications must set this field to +zero. diff --git a/Documentation/DocBook/media/v4l/vidioc-querycap.xml b/Documentation/DocBook/media/v4l/vidioc-querycap.xml index 370d49d..d0c5e60 100644 --- a/Documentation/DocBook/media/v4l/vidioc-querycap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-querycap.xml @@ -302,6 +302,12 @@ modulator programming see SDR Capture interface.
+ V4L2_CAP_EXT_PIX_FORMAT + 0x00200000 + The device supports the &v4l2-pix-format; extended +fields. + + V4L2_CAP_READWRITE 0x01000000 The device supports the sizeimage = pix->width * pix->height; /* Just a guess */ pix->colorspace = V4L2_COLORSPACE_SRGB; - pix->priv = 0; return 0; } @@ -785,7 +784,6 @@ static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format pix->sizeimage = pix->width * pix->height; /* Just a guess */ pix->colorspace = V4L2_COLORSPACE_SRGB; - pix->priv = 0; return 0; } diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index fefb2cd..6f2b590 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -156,7 +156,6 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh, pixfmt->height = cx->cxhdl.height; pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; pixfmt->field = V4L2_FIELD_INTERLACED; - pixfmt->priv = 0; if (id->type == CX18_ENC_STREAM_TYPE_YUV) { pixfmt->pixelformat = s->pixelformat; pixfmt->sizeimage = s->vb_bytes_per_frame; diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 8d2f1ab..3a419f1 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -576,7 +576,6 @@ static int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = (chan->width * chan->fmt->depth) >> 3; f->fmt.pix.sizeimage = chan->height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } @@ -615,7 +614,6 @@ static int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } @@ -867,7 +865,6 @@ static int cx25821_vidioc_try_fmt_vid_out(struct file *file, void *priv, f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index b3667a0..3e0cb77 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -351,7 +351,6 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f pixfmt->height = itv->cxhdl.height; pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; pixfmt->field = V4L2_FIELD_INTERLACED; - pixfmt->priv = 0; if (id->type == IVTV_ENC_STREAM_TYPE_YUV) { pixfmt->pixelformat = V4L2_PIX_FMT_HM12; /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */ @@ -418,7 +417,6 @@ static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *f pixfmt->height = itv->main_rect.height; pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; pixfmt->field = V4L2_FIELD_INTERLACED; - pixfmt->priv = 0; if (id->type == IVTV_DEC_STREAM_TYPE_YUV) { switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) { case IVTV_YUV_MODE_INTERLACED: @@ -1384,7 +1382,6 @@ static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) fb->fmt.bytesperline = fb->fmt.width; fb->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; fb->fmt.field = V4L2_FIELD_INTERLACED; - fb->fmt.priv = 0; if (fb->fmt.pixelformat != V4L2_PIX_FMT_PAL8) fb->fmt.bytesperline *= 2; if (fb->fmt.pixelformat == V4L2_PIX_FMT_RGB32 || diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 1a77f8d..aeae547 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -1166,7 +1166,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; return 0; } @@ -1232,7 +1231,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 5526ed5..ef39261 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -130,7 +130,6 @@ static int empress_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; f->fmt.pix.bytesperline = 0; - f->fmt.pix.priv = 0; return 0; } @@ -148,7 +147,6 @@ static int empress_s_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; f->fmt.pix.bytesperline = 0; - f->fmt.pix.priv = 0; return 0; } @@ -166,7 +164,6 @@ static int empress_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; f->fmt.pix.bytesperline = 0; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index d375999..0cfa2ca 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1235,7 +1235,6 @@ static int saa7134_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } @@ -1315,7 +1314,6 @@ static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index f2d8c70..365bd21 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -640,7 +640,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.width * 2 * f->fmt.pix.height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index b178379..5488660 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -613,8 +613,6 @@ static int coda_try_fmt(struct coda_ctx *ctx, struct coda_codec *codec, BUG(); } - f->fmt.pix.priv = 0; - return 0; } diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 877b46e..0bd6dcb 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -648,7 +648,6 @@ static int vpif_try_fmt_vid_out(struct file *file, void *priv, pixfmt->width = common->fmt.fmt.pix.width; pixfmt->height = common->fmt.fmt.pix.height; pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height * 2; - pixfmt->priv = 0; return 0; } diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 0714070..c1b03cf 100644 --- a/drivers/media/platform/mem2mem_testdev.c +++ b/drivers/media/platform/mem2mem_testdev.c @@ -532,7 +532,6 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt) f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 9a726ea..2d177fa 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -165,7 +165,6 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix) pix->pixelformat = omap_formats[ifmt].pixelformat; pix->field = V4L2_FIELD_ANY; - pix->priv = 0; switch (pix->pixelformat) { case V4L2_PIX_FMT_YUYV: @@ -1896,7 +1895,6 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) pix->field = V4L2_FIELD_ANY; pix->bytesperline = pix->width * 2; pix->sizeimage = pix->bytesperline * pix->height; - pix->priv = 0; pix->colorspace = V4L2_COLORSPACE_JPEG; vout->bpp = RGB565_BPP; diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 744e43b..8dc279d 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -425,7 +425,6 @@ static int sh_veu_g_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) pix->bytesperline = vfmt->bytesperline; pix->sizeimage = vfmt->bytesperline * pix->height * vfmt->fmt->depth / vfmt->fmt->ydepth; - pix->priv = 0; dev_dbg(veu->dev, "%s(): type: %d, size %u @ %ux%u, fmt %x\n", __func__, f->type, pix->sizeimage, pix->width, pix->height, pix->pixelformat); @@ -473,7 +472,6 @@ static int sh_veu_try_fmt(struct v4l2_format *f, const struct sh_veu_format *fmt pix->pixelformat = fmt->fourcc; pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); - pix->priv = 0; pr_debug("%s(): type: %d, size %u\n", __func__, f->type, pix->sizeimage); diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index 470d353..91d44ea1 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -3147,7 +3147,6 @@ static int vino_try_fmt_vid_cap(struct file *file, void *__fh, pf->colorspace = vino_data_formats[tempvcs.data_format].colorspace; - pf->priv = 0; return 0; } @@ -3175,8 +3174,6 @@ static int vino_g_fmt_vid_cap(struct file *file, void *__fh, pf->colorspace = vino_data_formats[vcs->data_format].colorspace; - pf->priv = 0; - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); return 0; } @@ -3219,8 +3216,6 @@ static int vino_s_fmt_vid_cap(struct file *file, void *__fh, pf->colorspace = vino_data_formats[vcs->data_format].colorspace; - pf->priv = 0; - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); return 0; } diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 7542b5d..8033371 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -1014,7 +1014,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; else f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index f0400e2..459bb0e 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1563,7 +1563,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = dev->ts1.width; f->fmt.pix.height = dev->ts1.height; f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.priv = 0; dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d\n", dev->ts1.width, dev->ts1.height); dprintk(3, "exit vidioc_g_fmt_vid_cap()\n"); @@ -1582,7 +1581,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.sizeimage = mpeglines * mpeglinesize; f->fmt.pix.field = V4L2_FIELD_INTERLACED; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d\n", dev->ts1.width, dev->ts1.height); dprintk(3, "exit vidioc_try_fmt_vid_cap()\n"); diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index ae31ca2..3b3ada6 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -885,7 +885,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.priv = 0; return 0; } @@ -930,7 +929,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 42d2232..e8cf23c 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1109,8 +1109,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct gspca_dev *gspca_dev = video_drvdata(file); fmt->fmt.pix = gspca_dev->pixfmt; - /* some drivers use priv internally, zero it before giving it to - userspace */ + /* some drivers use priv internally, zero it before giving it back to + the core */ fmt->fmt.pix.priv = 0; return 0; } @@ -1146,8 +1146,8 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, fmt->fmt.pix.height = h; gspca_dev->sd_desc->try_fmt(gspca_dev, fmt); } - /* some drivers use priv internally, zero it before giving it to - userspace */ + /* some drivers use priv internally, zero it before giving it back to + the core */ fmt->fmt.pix.priv = 0; return mode; /* used when s_fmt */ } diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index dca4b65..4b7653f 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -1022,7 +1022,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *_fh, f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = dev->bulk_in_size; f->fmt.pix.bytesperline = 0; - f->fmt.pix.priv = 0; if (f->fmt.pix.width == 720) { /* SDTV formats */ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index d76860b..3588dc3 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -923,7 +923,6 @@ static int stk_vidioc_g_fmt_vid_cap(struct file *filp, pix_format->bytesperline = 2 * pix_format->width; pix_format->sizeimage = pix_format->bytesperline * pix_format->height; - pix_format->priv = 0; return 0; } @@ -967,7 +966,6 @@ static int stk_try_fmt_vid_cap(struct file *filp, fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width; fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline * fmtd->fmt.pix.height; - fmtd->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c index 8df668d..8cd7f02 100644 --- a/drivers/media/usb/tlg2300/pd-video.c +++ b/drivers/media/usb/tlg2300/pd-video.c @@ -1321,7 +1321,6 @@ static void init_video_context(struct running_context *context) .bytesperline = 720 * 2, .sizeimage = 720 * 576 * 2, .colorspace = V4L2_COLORSPACE_SMPTE170M, - .priv = 0 }; } diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 9bde064..793577f 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -918,7 +918,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, (f->fmt.pix.width * fh->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.priv = 0; return 0; } @@ -959,7 +958,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width &= ~0x01; f->fmt.pix.field = field; - f->fmt.pix.priv = 0; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 3b80579..5c00627 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -806,7 +806,6 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), f->fmt.pix.field); @@ -829,7 +828,6 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; return 0; } @@ -866,7 +864,6 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; cam->vb_vidq.field = f->fmt.pix.field; if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 7e2411c..cca6c2f 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -540,7 +540,16 @@ struct v4l2_framebuffer32 { __u32 capability; __u32 flags; compat_caddr_t base; - struct v4l2_pix_format fmt; + struct { + __u32 width; + __u32 height; + __u32 pixelformat; + __u32 field; + __u32 bytesperline; + __u32 sizeimage; + __u32 colorspace; + __u32 priv; + } fmt; }; static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up) @@ -550,10 +559,10 @@ static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) || get_user(tmp, &up->base) || get_user(kp->capability, &up->capability) || - get_user(kp->flags, &up->flags)) + get_user(kp->flags, &up->flags) || + copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt))) return -EFAULT; kp->base = compat_ptr(tmp); - get_v4l2_pix_format(&kp->fmt, &up->fmt); return 0; } @@ -564,9 +573,9 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) || put_user(tmp, &up->base) || put_user(kp->capability, &up->capability) || - put_user(kp->flags, &up->flags)) + put_user(kp->flags, &up->flags) || + copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt))) return -EFAULT; - put_v4l2_pix_format(&kp->fmt, &up->fmt); return 0; } diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 96bc117..2e63000 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -973,13 +973,48 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) return -EINVAL; } +static void v4l_sanitize_format(struct v4l2_format *fmt) +{ + unsigned int offset; + + /* + * The v4l2_pix_format structure has been extended with fields that were + * not previously required to be set to zero by applications. The priv + * field, when set to a magic value, indicates the the extended fields + * are valid. Otherwise they will contain undefined values. To simplify + * the API towards drivers zero the extended fields and set the priv + * field to the magic value when the extended pixel format structure + * isn't used by applications. + */ + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return; + + if (fmt->fmt.pix.priv == V4L2_PIX_FMT_PRIV_MAGIC) + return; + + fmt->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + + offset = offsetof(struct v4l2_pix_format, priv) + + sizeof(fmt->fmt.pix.priv); + memset(((void *)&fmt->fmt.pix) + offset, 0, + sizeof(fmt->fmt.pix) - offset); +} + static int v4l_querycap(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_capability *cap = (struct v4l2_capability *)arg; + int ret; cap->version = LINUX_VERSION_CODE; - return ops->vidioc_querycap(file, fh, cap); + + ret = ops->vidioc_querycap(file, fh, cap); + + cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT; + + return ret; } static int v4l_s_input(const struct v4l2_ioctl_ops *ops, @@ -1103,12 +1138,17 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + int ret; + + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap)) break; - return ops->vidioc_g_fmt_vid_cap(file, fh, arg); + ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg); + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap_mplane)) break; @@ -1128,7 +1168,9 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out)) break; - return ops->vidioc_g_fmt_vid_out(file, fh, arg); + ret = ops->vidioc_g_fmt_vid_out(file, fh, arg); + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_mplane)) break; @@ -1163,6 +1205,8 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + v4l_sanitize_format(p); + switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap)) @@ -1233,6 +1277,8 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + v4l_sanitize_format(p); + switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap)) @@ -1516,7 +1562,18 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, struct v4l2_create_buffers *create = arg; int ret = check_fmt(file, create->format.type); - return ret ? ret : ops->vidioc_create_bufs(file, fh, create); + if (ret) + return ret; + + v4l_sanitize_format(&create->format); + + ret = ops->vidioc_create_bufs(file, fh, create); + + if (create->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE || + create->format.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + create->format.fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + + return ret; } static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops, diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index a498d8b..eb3bdd3 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -268,6 +268,7 @@ struct v4l2_capability { #define V4L2_CAP_MODULATOR 0x00080000 /* has a modulator */ #define V4L2_CAP_SDR_CAPTURE 0x00100000 /* Is a SDR capture device */ +#define V4L2_CAP_EXT_PIX_FORMAT 0x00200000 /* Supports the extended pixel format */ #define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */ #define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */ @@ -448,6 +449,9 @@ struct v4l2_pix_format { #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ +/* priv field value to indicates that subsequent fields are valid. */ +#define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe + /* * F O R M A T E N U M E R A T I O N */ @@ -752,7 +756,16 @@ struct v4l2_framebuffer { /* FIXME: in theory we should pass something like PCI device + memory * region + offset instead of some physical address */ void *base; - struct v4l2_pix_format fmt; + struct { + __u32 width; + __u32 height; + __u32 pixelformat; + __u32 field; /* enum v4l2_field */ + __u32 bytesperline; /* for padding, zero if unused */ + __u32 sizeimage; + __u32 colorspace; /* enum v4l2_colorspace */ + __u32 priv; /* reserved field, set to 0 */ + } fmt; }; /* Flags for the 'capability' field. Read only */ #define V4L2_FBUF_CAP_EXTERNOVERLAY 0x0001 -- cgit v0.10.2 From c96fd46afb34a554406bce9784126b96ad09091e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 May 2014 10:12:43 -0300 Subject: [media] v4l: Add premultiplied alpha flag for pixel formats When set, the new V4L2_PIX_FMT_FLAG_PREMUL_ALPHA flag indicates that the pixel values are premultiplied by the alpha channel value. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index bb36b38..87ce7f3 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -135,6 +135,12 @@ extended fields were set to zero. On return drivers must set the V4L2_PIX_FMT_PRIV_MAGIC and all the extended fields to applicable values.
+ + __u32 + flags + Flags set by the application or driver, see . + @@ -220,9 +226,15 @@ codes can be used. and the number of valid entries in the plane_fmt array. + + __u8 + flags + Flags set by the application or driver, see . + __u8 - reserved[11] + reserved[10] Reserved for future extensions. Should be zeroed by the application. @@ -1079,4 +1091,21 @@ concatenated to form the JPEG stream. + + + Format Flags + + &cs-def; + + + V4L2_PIX_FMT_FLAG_PREMUL_ALPHA + 0x00000001 + The color values are premultiplied by the alpha channel +value. For example, if a light blue pixel with 50% transparency was described by +RGBA values (128, 192, 255, 128), the same pixel described with premultiplied +colors would be described by RGBA values (64, 96, 128, 128) + + + +
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index d0a48be..f2f81f0 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -155,7 +155,7 @@ applications. --> 3.16 2014-05-27 lp - Extended &v4l2-pix-format;. + Extended &v4l2-pix-format;. Added format flags. diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 2e63000..e0bafda 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -256,7 +256,8 @@ static void v4l_print_format(const void *arg, bool write_only) pix = &p->fmt.pix; pr_cont(", width=%u, height=%u, " "pixelformat=%c%c%c%c, field=%s, " - "bytesperline=%u, sizeimage=%u, colorspace=%d\n", + "bytesperline=%u, sizeimage=%u, colorspace=%d, " + "flags %u\n", pix->width, pix->height, (pix->pixelformat & 0xff), (pix->pixelformat >> 8) & 0xff, @@ -264,7 +265,7 @@ static void v4l_print_format(const void *arg, bool write_only) (pix->pixelformat >> 24) & 0xff, prt_names(pix->field, v4l2_field_names), pix->bytesperline, pix->sizeimage, - pix->colorspace); + pix->colorspace, pix->flags); break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index eb3bdd3..b73e8cd 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -288,6 +288,7 @@ struct v4l2_pix_format { __u32 sizeimage; __u32 colorspace; /* enum v4l2_colorspace */ __u32 priv; /* private data, depends on pixelformat */ + __u32 flags; /* format flags (V4L2_PIX_FMT_FLAG_*) */ }; /* Pixel format FOURCC depth Description */ @@ -452,6 +453,9 @@ struct v4l2_pix_format { /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe +/* Flags */ +#define V4L2_PIX_FMT_FLAG_PREMUL_ALPHA 0x00000001 + /* * F O R M A T E N U M E R A T I O N */ @@ -1754,6 +1758,7 @@ struct v4l2_plane_pix_format { * @colorspace: enum v4l2_colorspace; supplemental to pixelformat * @plane_fmt: per-plane information * @num_planes: number of planes for this format + * @flags: format flags (V4L2_PIX_FMT_FLAG_*) */ struct v4l2_pix_format_mplane { __u32 width; @@ -1764,7 +1769,8 @@ struct v4l2_pix_format_mplane { struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES]; __u8 num_planes; - __u8 reserved[11]; + __u8 flags; + __u8 reserved[10]; } __attribute__ ((packed)); /** -- cgit v0.10.2 From bd994ddb2a12a3ff48cd549ec82cdceaea9614df Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Jun 2014 18:00:22 -0300 Subject: [media] v4l: vb2: Fix stream start and buffer completion race videobuf2 stores the driver streaming state internally in the queue in the start_streaming_called variable. The state is set right after the driver start_stream operation returns, and checked in the vb2_buffer_done() function, typically called from the frame completion interrupt handler. A race condition exists if the hardware finishes processing the first frame before the start_stream operation returns. Fix this by setting start_streaming_called to 1 before calling the start_stream operation, and resetting it to 0 if the operation fails. Cc: stable@vger.kernel.org # for v3.15 and up Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 7c4489c..1d67e95 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1750,12 +1750,14 @@ static int vb2_start_streaming(struct vb2_queue *q) __enqueue_in_driver(vb); /* Tell the driver to start streaming */ + q->start_streaming_called = 1; ret = call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count)); - q->start_streaming_called = ret == 0; if (!ret) return 0; + q->start_streaming_called = 0; + dprintk(1, "driver refused to start streaming\n"); if (WARN_ON(atomic_read(&q->owned_by_drv_count))) { unsigned i; -- cgit v0.10.2 From aaa48cbb098e13cf9c22066b8c65958a7243fb95 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Jun 2014 18:40:49 -0300 Subject: [media] v4l: vsp1: Fix routing cleanup when stopping the stream Commit d9b45ed3d8b75e8cf38c8cd1563c29217eecba27 ("v4l: vsp1: Support multi-input entities") reworked pipeline routing configuration and introduced a bug by writing to the entities routing registers without first checking whether the entity had a routing register. This results in overwriting the value at offset 0 of the device register space when stopping the stream. Fix this by skipping routing register write for entities without a routing register. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 677e3aa..c717e28 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -479,7 +479,7 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) ret = ret == 0 ? -ETIMEDOUT : 0; list_for_each_entry(entity, &pipe->entities, list_pipe) { - if (entity->route) + if (entity->route && entity->route->reg) vsp1_write(entity->vsp1, entity->route->reg, VI6_DPR_NODE_UNUSED); -- cgit v0.10.2 From 9df04e9d9ac6982d16a8a042c8274410ed29fb0e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Jun 2014 16:57:22 -0300 Subject: [media] v4l: vsp1: Release buffers at stream stop videobuf2 expects no buffer to be owned by the driver when the stop_stream queue operation returns. As the vsp1 driver fails to do so, a warning is generated at stream top time. Fix this by releasing all buffers queued on the IRQ queue in the stop_stream operation handler and marking them as erroneous. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index c717e28..9bb156c 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -742,6 +742,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + struct vsp1_video_buffer *buffer; unsigned long flags; int ret; @@ -759,6 +760,8 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) /* Remove all buffers from the IRQ queue. */ spin_lock_irqsave(&video->irqlock, flags); + list_for_each_entry(buffer, &video->irqqueue, queue) + vb2_buffer_done(&buffer->buf, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&video->irqqueue); spin_unlock_irqrestore(&video->irqlock, flags); } -- cgit v0.10.2 From 9b3e6e2abd7568f9458e927fc4e5eca1b3cde521 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Jun 2014 17:15:02 -0300 Subject: [media] v4l: vsp1: Fix pipeline stop timeout If the pipeline was already stopped when stopping the stream, no frame end interrupt will be generated and the driver will time out waiting for the pipeline to stop. Fix this by setting the pipeline state to STOPPED when the pipeline is idle waiting for frames to process, and to STOPPING at stream stop time only when the pipeline is currently RUNNING. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 9bb156c..a60332e 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -471,7 +471,8 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) int ret; spin_lock_irqsave(&pipe->irqlock, flags); - pipe->state = VSP1_PIPELINE_STOPPING; + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; spin_unlock_irqrestore(&pipe->irqlock, flags); ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED, @@ -576,6 +577,7 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) { + enum vsp1_pipeline_state state; unsigned long flags; unsigned int i; @@ -591,11 +593,13 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) spin_lock_irqsave(&pipe->irqlock, flags); + state = pipe->state; + pipe->state = VSP1_PIPELINE_STOPPED; + /* If a stop has been requested, mark the pipeline as stopped and * return. */ - if (pipe->state == VSP1_PIPELINE_STOPPING) { - pipe->state = VSP1_PIPELINE_STOPPED; + if (state == VSP1_PIPELINE_STOPPING) { wake_up(&pipe->wq); goto done; } -- cgit v0.10.2 From 7005a81705d032887b9f4f808e6dce8e57b8980a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 May 2014 12:59:39 -0300 Subject: [media] v4l: vsp1: Fix typos Several macros were mistakenly prefixed with VPS1 instead of VSP1. Fix them. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 6ca2cf2..3cfa393 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -36,9 +36,9 @@ struct vsp1_rwpf; struct vsp1_sru; struct vsp1_uds; -#define VPS1_MAX_RPF 5 -#define VPS1_MAX_UDS 3 -#define VPS1_MAX_WPF 4 +#define VSP1_MAX_RPF 5 +#define VSP1_MAX_UDS 3 +#define VSP1_MAX_WPF 4 struct vsp1_device { struct device *dev; @@ -55,10 +55,10 @@ struct vsp1_device { struct vsp1_hsit *hst; struct vsp1_lif *lif; struct vsp1_lut *lut; - struct vsp1_rwpf *rpf[VPS1_MAX_RPF]; + struct vsp1_rwpf *rpf[VSP1_MAX_RPF]; struct vsp1_sru *sru; - struct vsp1_uds *uds[VPS1_MAX_UDS]; - struct vsp1_rwpf *wpf[VPS1_MAX_WPF]; + struct vsp1_uds *uds[VSP1_MAX_UDS]; + struct vsp1_rwpf *wpf[VSP1_MAX_WPF]; struct list_head entities; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index c69ee06..0c5e74c 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -440,19 +440,19 @@ static int vsp1_validate_platform_data(struct platform_device *pdev, return -EINVAL; } - if (pdata->rpf_count <= 0 || pdata->rpf_count > VPS1_MAX_RPF) { + if (pdata->rpf_count <= 0 || pdata->rpf_count > VSP1_MAX_RPF) { dev_err(&pdev->dev, "invalid number of RPF (%u)\n", pdata->rpf_count); return -EINVAL; } - if (pdata->uds_count <= 0 || pdata->uds_count > VPS1_MAX_UDS) { + if (pdata->uds_count <= 0 || pdata->uds_count > VSP1_MAX_UDS) { dev_err(&pdev->dev, "invalid number of UDS (%u)\n", pdata->uds_count); return -EINVAL; } - if (pdata->wpf_count <= 0 || pdata->wpf_count > VPS1_MAX_WPF) { + if (pdata->wpf_count <= 0 || pdata->wpf_count > VSP1_MAX_WPF) { dev_err(&pdev->dev, "invalid number of WPF (%u)\n", pdata->wpf_count); return -EINVAL; diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index 7284320..cb5d9ef 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -73,7 +73,7 @@ struct vsp1_pipeline { unsigned int num_video; unsigned int num_inputs; - struct vsp1_rwpf *inputs[VPS1_MAX_RPF]; + struct vsp1_rwpf *inputs[VSP1_MAX_RPF]; struct vsp1_rwpf *output; struct vsp1_entity *bru; struct vsp1_entity *lif; -- cgit v0.10.2 From 1499be67a545fb6f41acb5614b8e4732147cec50 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 28 May 2014 12:49:13 -0300 Subject: [media] v4l: vsp1: Cleanup video nodes at removal time Video nodes created and initialized in the RPF and WPF init code paths are never unregistered, and the related resources (videobuf alloc context and media entity) never released. Fix this by storing a pointer to the vsp1_video object in vsp1_entity and calling vsp1_video_cleanup() from vsp1_entity_destroy(). This also allows simplifying the init error code paths. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 4416783..ceac0d7 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -20,6 +20,7 @@ #include "vsp1.h" #include "vsp1_entity.h" +#include "vsp1_video.h" /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations @@ -185,6 +186,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, void vsp1_entity_destroy(struct vsp1_entity *entity) { + if (entity->video) + vsp1_video_cleanup(entity->video); if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); media_entity_cleanup(&entity->subdev.entity); diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 7afbd8a..f0257f6 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -18,6 +18,7 @@ #include struct vsp1_device; +struct vsp1_video; enum vsp1_entity_type { VSP1_ENTITY_BRU, @@ -68,6 +69,8 @@ struct vsp1_entity { struct v4l2_subdev subdev; struct v4l2_mbus_framefmt *formats; + + struct vsp1_video *video; }; static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index c3d98642..9b3fc70 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -205,7 +205,9 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) ret = vsp1_video_init(video, &rpf->entity); if (ret < 0) - goto error_video; + goto error; + + rpf->entity.video = video; /* Connect the video device to the RPF. */ ret = media_entity_create_link(&rpf->video.video.entity, 0, @@ -214,13 +216,11 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); if (ret < 0) - goto error_link; + goto error; return rpf; -error_link: - vsp1_video_cleanup(video); -error_video: - media_entity_cleanup(&rpf->entity.subdev.entity); +error: + vsp1_entity_destroy(&rpf->entity); return ERR_PTR(ret); } diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 1294340..36c4793 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -216,7 +216,9 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) ret = vsp1_video_init(video, &wpf->entity); if (ret < 0) - goto error_video; + goto error; + + wpf->entity.video = video; /* Connect the video device to the WPF. All connections are immutable * except for the WPF0 source link if a LIF is present. @@ -229,15 +231,13 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) RWPF_PAD_SOURCE, &wpf->video.video.entity, 0, flags); if (ret < 0) - goto error_link; + goto error; wpf->entity.sink = &wpf->video.video.entity; return wpf; -error_link: - vsp1_video_cleanup(video); -error_video: - media_entity_cleanup(&wpf->entity.subdev.entity); +error: + vsp1_entity_destroy(&wpf->entity); return ERR_PTR(ret); } -- cgit v0.10.2 From 4c16d6a079a4c9a64d664cc9c30ebae5f0bd0c64 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 31 May 2014 08:50:32 -0300 Subject: [media] v4l: vsp1: Propagate vsp1_device_get errors to the callers Modify the vsp1_device_get() function to return an error code instead of a pointer to the VSP1 device, and use the return value in the callers. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 3cfa393..1246719 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -66,7 +66,7 @@ struct vsp1_device { struct media_device media_dev; }; -struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1); +int vsp1_device_get(struct vsp1_device *vsp1); void vsp1_device_put(struct vsp1_device *vsp1); static inline u32 vsp1_read(struct vsp1_device *vsp1, u32 reg) diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 0c5e74c..3e6601b 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -345,36 +345,32 @@ static int vsp1_device_init(struct vsp1_device *vsp1) * Increment the VSP1 reference count and initialize the device if the first * reference is taken. * - * Return a pointer to the VSP1 device or NULL if an error occurred. + * Return 0 on success or a negative error code otherwise. */ -struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1) +int vsp1_device_get(struct vsp1_device *vsp1) { - struct vsp1_device *__vsp1 = vsp1; - int ret; + int ret = 0; mutex_lock(&vsp1->lock); if (vsp1->ref_count > 0) goto done; ret = clk_prepare_enable(vsp1->clock); - if (ret < 0) { - __vsp1 = NULL; + if (ret < 0) goto done; - } ret = vsp1_device_init(vsp1); if (ret < 0) { clk_disable_unprepare(vsp1->clock); - __vsp1 = NULL; goto done; } done: - if (__vsp1) + if (!ret) vsp1->ref_count++; mutex_unlock(&vsp1->lock); - return __vsp1; + return ret; } /* diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index a60332e..885ec01 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -955,8 +955,8 @@ static int vsp1_video_open(struct file *file) file->private_data = vfh; - if (!vsp1_device_get(video->vsp1)) { - ret = -EBUSY; + ret = vsp1_device_get(video->vsp1); + if (ret < 0) { v4l2_fh_del(vfh); kfree(vfh); } -- cgit v0.10.2 From 960de2cff49a4e5f45e6a60175f4298cc34dc526 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 31 May 2014 10:40:51 -0300 Subject: [media] v4l: vsp1: Setup control handler automatically at stream on time When setting a control directly on a subdev node the VSP1 driver doesn't guarantee that the device is powered on. This leads to crashes when the control handlers writes to hardware registers. One easy way to fix this is to ensure that the device gets powered on when a subdev node is opened. However, this consumes power unnecessarily, as there's no need to power the device on when setting formats on the pipeline. Furthermore, control handler setup at entity init time suffers from the same problem as the device isn't powered on easier. Fix this by extend the entity base object to setup the control handler automatically when starting the stream. Entities must then skip writing to registers in the set control handler when not streaming, which can be tested with the new vsp1_entity_is_streaming() helper function. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index ceac0d7..79af71d 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -22,6 +22,41 @@ #include "vsp1_entity.h" #include "vsp1_video.h" +bool vsp1_entity_is_streaming(struct vsp1_entity *entity) +{ + bool streaming; + + mutex_lock(&entity->lock); + streaming = entity->streaming; + mutex_unlock(&entity->lock); + + return streaming; +} + +int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) +{ + int ret; + + mutex_lock(&entity->lock); + entity->streaming = streaming; + mutex_unlock(&entity->lock); + + if (!streaming) + return 0; + + if (!entity->subdev.ctrl_handler) + return 0; + + ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler); + if (ret < 0) { + mutex_lock(&entity->lock); + entity->streaming = false; + mutex_unlock(&entity->lock); + } + + return ret; +} + /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ @@ -158,6 +193,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, if (i == ARRAY_SIZE(vsp1_routes)) return -EINVAL; + mutex_init(&entity->lock); + entity->vsp1 = vsp1; entity->source_pad = num_pads - 1; @@ -191,4 +228,6 @@ void vsp1_entity_destroy(struct vsp1_entity *entity) if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); media_entity_cleanup(&entity->subdev.entity); + + mutex_destroy(&entity->lock); } diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index f0257f6..aa20aaa 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -14,6 +14,7 @@ #define __VSP1_ENTITY_H__ #include +#include #include @@ -71,6 +72,9 @@ struct vsp1_entity { struct v4l2_mbus_framefmt *formats; struct vsp1_video *video; + + struct mutex lock; /* Protects the streaming field */ + bool streaming; }; static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) @@ -92,4 +96,7 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity, void vsp1_entity_init_formats(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh); +bool vsp1_entity_is_streaming(struct vsp1_entity *entity); +int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming); + #endif /* __VSP1_ENTITY_H__ */ -- cgit v0.10.2 From a1606102dafe6942905e97a88ceea9a60e1a2c7d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 May 2014 19:00:05 -0300 Subject: [media] v4l: vsp1: sru: Fix the intensity control default value The default value isn't set and defaults to 0, which isn't in the 1-6 min-max range. Fix it by setting the default value to 1. This shoud have been caught when checking the control handler error field at initialization time, but the check was missing. Add it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index aa0e04c..18e127a 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -67,6 +67,7 @@ static const struct v4l2_ctrl_config sru_intensity_control = { .type = V4L2_CTRL_TYPE_INTEGER, .min = 1, .max = 6, + .def = 1, .step = 1, }; @@ -348,8 +349,17 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) /* Initialize the control handler. */ v4l2_ctrl_handler_init(&sru->ctrls, 1); v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL); - v4l2_ctrl_handler_setup(&sru->ctrls); + sru->entity.subdev.ctrl_handler = &sru->ctrls; + if (sru->ctrls.error) { + dev_err(vsp1->dev, "sru: failed to initialize controls\n"); + ret = sru->ctrls.error; + vsp1_entity_destroy(&sru->entity); + return ERR_PTR(ret); + } + + v4l2_ctrl_handler_setup(&sru->ctrls); + return sru; } -- cgit v0.10.2 From 58f896d859ce555dd3684f439f8a65bfda437533 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 31 May 2014 20:30:11 -0300 Subject: [media] v4l: vsp1: sru: Make the intensity controllable during streaming The control value is currently stored in the SRU structure by the control set handler and written to the hardware at stream on time, making control set during streaming ineffective. Fix it by writing to the registers from within the control set handler. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 3e74b44..55f163d 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -336,7 +336,9 @@ */ #define VI6_SRU_CTRL0 0x2200 +#define VI6_SRU_CTRL0_PARAM0_MASK (0x1ff << 16) #define VI6_SRU_CTRL0_PARAM0_SHIFT 16 +#define VI6_SRU_CTRL0_PARAM1_MASK (0x1f << 8) #define VI6_SRU_CTRL0_PARAM1_SHIFT 8 #define VI6_SRU_CTRL0_MODE_UPSCALE (4 << 4) #define VI6_SRU_CTRL0_PARAM2 (1 << 3) diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 18e127a..b7d3c8b 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -42,39 +42,6 @@ static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data) #define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE + 1) -static int sru_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vsp1_sru *sru = - container_of(ctrl->handler, struct vsp1_sru, ctrls); - - switch (ctrl->id) { - case V4L2_CID_VSP1_SRU_INTENSITY: - sru->intensity = ctrl->val; - break; - } - - return 0; -} - -static const struct v4l2_ctrl_ops sru_ctrl_ops = { - .s_ctrl = sru_s_ctrl, -}; - -static const struct v4l2_ctrl_config sru_intensity_control = { - .ops = &sru_ctrl_ops, - .id = V4L2_CID_VSP1_SRU_INTENSITY, - .name = "Intensity", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = 1, - .max = 6, - .def = 1, - .step = 1, -}; - -/* ----------------------------------------------------------------------------- - * V4L2 Subdevice Core Operations - */ - struct vsp1_sru_param { u32 ctrl0; u32 ctrl2; @@ -111,22 +78,66 @@ static const struct vsp1_sru_param vsp1_sru_params[] = { }, }; +static int sru_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_sru *sru = + container_of(ctrl->handler, struct vsp1_sru, ctrls); + const struct vsp1_sru_param *param; + u32 value; + + switch (ctrl->id) { + case V4L2_CID_VSP1_SRU_INTENSITY: + param = &vsp1_sru_params[ctrl->val - 1]; + + value = vsp1_sru_read(sru, VI6_SRU_CTRL0); + value &= ~(VI6_SRU_CTRL0_PARAM0_MASK | + VI6_SRU_CTRL0_PARAM1_MASK); + value |= param->ctrl0; + vsp1_sru_write(sru, VI6_SRU_CTRL0, value); + + vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops sru_ctrl_ops = { + .s_ctrl = sru_s_ctrl, +}; + +static const struct v4l2_ctrl_config sru_intensity_control = { + .ops = &sru_ctrl_ops, + .id = V4L2_CID_VSP1_SRU_INTENSITY, + .name = "Intensity", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 6, + .def = 1, + .step = 1, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + static int sru_s_stream(struct v4l2_subdev *subdev, int enable) { struct vsp1_sru *sru = to_sru(subdev); - const struct vsp1_sru_param *param; struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - bool upscale; u32 ctrl0; + int ret; + + ret = vsp1_entity_set_streaming(&sru->entity, enable); + if (ret < 0) + return ret; if (!enable) return 0; input = &sru->entity.formats[SRU_PAD_SINK]; output = &sru->entity.formats[SRU_PAD_SOURCE]; - upscale = input->width != output->width; - param = &vsp1_sru_params[sru->intensity]; if (input->code == V4L2_MBUS_FMT_ARGB8888_1X32) ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 @@ -134,10 +145,18 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable) else ctrl0 = VI6_SRU_CTRL0_PARAM3; - vsp1_sru_write(sru, VI6_SRU_CTRL0, param->ctrl0 | ctrl0 | - (upscale ? VI6_SRU_CTRL0_MODE_UPSCALE : 0)); + if (input->width != output->width) + ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE; + + /* Take the control handler lock to ensure that the CTRL0 value won't be + * changed behind our back by a set control operation. + */ + mutex_lock(sru->ctrls.lock); + ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0) + & (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK); + mutex_unlock(sru->ctrls.lock); + vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); - vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2); return 0; } @@ -359,7 +378,5 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) return ERR_PTR(ret); } - v4l2_ctrl_handler_setup(&sru->ctrls); - return sru; } diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/vsp1/vsp1_sru.h index 381870b..b6768bf 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.h +++ b/drivers/media/platform/vsp1/vsp1_sru.h @@ -28,7 +28,6 @@ struct vsp1_sru { struct vsp1_entity entity; struct v4l2_ctrl_handler ctrls; - unsigned int intensity; }; static inline struct vsp1_sru *to_sru(struct v4l2_subdev *subdev) -- cgit v0.10.2 From 5aeb01adbea81e858318072097d98fc44578c7be Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 May 2014 20:35:36 -0300 Subject: [media] v4l: vsp1: wpf: Simplify cast to pipeline structure Use the subdev pointer directly to_vsp1_pipeline() macro instead of casting from the subdev to the wpf object and back to the subdev. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 36c4793..591f09c 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -44,9 +44,8 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data) static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) { + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); struct vsp1_rwpf *wpf = to_rwpf(subdev); - struct vsp1_pipeline *pipe = - to_vsp1_pipeline(&wpf->entity.subdev.entity); struct vsp1_device *vsp1 = wpf->entity.vsp1; const struct v4l2_rect *crop = &wpf->crop; unsigned int i; -- cgit v0.10.2 From d6c71e8ceb9fe79aaf9c2f73af5cb2ef93526b0d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 28 May 2014 13:10:33 -0300 Subject: [media] v4l: vsp1: wpf: Clear RPF to WPF association at stream off time The VSP1 stores the video pipelines' input (RPF) to output (WPF) mappings in a WPF register. An RPF must never be associated with multiple WPFs, even if all of those WPFs but one are unused, otherwise the hardware won't function properly. The driver doesn't ensure this correctly as it never clears the mappings. An RPF used with one WPF and later with a different WPF will lead to malfunction, as it will be associated with two WPFs. Clear the mappings at stream off time to fix this. Reported-by: Damian Hobson-Garcia Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 591f09c..d330865 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -54,6 +54,7 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) if (!enable) { vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); + vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, 0); return 0; } -- cgit v0.10.2 From 56bfef3e72cf4a98d0e8f56129673146733c1366 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 26 May 2014 19:57:21 -0300 Subject: [media] v4l: vsp1: Switch to XRGB formats The driver ignores the alpha component on output video nodes and hardcodes the alpha component to 0 on capture video nodes. Make this explicit by exposing XRGB formats. Compatibility with existing userspace applications is handled by selecting the XRGB format corresponding to the requested old RGB format. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 885ec01..3dc7d84 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -51,11 +51,11 @@ static const struct vsp1_format_info vsp1_video_formats[] = { VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, 1, { 8, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_RGB444, V4L2_MBUS_FMT_ARGB8888_1X32, + { V4L2_PIX_FMT_XRGB444, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, 1, { 16, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_RGB555, V4L2_MBUS_FMT_ARGB8888_1X32, + { V4L2_PIX_FMT_XRGB555, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, 1, { 16, 0, 0 }, false, false, 1, 1 }, @@ -71,10 +71,10 @@ static const struct vsp1_format_info vsp1_video_formats[] = { VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, 1, { 24, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_BGR32, V4L2_MBUS_FMT_ARGB8888_1X32, + { V4L2_PIX_FMT_XBGR32, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, 1, { 32, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_RGB32, V4L2_MBUS_FMT_ARGB8888_1X32, + { V4L2_PIX_FMT_XRGB32, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, 1, { 32, 0, 0 }, false, false, 1, 1 }, @@ -181,11 +181,29 @@ static int __vsp1_video_try_format(struct vsp1_video *video, struct v4l2_pix_format_mplane *pix, const struct vsp1_format_info **fmtinfo) { + static const u32 xrgb_formats[][2] = { + { V4L2_PIX_FMT_RGB444, V4L2_PIX_FMT_XRGB444 }, + { V4L2_PIX_FMT_RGB555, V4L2_PIX_FMT_XRGB555 }, + { V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_XBGR32 }, + { V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_XRGB32 }, + }; + const struct vsp1_format_info *info; unsigned int width = pix->width; unsigned int height = pix->height; unsigned int i; + /* Backward compatibility: replace deprecated RGB formats by their XRGB + * equivalent. This selects the format older userspace applications want + * while still exposing the new format. + */ + for (i = 0; i < ARRAY_SIZE(xrgb_formats); ++i) { + if (xrgb_formats[i][0] == pix->pixelformat) { + pix->pixelformat = xrgb_formats[i][1]; + break; + } + } + /* Retrieve format information and select the default format if the * requested format isn't supported. */ -- cgit v0.10.2 From 7a52b6dea8e9559428149fbed0cddd587004006e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 26 May 2014 20:12:53 -0300 Subject: [media] v4l: vsp1: Add alpha channel support to the memory ports Support ARGB formats on the RPF side by reading the alpha component from memory and on the WPF side by writing it to memory. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 9b3fc70..2824f53 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -101,10 +101,12 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) (rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) | (rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT)); - /* Disable alpha, mask and color key. Set the alpha channel to a fixed - * value of 255. + /* Use the alpha channel (extended to 8 bits) when available or a + * hardcoded 255 value otherwise. Disable color keying. */ - vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_ASEL_FIXED); + vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | + (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED + : VI6_RPF_ALPH_SEL_ASEL_FIXED)); vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, 255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0); diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 3dc7d84..4dd4d61 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -50,70 +50,85 @@ static const struct vsp1_format_info vsp1_video_formats[] = { { V4L2_PIX_FMT_RGB332, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 8, 0, 0 }, false, false, 1, 1 }, + 1, { 8, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ARGB444, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, { V4L2_PIX_FMT_XRGB444, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1 }, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_ARGB555, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, { V4L2_PIX_FMT_XRGB555, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1 }, + 1, { 16, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_RGB565, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1 }, + 1, { 16, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_BGR24, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 24, 0, 0 }, false, false, 1, 1 }, + 1, { 24, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_RGB24, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 24, 0, 0 }, false, false, 1, 1 }, + 1, { 24, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ABGR32, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, + 1, { 32, 0, 0 }, false, false, 1, 1, true }, { V4L2_PIX_FMT_XBGR32, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, - 1, { 32, 0, 0 }, false, false, 1, 1 }, + 1, { 32, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ARGB32, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 32, 0, 0 }, false, false, 1, 1, true }, { V4L2_PIX_FMT_XRGB32, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 32, 0, 0 }, false, false, 1, 1 }, + 1, { 32, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_UYVY, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, false, false, 2, 1 }, + 1, { 16, 0, 0 }, false, false, 2, 1, false }, { V4L2_PIX_FMT_VYUY, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, false, true, 2, 1 }, + 1, { 16, 0, 0 }, false, true, 2, 1, false }, { V4L2_PIX_FMT_YUYV, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, true, false, 2, 1 }, + 1, { 16, 0, 0 }, true, false, 2, 1, false }, { V4L2_PIX_FMT_YVYU, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, true, true, 2, 1 }, + 1, { 16, 0, 0 }, true, true, 2, 1, false }, { V4L2_PIX_FMT_NV12M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, false, 2, 2 }, + 2, { 8, 16, 0 }, false, false, 2, 2, false }, { V4L2_PIX_FMT_NV21M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, true, 2, 2 }, + 2, { 8, 16, 0 }, false, true, 2, 2, false }, { V4L2_PIX_FMT_NV16M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, false, 2, 1 }, + 2, { 8, 16, 0 }, false, false, 2, 1, false }, { V4L2_PIX_FMT_NV61M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, true, 2, 1 }, + 2, { 8, 16, 0 }, false, true, 2, 1, false }, { V4L2_PIX_FMT_YUV420M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 3, { 8, 8, 8 }, false, false, 2, 2 }, + 3, { 8, 8, 8 }, false, false, 2, 2, false }, }; /* diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index cb5d9ef..4dad110 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -33,6 +33,7 @@ struct vsp1_video; * @swap_uv: the U and V components are swapped (V comes before U) * @hsub: horizontal subsampling factor * @vsub: vertical subsampling factor + * @alpha: has an alpha channel */ struct vsp1_format_info { u32 fourcc; @@ -45,6 +46,7 @@ struct vsp1_format_info { bool swap_uv; unsigned int hsub; unsigned int vsub; + bool alpha; }; enum vsp1_pipeline_state { diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index d330865..a2ba107 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -99,6 +99,8 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; + if (fmtinfo->alpha) + outfmt |= VI6_WPF_OUTFMT_PXA; if (fmtinfo->swap_yc) outfmt |= VI6_WPF_OUTFMT_SPYCS; if (fmtinfo->swap_uv) -- cgit v0.10.2 From 7578c204620c8e25ec6e4849cd12098f831a14d0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 May 2014 19:00:05 -0300 Subject: [media] v4l: vsp1: Add V4L2_CID_ALPHA_COMPONENT control support The control is used to configure the fixed alpha channel value, when reading from memory in the RPF or writing to memory in the WPF. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 2824f53..576779f 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -39,6 +39,32 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) } /* ----------------------------------------------------------------------------- + * Controls + */ + +static int rpf_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_rwpf *rpf = + container_of(ctrl->handler, struct vsp1_rwpf, ctrls); + + if (!vsp1_entity_is_streaming(&rpf->entity)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ALPHA_COMPONENT: + vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, + ctrl->val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops rpf_ctrl_ops = { + .s_ctrl = rpf_s_ctrl, +}; + +/* ----------------------------------------------------------------------------- * V4L2 Subdevice Core Operations */ @@ -50,6 +76,11 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) const struct v4l2_rect *crop = &rpf->crop; u32 pstride; u32 infmt; + int ret; + + ret = vsp1_entity_set_streaming(&rpf->entity, enable); + if (ret < 0) + return ret; if (!enable) return 0; @@ -101,14 +132,13 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) (rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) | (rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT)); - /* Use the alpha channel (extended to 8 bits) when available or a - * hardcoded 255 value otherwise. Disable color keying. + /* Use the alpha channel (extended to 8 bits) when available or an + * alpha value set through the V4L2_CID_ALPHA_COMPONENT control + * otherwise. Disable color keying. */ vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED : VI6_RPF_ALPH_SEL_ASEL_FIXED)); - vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, - 255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0); vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0); @@ -198,6 +228,20 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) vsp1_entity_init_formats(subdev, NULL); + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&rpf->ctrls, 1); + v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, + 0, 255, 1, 255); + + rpf->entity.subdev.ctrl_handler = &rpf->ctrls; + + if (rpf->ctrls.error) { + dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n", + index); + ret = rpf->ctrls.error; + goto error; + } + /* Initialize the video device. */ video = &rpf->video; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index b4fb65e..28dd9e7 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -14,6 +14,7 @@ #define __VSP1_RWPF_H__ #include +#include #include #include "vsp1.h" @@ -26,6 +27,7 @@ struct vsp1_rwpf { struct vsp1_entity entity; struct vsp1_video video; + struct v4l2_ctrl_handler ctrls; unsigned int max_width; unsigned int max_height; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index a2ba107..6e05776 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -39,6 +39,35 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data) } /* ----------------------------------------------------------------------------- + * Controls + */ + +static int wpf_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_rwpf *wpf = + container_of(ctrl->handler, struct vsp1_rwpf, ctrls); + u32 value; + + if (!vsp1_entity_is_streaming(&wpf->entity)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ALPHA_COMPONENT: + value = vsp1_wpf_read(wpf, VI6_WPF_OUTFMT); + value &= ~VI6_WPF_OUTFMT_PDV_MASK; + value |= ctrl->val << VI6_WPF_OUTFMT_PDV_SHIFT; + vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, value); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops wpf_ctrl_ops = { + .s_ctrl = wpf_s_ctrl, +}; + +/* ----------------------------------------------------------------------------- * V4L2 Subdevice Core Operations */ @@ -51,6 +80,11 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) unsigned int i; u32 srcrpf = 0; u32 outfmt = 0; + int ret; + + ret = vsp1_entity_set_streaming(&wpf->entity, enable); + if (ret < 0) + return ret; if (!enable) { vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); @@ -113,7 +147,13 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) wpf->entity.formats[RWPF_PAD_SOURCE].code) outfmt |= VI6_WPF_OUTFMT_CSC; + /* Take the control handler lock to ensure that the PDV value won't be + * changed behind our back by a set control operation. + */ + mutex_lock(wpf->ctrls.lock); + outfmt |= vsp1_wpf_read(wpf, VI6_WPF_OUTFMT) & VI6_WPF_OUTFMT_PDV_MASK; vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt); + mutex_unlock(wpf->ctrls.lock); vsp1_write(vsp1, VI6_DPR_WPF_FPORCH(wpf->entity.index), VI6_DPR_WPF_FPORCH_FP_WPFN); @@ -209,6 +249,20 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) vsp1_entity_init_formats(subdev, NULL); + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&wpf->ctrls, 1); + v4l2_ctrl_new_std(&wpf->ctrls, &wpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, + 0, 255, 1, 255); + + wpf->entity.subdev.ctrl_handler = &wpf->ctrls; + + if (wpf->ctrls.error) { + dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", + index); + ret = wpf->ctrls.error; + goto error; + } + /* Initialize the video device. */ video = &wpf->video; -- cgit v0.10.2 From 6418b4d6e1f1d70199a93eeeed948be2229b9c1a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 May 2014 20:35:36 -0300 Subject: [media] v4l: vsp1: bru: Support premultiplied alpha at the BRU inputs Adjust the BRU blending formula to avoid the multiplication by alpha when the corresponding input format is premultiplied. As this requires access to the RPFs connected to the BRU inputs from the BRU module, store pointers to the RPFs in the BRU structure when validating the pipeline. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index f806954..d8d49fb 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -18,6 +18,7 @@ #include "vsp1.h" #include "vsp1_bru.h" +#include "vsp1_rwpf.h" #define BRU_MIN_SIZE 4U #define BRU_MAX_SIZE 8190U @@ -40,11 +41,6 @@ static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data) * V4L2 Subdevice Core Operations */ -static bool bru_is_input_enabled(struct vsp1_bru *bru, unsigned int input) -{ - return media_entity_remote_pad(&bru->entity.pads[input]) != NULL; -} - static int bru_s_stream(struct v4l2_subdev *subdev, int enable) { struct vsp1_bru *bru = to_bru(subdev); @@ -84,6 +80,7 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) VI6_BRU_ROP_AROP(VI6_ROP_NOP)); for (i = 0; i < 4; ++i) { + bool premultiplied = false; u32 ctrl = 0; /* Configure all Blend/ROP units corresponding to an enabled BRU @@ -91,11 +88,15 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) * disabled BRU inputs are used in ROP NOP mode to ignore the * SRC input. */ - if (bru_is_input_enabled(bru, i)) + if (bru->inputs[i].rpf) { ctrl |= VI6_BRU_CTRL_RBC; - else + + premultiplied = bru->inputs[i].rpf->video.format.flags + & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; + } else { ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) | VI6_BRU_CTRL_AROP(VI6_ROP_NOP); + } /* Select the virtual RPF as the Blend/ROP unit A DST input to * serve as a background color. @@ -117,10 +118,18 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) * * DSTc = DSTc * (1 - SRCa) + SRCc * SRCa * DSTa = DSTa * (1 - SRCa) + SRCa + * + * when the SRC input isn't premultiplied, and to + * + * DSTc = DSTc * (1 - SRCa) + SRCc + * DSTa = DSTa * (1 - SRCa) + SRCa + * + * otherwise. */ vsp1_bru_write(bru, VI6_BRU_BLD(i), VI6_BRU_BLD_CCMDX_255_SRC_A | - VI6_BRU_BLD_CCMDY_SRC_A | + (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY : + VI6_BRU_BLD_CCMDY_SRC_A) | VI6_BRU_BLD_ACMDX_255_SRC_A | VI6_BRU_BLD_ACMDY_COEFY | (0xff << VI6_BRU_BLD_COEFY_SHIFT)); @@ -192,7 +201,7 @@ static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, case V4L2_SUBDEV_FORMAT_TRY: return v4l2_subdev_get_try_crop(fh, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: - return &bru->compose[pad]; + return &bru->inputs[pad].compose; default: return NULL; } diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h index 3706270..5b03479 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.h +++ b/drivers/media/platform/vsp1/vsp1_bru.h @@ -19,6 +19,7 @@ #include "vsp1_entity.h" struct vsp1_device; +struct vsp1_rwpf; #define BRU_PAD_SINK(n) (n) #define BRU_PAD_SOURCE 4 @@ -26,7 +27,10 @@ struct vsp1_device; struct vsp1_bru { struct vsp1_entity entity; - struct v4l2_rect compose[4]; + struct { + struct vsp1_rwpf *rpf; + struct v4l2_rect compose; + } inputs[4]; }; static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 4dd4d61..58fc076 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -334,7 +334,10 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, */ if (entity->type == VSP1_ENTITY_BRU) { struct vsp1_bru *bru = to_bru(&entity->subdev); - struct v4l2_rect *rect = &bru->compose[pad->index]; + struct v4l2_rect *rect = + &bru->inputs[pad->index].compose; + + bru->inputs[pad->index].rpf = input; input->location.left = rect->left; input->location.top = rect->top; @@ -373,6 +376,26 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, return 0; } +static void __vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) +{ + if (pipe->bru) { + struct vsp1_bru *bru = to_bru(&pipe->bru->subdev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i) + bru->inputs[i].rpf = NULL; + } + + INIT_LIST_HEAD(&pipe->entities); + pipe->state = VSP1_PIPELINE_STOPPED; + pipe->buffers_ready = 0; + pipe->num_video = 0; + pipe->num_inputs = 0; + pipe->output = NULL; + pipe->bru = NULL; + pipe->lif = NULL; +} + static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, struct vsp1_video *video) { @@ -437,13 +460,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, return 0; error: - INIT_LIST_HEAD(&pipe->entities); - pipe->buffers_ready = 0; - pipe->num_video = 0; - pipe->num_inputs = 0; - pipe->output = NULL; - pipe->bru = NULL; - pipe->lif = NULL; + __vsp1_pipeline_cleanup(pipe); return ret; } @@ -474,16 +491,8 @@ static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) mutex_lock(&pipe->lock); /* If we're the last user clean up the pipeline. */ - if (--pipe->use_count == 0) { - INIT_LIST_HEAD(&pipe->entities); - pipe->state = VSP1_PIPELINE_STOPPED; - pipe->buffers_ready = 0; - pipe->num_video = 0; - pipe->num_inputs = 0; - pipe->output = NULL; - pipe->bru = NULL; - pipe->lif = NULL; - } + if (--pipe->use_count == 0) + __vsp1_pipeline_cleanup(pipe); mutex_unlock(&pipe->lock); } -- cgit v0.10.2 From 9aca813e1401dcafe1c0a9d3b03795e254571504 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 May 2014 20:35:36 -0300 Subject: [media] v4l: vsp1: bru: Support non-premultiplied colors at the BRU output The BRU outputs premultiplied colors, enable color data normalization when the format configured at the output of the pipeline isn't premultiplied. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index d8d49fb..86b32bc 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -43,8 +43,10 @@ static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data) static int bru_s_stream(struct v4l2_subdev *subdev, int enable) { + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); struct vsp1_bru *bru = to_bru(subdev); struct v4l2_mbus_framefmt *format; + unsigned int flags; unsigned int i; if (!enable) @@ -58,8 +60,13 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) * to sane default values for now. */ - /* Disable both color data normalization and dithering. */ - vsp1_bru_write(bru, VI6_BRU_INCTRL, 0); + /* Disable dithering and enable color data normalization unless the + * format at the pipeline output is premultiplied. + */ + flags = pipe->output ? pipe->output->video.format.flags : 0; + vsp1_bru_write(bru, VI6_BRU_INCTRL, + flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? + 0 : VI6_BRU_INCTRL_NRM); /* Set the background position to cover the whole output image and * set its color to opaque black. -- cgit v0.10.2 From a16e2794592b733d47f372f0cadcd9ff0f349ca9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 May 2014 22:46:30 -0300 Subject: [media] v4l: vsp1: bru: Make the background color configurable Expose the background color to userspace through the V4L2_CID_BG_COLOR control. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 86b32bc..a0c1984 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -38,6 +38,32 @@ static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data) } /* ----------------------------------------------------------------------------- + * Controls + */ + +static int bru_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vsp1_bru *bru = + container_of(ctrl->handler, struct vsp1_bru, ctrls); + + if (!vsp1_entity_is_streaming(&bru->entity)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BG_COLOR: + vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, ctrl->val | + (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops bru_ctrl_ops = { + .s_ctrl = bru_s_ctrl, +}; + +/* ----------------------------------------------------------------------------- * V4L2 Subdevice Core Operations */ @@ -48,6 +74,11 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) struct v4l2_mbus_framefmt *format; unsigned int flags; unsigned int i; + int ret; + + ret = vsp1_entity_set_streaming(&bru->entity, enable); + if (ret < 0) + return ret; if (!enable) return 0; @@ -68,15 +99,11 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? 0 : VI6_BRU_INCTRL_NRM); - /* Set the background position to cover the whole output image and - * set its color to opaque black. - */ + /* Set the background position to cover the whole output image. */ vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE, (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0); - vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, - 0xff << VI6_BRU_VIRRPF_COL_A_SHIFT); /* Route BRU input 1 as SRC input to the ROP unit and configure the ROP * unit with a NOP operation to make BRU input 1 available as the @@ -407,5 +434,19 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) vsp1_entity_init_formats(subdev, NULL); + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&bru->ctrls, 1); + v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR, + 0, 0xffffff, 1, 0); + + bru->entity.subdev.ctrl_handler = &bru->ctrls; + + if (bru->ctrls.error) { + dev_err(vsp1->dev, "bru: failed to initialize controls\n"); + ret = bru->ctrls.error; + vsp1_entity_destroy(&bru->entity); + return ERR_PTR(ret); + } + return bru; } diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h index 5b03479..16b1c65 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.h +++ b/drivers/media/platform/vsp1/vsp1_bru.h @@ -14,6 +14,7 @@ #define __VSP1_BRU_H__ #include +#include #include #include "vsp1_entity.h" @@ -27,6 +28,8 @@ struct vsp1_rwpf; struct vsp1_bru { struct vsp1_entity entity; + struct v4l2_ctrl_handler ctrls; + struct { struct vsp1_rwpf *rpf; struct v4l2_rect compose; -- cgit v0.10.2 From bdc2df62ae38bbab044078f4d25a7a3d9e2379c9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 30 May 2014 21:45:48 -0300 Subject: [media] v4l: vsp1: uds: Fix scaling of alpha layer Pixel color components can be scaled using either bilinear interpolation or a multitap filter. The multitap filter provides better results, but can't be selected when the alpha layer need to be scaled down by more than 1/2. Disable alpha scaling when the input has a fixed alpha value, and program the UDS to output a fixed alpha value in that case. This ensures the multitap filter will be used whenever possible. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 576779f..d14d26b 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -46,6 +46,7 @@ static int rpf_s_ctrl(struct v4l2_ctrl *ctrl) { struct vsp1_rwpf *rpf = container_of(ctrl->handler, struct vsp1_rwpf, ctrls); + struct vsp1_pipeline *pipe; if (!vsp1_entity_is_streaming(&rpf->entity)) return 0; @@ -54,6 +55,9 @@ static int rpf_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_ALPHA_COMPONENT: vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, ctrl->val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + + pipe = to_vsp1_pipeline(&rpf->entity.subdev.entity); + vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, ctrl->val); break; } diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 0293bdb..de92ef4 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -45,6 +45,11 @@ static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data) * Scaling Computation */ +void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha) +{ + vsp1_uds_write(uds, VI6_UDS_ALPVAL, alpha << VI6_UDS_ALPVAL_VAL0_SHIFT); +} + /* * uds_output_size - Return the output size for an input size and scaling ratio * @input: input size in pixels @@ -105,49 +110,56 @@ static unsigned int uds_compute_ratio(unsigned int input, unsigned int output) return (input - 1) * 4096 / (output - 1); } -static void uds_compute_ratios(struct vsp1_uds *uds) -{ - struct v4l2_mbus_framefmt *input = &uds->entity.formats[UDS_PAD_SINK]; - struct v4l2_mbus_framefmt *output = - &uds->entity.formats[UDS_PAD_SOURCE]; - - uds->hscale = uds_compute_ratio(input->width, output->width); - uds->vscale = uds_compute_ratio(input->height, output->height); - - dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", - uds->hscale, uds->vscale); -} - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Core Operations */ static int uds_s_stream(struct v4l2_subdev *subdev, int enable) { - const struct v4l2_mbus_framefmt *format; struct vsp1_uds *uds = to_uds(subdev); + const struct v4l2_mbus_framefmt *output; + const struct v4l2_mbus_framefmt *input; + unsigned int hscale; + unsigned int vscale; + bool multitap; if (!enable) return 0; - /* Enable multi-tap scaling. */ - vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_AON | VI6_UDS_CTRL_BC); + input = &uds->entity.formats[UDS_PAD_SINK]; + output = &uds->entity.formats[UDS_PAD_SOURCE]; + + hscale = uds_compute_ratio(input->width, output->width); + vscale = uds_compute_ratio(input->height, output->height); + + dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", hscale, vscale); + + /* Multi-tap scaling can't be enabled along with alpha scaling when + * scaling down with a factor lower than or equal to 1/2 in either + * direction. + */ + if (uds->scale_alpha && (hscale >= 8192 || vscale >= 8192)) + multitap = false; + else + multitap = true; + + vsp1_uds_write(uds, VI6_UDS_CTRL, + (uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) | + (multitap ? VI6_UDS_CTRL_BC : 0)); vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH, - (uds_passband_width(uds->hscale) + (uds_passband_width(hscale) << VI6_UDS_PASS_BWIDTH_H_SHIFT) | - (uds_passband_width(uds->vscale) + (uds_passband_width(vscale) << VI6_UDS_PASS_BWIDTH_V_SHIFT)); /* Set the scaling ratios and the output size. */ - format = &uds->entity.formats[UDS_PAD_SOURCE]; - vsp1_uds_write(uds, VI6_UDS_SCALE, - (uds->hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | - (uds->vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); + (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | + (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE, - (format->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (format->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); + (output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | + (output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); return 0; } @@ -280,9 +292,6 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which); } - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) - uds_compute_ratios(uds); - return 0; } diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h index 479d12d..031ac0d 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.h +++ b/drivers/media/platform/vsp1/vsp1_uds.h @@ -25,9 +25,7 @@ struct vsp1_device; struct vsp1_uds { struct vsp1_entity entity; - - unsigned int hscale; - unsigned int vscale; + bool scale_alpha; }; static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev) @@ -37,4 +35,6 @@ static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev) struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index); +void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha); + #endif /* __VSP1_UDS_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 58fc076..915a20e 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -31,6 +31,7 @@ #include "vsp1_bru.h" #include "vsp1_entity.h" #include "vsp1_rwpf.h" +#include "vsp1_uds.h" #include "vsp1_video.h" #define VSP1_VIDEO_DEF_FORMAT V4L2_PIX_FMT_YUYV @@ -306,13 +307,14 @@ vsp1_video_format_adjust(struct vsp1_video *video, * Pipeline Management */ -static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, +static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe, + struct vsp1_rwpf *input, struct vsp1_rwpf *output) { struct vsp1_entity *entity; unsigned int entities = 0; struct media_pad *pad; - bool uds_found = false; + bool bru_found = false; input->location.left = 0; input->location.top = 0; @@ -341,6 +343,8 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, input->location.left = rect->left; input->location.top = rect->top; + + bru_found = true; } /* We've reached the WPF, we're done. */ @@ -355,9 +359,12 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, /* UDS can't be chained. */ if (entity->type == VSP1_ENTITY_UDS) { - if (uds_found) + if (pipe->uds) return -EPIPE; - uds_found = true; + + pipe->uds = entity; + pipe->uds_input = bru_found ? pipe->bru + : &input->entity; } /* Follow the source link. The link setup operations ensure @@ -394,6 +401,7 @@ static void __vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) pipe->output = NULL; pipe->bru = NULL; pipe->lif = NULL; + pipe->uds = NULL; } static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, @@ -451,7 +459,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, * contains no loop and that all branches end at the output WPF. */ for (i = 0; i < pipe->num_inputs; ++i) { - ret = vsp1_pipeline_validate_branch(pipe->inputs[i], + ret = vsp1_pipeline_validate_branch(pipe, pipe->inputs[i], pipe->output); if (ret < 0) goto error; @@ -654,6 +662,47 @@ done: spin_unlock_irqrestore(&pipe->irqlock, flags); } +/* + * Propagate the alpha value through the pipeline. + * + * As the UDS has restricted scaling capabilities when the alpha component needs + * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha + * value. The UDS then outputs a fixed alpha value which needs to be programmed + * from the input RPF alpha. + */ +void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, + struct vsp1_entity *input, + unsigned int alpha) +{ + struct vsp1_entity *entity; + struct media_pad *pad; + + pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]); + + while (pad) { + if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); + + /* The BRU background color has a fixed alpha value set to 255, + * the output alpha value is thus always equal to 255. + */ + if (entity->type == VSP1_ENTITY_BRU) + alpha = 255; + + if (entity->type == VSP1_ENTITY_UDS) { + struct vsp1_uds *uds = to_uds(&entity->subdev); + + vsp1_uds_set_alpha(uds, alpha); + break; + } + + pad = &entity->pads[entity->source_pad]; + pad = media_entity_remote_pad(pad); + } +} + /* ----------------------------------------------------------------------------- * videobuf2 Queue Operations */ @@ -761,6 +810,25 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) mutex_lock(&pipe->lock); if (pipe->stream_count == pipe->num_video - 1) { + if (pipe->uds) { + struct vsp1_uds *uds = to_uds(&pipe->uds->subdev); + + /* If a BRU is present in the pipeline before the UDS, + * the alpha component doesn't need to be scaled as the + * BRU output alpha value is fixed to 255. Otherwise we + * need to scale the alpha component only when available + * at the input RPF. + */ + if (pipe->uds_input->type == VSP1_ENTITY_BRU) { + uds->scale_alpha = false; + } else { + struct vsp1_rwpf *rpf = + to_rwpf(&pipe->uds_input->subdev); + + uds->scale_alpha = rpf->video.fmtinfo->alpha; + } + } + list_for_each_entry(entity, &pipe->entities, list_pipe) { vsp1_entity_route_setup(entity); diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index 4dad110..fd2851a 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -79,6 +79,8 @@ struct vsp1_pipeline { struct vsp1_rwpf *output; struct vsp1_entity *bru; struct vsp1_entity *lif; + struct vsp1_entity *uds; + struct vsp1_entity *uds_input; struct list_head entities; }; @@ -143,4 +145,8 @@ void vsp1_video_cleanup(struct vsp1_video *video); void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); +void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, + struct vsp1_entity *input, + unsigned int alpha); + #endif /* __VSP1_VIDEO_H__ */ -- cgit v0.10.2 From 9241650d62f79a3da01f1d5e8ebd195083330b75 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Jun 2014 17:41:06 -0300 Subject: [media] v4l: vb2: Don't return POLLERR during transient buffer underruns The V4L2 specification states that "When the application did not call VIDIOC_QBUF or VIDIOC_STREAMON yet the poll() function succeeds, but sets the POLLERR flag in the revents field." The vb2_poll() function sets POLLERR when the queued buffers list is empty, regardless of whether this is caused by the stream not being active yet, or by a transient buffer underrun. Bring the implementation in line with the specification by returning POLLERR if no buffer has been queued only when the queue is not streaming. Buffer underruns during streaming are not treated specially anymore and just result in poll() blocking until the next event. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Pawel Osciak Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 1d67e95..1fc85fb 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -2559,9 +2559,10 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) } /* - * There is nothing to wait for if no buffers have already been queued. + * There is nothing to wait for if no buffer has been queued and the + * queue isn't streaming. */ - if (list_empty(&q->queued_list)) + if (list_empty(&q->queued_list) && !vb2_is_streaming(q)) return res | POLLERR; if (list_empty(&q->done_list)) -- cgit v0.10.2 From 4bb7267dc41247810815e8b15f0e9fb1456c8d8c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Jun 2014 18:53:25 -0300 Subject: [media] v4l: vb2: Add fatal error condition flag When a fatal error occurs that render the device unusable, the only options for a driver to signal the error condition to userspace is to set the V4L2_BUF_FLAG_ERROR flag when dequeuing buffers and to return an error from the buffer prepare handler when queuing buffers. The buffer error flag indicates a transient error and can't be used by applications to detect fatal errors. Returning an error from vb2_qbuf() is thus the only real indication that a fatal error occurred. However, this is difficult to handle for multithreaded applications that requeue buffers from a thread other than the control thread. In particular the poll() call in the control thread will not notify userspace of the error. This patch adds an explicit mechanism to report fatal errors to userspace. Drivers can call the vb2_queue_error() function to signal a fatal error. From this moment on, buffer preparation will return -EIO to userspace, and vb2_poll() will set the POLLERR flag and return immediately. The error flag is cleared when cancelling the queue, either at stream off time (through vb2_streamoff) or when releasing the queue with vb2_queue_release(). Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 1fc85fb..a5c41cc 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1606,6 +1606,11 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) return -EINVAL; } + if (q->error) { + dprintk(1, "fatal error occurred on queue\n"); + return -EIO; + } + vb->state = VB2_BUF_STATE_PREPARING; vb->v4l2_buf.timestamp.tv_sec = 0; vb->v4l2_buf.timestamp.tv_usec = 0; @@ -1903,6 +1908,11 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) return -EINVAL; } + if (q->error) { + dprintk(1, "Queue in error state, will not wait for buffers\n"); + return -EIO; + } + if (!list_empty(&q->done_list)) { /* * Found a buffer that we were waiting for. @@ -1928,7 +1938,8 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) */ dprintk(3, "will sleep waiting for buffers\n"); ret = wait_event_interruptible(q->done_wq, - !list_empty(&q->done_list) || !q->streaming); + !list_empty(&q->done_list) || !q->streaming || + q->error); /* * We need to reevaluate both conditions again after reacquiring @@ -2125,6 +2136,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) q->streaming = 0; q->start_streaming_called = 0; q->queued_count = 0; + q->error = 0; /* * Remove all buffers from videobuf's list... @@ -2202,6 +2214,27 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) } /** + * vb2_queue_error() - signal a fatal error on the queue + * @q: videobuf2 queue + * + * Flag that a fatal unrecoverable error has occurred and wake up all processes + * waiting on the queue. Polling will now set POLLERR and queuing and dequeuing + * buffers will return -EIO. + * + * The error flag will be cleared when cancelling the queue, either from + * vb2_streamoff or vb2_queue_release. Drivers should thus not call this + * function before starting the stream, otherwise the error flag will remain set + * until the queue is released when closing the device node. + */ +void vb2_queue_error(struct vb2_queue *q) +{ + q->error = 1; + + wake_up_all(&q->done_wq); +} +EXPORT_SYMBOL_GPL(vb2_queue_error); + +/** * vb2_streamon - start streaming * @q: videobuf2 queue * @type: type argument passed from userspace to vidioc_streamon handler @@ -2560,9 +2593,9 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) /* * There is nothing to wait for if no buffer has been queued and the - * queue isn't streaming. + * queue isn't streaming, or if the error flag is set. */ - if (list_empty(&q->queued_list) && !vb2_is_streaming(q)) + if ((list_empty(&q->queued_list) && !vb2_is_streaming(q)) || q->error) return res | POLLERR; if (list_empty(&q->done_list)) diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 8fab6fa..1a262ae 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -375,6 +375,7 @@ struct v4l2_fh; * @streaming: current streaming state * @start_streaming_called: start_streaming() was called successfully and we * started streaming. + * @error: a fatal error occurred on the queue * @fileio: file io emulator internal data, used only if emulator is active * @threadio: thread io internal data, used only if thread is active */ @@ -411,6 +412,7 @@ struct vb2_queue { unsigned int streaming:1; unsigned int start_streaming_called:1; + unsigned int error:1; struct vb2_fileio_data *fileio; struct vb2_threadio_data *threadio; @@ -444,6 +446,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b); int __must_check vb2_queue_init(struct vb2_queue *q); void vb2_queue_release(struct vb2_queue *q); +void vb2_queue_error(struct vb2_queue *q); int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b); int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb); -- cgit v0.10.2 From 3ed1a0023c48db707db537ef8aeb21445db637a6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 11 Jun 2014 11:49:31 -0300 Subject: [media] v4l: omap4iss: tighten omap4iss dependencies The OMAP4 camera support depends on I2C and VIDEO_V4L2, both of which can be loadable modules. This causes build failures if we want the camera driver to be built-in. This can be solved by turning the option into "tristate", which unfortunately causes another problem, because the driver incorrectly calls a platform-internal interface for omap4_ctrl_pad_readl/omap4_ctrl_pad_writel. Instead, this patch just forbids the invalid configurations and ensures that the driver can only be built if all its dependencies are built-in. Cc: stable@vger.kernel.org Signed-off-by: Arnd Bergmann Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/Kconfig b/drivers/staging/media/omap4iss/Kconfig index 78b0fba..8afc6fe 100644 --- a/drivers/staging/media/omap4iss/Kconfig +++ b/drivers/staging/media/omap4iss/Kconfig @@ -1,6 +1,6 @@ config VIDEO_OMAP4 bool "OMAP 4 Camera support" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && I2C && ARCH_OMAP4 + depends on VIDEO_V4L2=y && VIDEO_V4L2_SUBDEV_API && I2C=y && ARCH_OMAP4 select VIDEOBUF2_DMA_CONTIG ---help--- Driver for an OMAP 4 ISS controller. -- cgit v0.10.2 From 057ef1e56c956b4f68f33c8d0ccd301a0ecb6326 Mon Sep 17 00:00:00 2001 From: Vitaly Osipov Date: Thu, 5 Jun 2014 04:07:48 -0300 Subject: [media] v4l: omap4iss: Copy paste error in iss_get_clocks It makes more sense to return PTR_ERR(iss->iss_ctrlclk) here. The current code looks like an oversight in pasting the block just above this one. Signed-off-by: Vitaly Osipov Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 2e422dd..4a9e444 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -1029,7 +1029,7 @@ static int iss_get_clocks(struct iss_device *iss) if (IS_ERR(iss->iss_ctrlclk)) { dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n"); iss_put_clocks(iss); - return PTR_ERR(iss->iss_fck); + return PTR_ERR(iss->iss_ctrlclk); } return 0; -- cgit v0.10.2 From 2b44ac9e84d583c0e25b22bd0809d9ddf0c54f6f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Jun 2014 19:23:16 -0300 Subject: [media] v4l: omap4iss: Don't reinitialize the video qlock at every streamon Initialize the spin lock once only when initializing the video object. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index cbf455d..56942df 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -895,7 +895,6 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) video->queue = &vfh->queue; INIT_LIST_HEAD(&video->dmaqueue); - spin_lock_init(&video->qlock); video->error = false; atomic_set(&pipe->frame_number, -1); @@ -1175,6 +1174,7 @@ int omap4iss_video_init(struct iss_video *video, const char *name) if (ret < 0) return ret; + spin_lock_init(&video->qlock); mutex_init(&video->mutex); atomic_set(&video->active, 0); -- cgit v0.10.2 From 186612342500b0af8498d7c8bc6b3ac32ac7a48e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Jun 2014 17:49:41 -0300 Subject: [media] v4l: omap4iss: Add module debug parameter The parameter is used to initialize the video node debug field and activate the V4L debug infrastructure. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 56942df..61b974a 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -25,6 +25,9 @@ #include "iss_video.h" #include "iss.h" +static unsigned debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); /* ----------------------------------------------------------------------------- * Helper functions @@ -1043,6 +1046,8 @@ static int iss_video_open(struct file *file) if (handle == NULL) return -ENOMEM; + video->video.debug = debug; + v4l2_fh_init(&handle->vfh, &video->video); v4l2_fh_add(&handle->vfh); -- cgit v0.10.2 From 1153be56a10576abc3dd2a89aee59debb4dcc2b6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 6 Jun 2014 11:59:04 -0300 Subject: [media] v4l: omap4iss: Use the devm_* managed allocators This simplifies remove and error code paths. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 4a9e444..d548371 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -1003,32 +1003,17 @@ static void iss_disable_clocks(struct iss_device *iss) clk_disable(iss->iss_fck); } -static void iss_put_clocks(struct iss_device *iss) -{ - if (iss->iss_fck) { - clk_put(iss->iss_fck); - iss->iss_fck = NULL; - } - - if (iss->iss_ctrlclk) { - clk_put(iss->iss_ctrlclk); - iss->iss_ctrlclk = NULL; - } -} - static int iss_get_clocks(struct iss_device *iss) { - iss->iss_fck = clk_get(iss->dev, "iss_fck"); + iss->iss_fck = devm_clk_get(iss->dev, "iss_fck"); if (IS_ERR(iss->iss_fck)) { dev_err(iss->dev, "Unable to get iss_fck clock info\n"); - iss_put_clocks(iss); return PTR_ERR(iss->iss_fck); } - iss->iss_ctrlclk = clk_get(iss->dev, "iss_ctrlclk"); + iss->iss_ctrlclk = devm_clk_get(iss->dev, "iss_ctrlclk"); if (IS_ERR(iss->iss_ctrlclk)) { dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n"); - iss_put_clocks(iss); return PTR_ERR(iss->iss_ctrlclk); } @@ -1104,29 +1089,11 @@ static int iss_map_mem_resource(struct platform_device *pdev, { struct resource *mem; - /* request the mem region for the camera registers */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, res); - if (!mem) { - dev_err(iss->dev, "no mem resource?\n"); - return -ENODEV; - } - - if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { - dev_err(iss->dev, - "cannot reserve camera register I/O region\n"); - return -ENODEV; - } - iss->res[res] = mem; - /* map the region */ - iss->regs[res] = ioremap_nocache(mem->start, resource_size(mem)); - if (!iss->regs[res]) { - dev_err(iss->dev, "cannot map camera register I/O region\n"); - return -ENODEV; - } + iss->regs[res] = devm_ioremap_resource(iss->dev, mem); - return 0; + return PTR_ERR_OR_ZERO(iss->regs[res]); } static void iss_unregister_entities(struct iss_device *iss) @@ -1389,7 +1356,7 @@ static int iss_probe(struct platform_device *pdev) if (pdata == NULL) return -EINVAL; - iss = kzalloc(sizeof(*iss), GFP_KERNEL); + iss = devm_kzalloc(&pdev->dev, sizeof(*iss), GFP_KERNEL); if (!iss) { dev_err(&pdev->dev, "Could not allocate memory\n"); return -ENOMEM; @@ -1456,7 +1423,8 @@ static int iss_probe(struct platform_device *pdev) goto error_iss; } - if (request_irq(iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) { + if (devm_request_irq(iss->dev, iss->irq_num, iss_isr, IRQF_SHARED, + "OMAP4 ISS", iss)) { dev_err(iss->dev, "Unable to request IRQ\n"); ret = -EINVAL; goto error_iss; @@ -1465,7 +1433,7 @@ static int iss_probe(struct platform_device *pdev) /* Entities */ ret = iss_initialize_modules(iss); if (ret < 0) - goto error_irq; + goto error_iss; ret = iss_register_entities(iss); if (ret < 0) @@ -1477,29 +1445,12 @@ static int iss_probe(struct platform_device *pdev) error_modules: iss_cleanup_modules(iss); -error_irq: - free_irq(iss->irq_num, iss); error_iss: omap4iss_put(iss); error: - iss_put_clocks(iss); - - for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) { - if (iss->regs[i]) { - iounmap(iss->regs[i]); - iss->regs[i] = NULL; - } - - if (iss->res[i]) { - release_mem_region(iss->res[i]->start, - resource_size(iss->res[i])); - iss->res[i] = NULL; - } - } platform_set_drvdata(pdev, NULL); mutex_destroy(&iss->iss_mutex); - kfree(iss); return ret; } @@ -1507,29 +1458,10 @@ error: static int iss_remove(struct platform_device *pdev) { struct iss_device *iss = platform_get_drvdata(pdev); - unsigned int i; iss_unregister_entities(iss); iss_cleanup_modules(iss); - free_irq(iss->irq_num, iss); - iss_put_clocks(iss); - - for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) { - if (iss->regs[i]) { - iounmap(iss->regs[i]); - iss->regs[i] = NULL; - } - - if (iss->res[i]) { - release_mem_region(iss->res[i]->start, - resource_size(iss->res[i])); - iss->res[i] = NULL; - } - } - - kfree(iss); - return 0; } -- cgit v0.10.2 From cb766937e36fc651ad8e8f61e0a8be323140ef75 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Jun 2014 19:25:06 -0300 Subject: [media] v4l: omap4iss: Signal fatal errors to the vb2 queue When a fatal error occurs in the pipeline signal it to the vb2 queue with a call to vb2_queue_error(). The queue will then take care to return -EIO when preparing buffers, remove the driver-specific code that now duplicates that check. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 61b974a..943b5b0 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -331,15 +331,6 @@ static int iss_video_buf_prepare(struct vb2_buffer *vb) if (vb2_plane_size(vb, 0) < size) return -ENOBUFS; - /* Refuse to prepare the buffer is the video node has registered an - * error. We don't need to take any lock here as the operation is - * inherently racy. The authoritative check will be performed in the - * queue handler, which can't return an error, this check is just a best - * effort to notify userspace as early as possible. - */ - if (unlikely(video->error)) - return -EIO; - addr = vb2_dma_contig_plane_dma_addr(vb, 0); if (!IS_ALIGNED(addr, 32)) { dev_dbg(video->iss->dev, @@ -363,6 +354,11 @@ static void iss_video_buf_queue(struct vb2_buffer *vb) spin_lock_irqsave(&video->qlock, flags); + /* Mark the buffer is faulty and give it back to the queue immediately + * if the video node has registered an error. vb2 will perform the same + * check when preparing the buffer, but that is inherently racy, so we + * need to handle the race condition with an authoritative check here. + */ if (unlikely(video->error)) { vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); spin_unlock_irqrestore(&video->qlock, flags); @@ -513,6 +509,7 @@ void omap4iss_video_cancel_stream(struct iss_video *video) vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } + vb2_queue_error(video->queue); video->error = true; spin_unlock_irqrestore(&video->qlock, flags); -- cgit v0.10.2 From 0db83cede26c5676303e8007f27a291b0277ba1b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 6 Jun 2014 12:08:59 -0300 Subject: [media] MAINTAINERS: Add the OMAP4 ISS driver Update the OMAP Image Signal Processor entry to cover both the OMAP3 ISP and OMAP4 ISS. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 6bae4d8..c771406 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6491,11 +6491,12 @@ L: linux-omap@vger.kernel.org S: Maintained F: arch/arm/mach-omap2/omap_hwmod_44xx_data.c -OMAP IMAGE SIGNAL PROCESSOR (ISP) +OMAP IMAGING SUBSYSTEM (OMAP3 ISP and OMAP4 ISS) M: Laurent Pinchart L: linux-media@vger.kernel.org S: Maintained F: drivers/media/platform/omap3isp/ +F: drivers/staging/media/omap4iss/ OMAP USB SUPPORT M: Felipe Balbi -- cgit v0.10.2 From d2dbd9d5bfb38c4bb4aeaf650ac7661e6cf9c105 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Jun 2014 04:47:37 -0300 Subject: [media] DocBook media: fix wrong spacing There shouldn't be any spaces after or before . This leads to ugly results like: 'image size set by VIDIOC_S_FMT .' Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/selection-api.xml b/Documentation/DocBook/media/v4l/selection-api.xml index 4c238ce..28cbded 100644 --- a/Documentation/DocBook/media/v4l/selection-api.xml +++ b/Documentation/DocBook/media/v4l/selection-api.xml @@ -86,47 +86,47 @@ selection targets available for a video capture device. It is recommended to configure the cropping targets before to the composing targets. The range of coordinates of the top left corner, width and height of -areas that can be sampled is given by the V4L2_SEL_TGT_CROP_BOUNDS - target. It is recommended for the driver developers to put the -top/left corner at position (0,0) . The rectangle's +areas that can be sampled is given by the V4L2_SEL_TGT_CROP_BOUNDS +target. It is recommended for the driver developers to put the +top/left corner at position (0,0). The rectangle's coordinates are expressed in pixels. The top left corner, width and height of the source rectangle, that is -the area actually sampled, is given by the V4L2_SEL_TGT_CROP - target. It uses the same coordinate system as -V4L2_SEL_TGT_CROP_BOUNDS . The active cropping area must lie -completely inside the capture boundaries. The driver may further adjust the -requested size and/or position according to hardware limitations. +the area actually sampled, is given by the V4L2_SEL_TGT_CROP +target. It uses the same coordinate system as V4L2_SEL_TGT_CROP_BOUNDS. +The active cropping area must lie completely inside the capture boundaries. The +driver may further adjust the requested size and/or position according to hardware +limitations. Each capture device has a default source rectangle, given by the - V4L2_SEL_TGT_CROP_DEFAULT target. This rectangle shall +V4L2_SEL_TGT_CROP_DEFAULT target. This rectangle shall over what the driver writer considers the complete picture. Drivers shall set the active crop rectangle to the default when the driver is first loaded, but not later. The composing targets refer to a memory buffer. The limits of composing -coordinates are obtained using V4L2_SEL_TGT_COMPOSE_BOUNDS -. All coordinates are expressed in pixels. The rectangle's top/left -corner must be located at position (0,0) . The width and -height are equal to the image size set by VIDIOC_S_FMT . +coordinates are obtained using V4L2_SEL_TGT_COMPOSE_BOUNDS. +All coordinates are expressed in pixels. The rectangle's top/left +corner must be located at position (0,0). The width and +height are equal to the image size set by VIDIOC_S_FMT. The part of a buffer into which the image is inserted by the hardware is -controlled by the V4L2_SEL_TGT_COMPOSE target. +controlled by the V4L2_SEL_TGT_COMPOSE target. The rectangle's coordinates are also expressed in the same coordinate system as the bounds rectangle. The composing rectangle must lie completely inside bounds rectangle. The driver must adjust the composing rectangle to fit to the bounding limits. Moreover, the driver can perform other adjustments according to hardware limitations. The application can control rounding behaviour using - constraint flags . + constraint flags. For capture devices the default composing rectangle is queried using - V4L2_SEL_TGT_COMPOSE_DEFAULT . It is usually equal to the +V4L2_SEL_TGT_COMPOSE_DEFAULT. It is usually equal to the bounding rectangle. The part of a buffer that is modified by the hardware is given by - V4L2_SEL_TGT_COMPOSE_PADDED . It contains all pixels -defined using V4L2_SEL_TGT_COMPOSE plus all +V4L2_SEL_TGT_COMPOSE_PADDED. It contains all pixels +defined using V4L2_SEL_TGT_COMPOSE plus all padding data modified by hardware during insertion process. All pixels outside this rectangle must not be changed by the hardware. The content of pixels that lie inside the padded area but outside active area is @@ -140,52 +140,51 @@ where the rubbish pixels are located and remove them if needed. Configuration of video output For output devices targets and ioctls are used similarly to the video -capture case. The composing rectangle refers to the +capture case. The composing rectangle refers to the insertion of an image into a video signal. The cropping rectangles refer to a memory buffer. It is recommended to configure the composing targets before to the cropping targets. The cropping targets refer to the memory buffer that contains an image to be inserted into a video signal or graphical screen. The limits of cropping -coordinates are obtained using V4L2_SEL_TGT_CROP_BOUNDS . +coordinates are obtained using V4L2_SEL_TGT_CROP_BOUNDS. All coordinates are expressed in pixels. The top/left corner is always point - (0,0) . The width and height is equal to the image size -specified using VIDIOC_S_FMT ioctl. +(0,0). The width and height is equal to the image size +specified using VIDIOC_S_FMT ioctl. The top left corner, width and height of the source rectangle, that is the area from which image date are processed by the hardware, is given by the - V4L2_SEL_TGT_CROP . Its coordinates are expressed +V4L2_SEL_TGT_CROP. Its coordinates are expressed in in the same coordinate system as the bounds rectangle. The active cropping area must lie completely inside the crop boundaries and the driver may further adjust the requested size and/or position according to hardware limitations. For output devices the default cropping rectangle is queried using - V4L2_SEL_TGT_CROP_DEFAULT . It is usually equal to the +V4L2_SEL_TGT_CROP_DEFAULT. It is usually equal to the bounding rectangle. The part of a video signal or graphics display where the image is -inserted by the hardware is controlled by -V4L2_SEL_TGT_COMPOSE target. The rectangle's coordinates -are expressed in pixels. The composing rectangle must lie completely inside the -bounds rectangle. The driver must adjust the area to fit to the bounding -limits. Moreover, the driver can perform other adjustments according to -hardware limitations. - -The device has a default composing rectangle, given by the -V4L2_SEL_TGT_COMPOSE_DEFAULT target. This rectangle shall cover what +inserted by the hardware is controlled by V4L2_SEL_TGT_COMPOSE +target. The rectangle's coordinates are expressed in pixels. The composing +rectangle must lie completely inside the bounds rectangle. The driver must +adjust the area to fit to the bounding limits. Moreover, the driver can +perform other adjustments according to hardware limitations. + +The device has a default composing rectangle, given by the +V4L2_SEL_TGT_COMPOSE_DEFAULT target. This rectangle shall cover what the driver writer considers the complete picture. It is recommended for the -driver developers to put the top/left corner at position (0,0) -. Drivers shall set the active composing rectangle to the default +driver developers to put the top/left corner at position (0,0). +Drivers shall set the active composing rectangle to the default one when the driver is first loaded. The devices may introduce additional content to video signal other than an image from memory buffers. It includes borders around an image. However, such a padded area is driver-dependent feature not covered by this document. Driver developers are encouraged to keep padded rectangle equal to active one. -The padded target is accessed by the V4L2_SEL_TGT_COMPOSE_PADDED - identifier. It must contain all pixels from the -V4L2_SEL_TGT_COMPOSE target. +The padded target is accessed by the V4L2_SEL_TGT_COMPOSE_PADDED +identifier. It must contain all pixels from the V4L2_SEL_TGT_COMPOSE +target. @@ -194,8 +193,8 @@ V4L2_SEL_TGT_COMPOSE target. Scaling control An application can detect if scaling is performed by comparing the width -and the height of rectangles obtained using V4L2_SEL_TGT_CROP - and V4L2_SEL_TGT_COMPOSE targets. If +and the height of rectangles obtained using V4L2_SEL_TGT_CROP +and V4L2_SEL_TGT_COMPOSE targets. If these are not equal then the scaling is applied. The application can compute the scaling ratios using these values. @@ -208,7 +207,7 @@ the scaling ratios using these values. Comparison with old cropping API The selection API was introduced to cope with deficiencies of previous - API , that was designed to control simple capture + API, that was designed to control simple capture devices. Later the cropping API was adopted by video output drivers. The ioctls are used to select a part of the display were the video signal is inserted. It should be considered as an API abuse because the described operation is @@ -220,7 +219,7 @@ part of an image by abusing V4L2 API. Cropping a smaller image from a larger one is achieved by setting the field &v4l2-pix-format;::bytesperline. Introducing an image offsets could be done by modifying field &v4l2-buffer;::m_userptr -before calling VIDIOC_QBUF . Those +before calling VIDIOC_QBUF. Those operations should be avoided because they are not portable (endianness), and do not work for macroblock and Bayer formats and mmap buffers. The selection API deals with configuration of buffer cropping/composing in a clear, intuitive and @@ -229,7 +228,7 @@ and constraints flags are introduced. Finally, &v4l2-crop; and &v4l2-cropcap; have no reserved fields. Therefore there is no way to extend their functionality. The new &v4l2-selection; provides a lot of place for future extensions. Driver developers are encouraged to implement only selection API. -The former cropping API would be simulated using the new one. +The former cropping API would be simulated using the new one. @@ -238,9 +237,9 @@ The former cropping API would be simulated using the new one. Resetting the cropping parameters - (A video capture device is assumed; change -V4L2_BUF_TYPE_VIDEO_CAPTURE for other devices; change target to - V4L2_SEL_TGT_COMPOSE_* family to configure composing + (A video capture device is assumed; change +V4L2_BUF_TYPE_VIDEO_CAPTURE for other devices; change target to +V4L2_SEL_TGT_COMPOSE_* family to configure composing area) @@ -292,8 +291,8 @@ area) Querying for scaling factors - A video output device is assumed; change -V4L2_BUF_TYPE_VIDEO_OUTPUT for other devices + A video output device is assumed; change +V4L2_BUF_TYPE_VIDEO_OUTPUT for other devices &v4l2-selection; compose = { diff --git a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml index b11ec75..9c04ac8 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml @@ -58,17 +58,16 @@ The ioctls are used to query and configure selection rectangles. - To query the cropping (composing) rectangle set &v4l2-selection; +To query the cropping (composing) rectangle set &v4l2-selection; type field to the respective buffer type. -Do not use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE - instead of V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE -. Use V4L2_BUF_TYPE_VIDEO_OUTPUT instead of - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE . The next step is +Do not use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE +instead of V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE. Use +V4L2_BUF_TYPE_VIDEO_OUTPUT instead of +V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE. The next step is setting the value of &v4l2-selection; target field -to V4L2_SEL_TGT_CROP ( -V4L2_SEL_TGT_COMPOSE ). Please refer to table or for additional -targets. The flags and reserved +to V4L2_SEL_TGT_CROP (V4L2_SEL_TGT_COMPOSE). +Please refer to table or +for additional targets. The flags and reserved fields of &v4l2-selection; are ignored and they must be filled with zeros. The driver fills the rest of the structure or returns &EINVAL; if incorrect buffer type or target was used. If cropping @@ -77,19 +76,18 @@ always equal to the bounds rectangle. Finally, the &v4l2-rect; r rectangle is filled with the current cropping (composing) coordinates. The coordinates are expressed in driver-dependent units. The only exception are rectangles for images in raw formats, whose -coordinates are always expressed in pixels. +coordinates are always expressed in pixels. - To change the cropping (composing) rectangle set the &v4l2-selection; +To change the cropping (composing) rectangle set the &v4l2-selection; type field to the respective buffer type. Do not -use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE - instead of V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE -. Use V4L2_BUF_TYPE_VIDEO_OUTPUT instead of - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE . The next step is +use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE +instead of V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE. Use +V4L2_BUF_TYPE_VIDEO_OUTPUT instead of +V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE. The next step is setting the value of &v4l2-selection; target to -V4L2_SEL_TGT_CROP ( -V4L2_SEL_TGT_COMPOSE ). Please refer to table or for additional -targets. The &v4l2-rect; r rectangle need to be +V4L2_SEL_TGT_CROP (V4L2_SEL_TGT_COMPOSE). +Please refer to table or +for additional targets. The &v4l2-rect; r rectangle need to be set to the desired active area. Field &v4l2-selection; reserved is ignored and must be filled with zeros. The driver may adjust coordinates of the requested rectangle. An application may @@ -149,8 +147,8 @@ On success the &v4l2-rect; r field contains the adjusted rectangle. When the parameters are unsuitable the application may modify the cropping (composing) or image parameters and repeat the cycle until satisfactory parameters have been negotiated. If constraints flags have to be -violated at then ERANGE is returned. The error indicates that there -exist no rectangle that satisfies the constraints. +violated at then ERANGE is returned. The error indicates that there +exist no rectangle that satisfies the constraints. Selection targets and flags are documented in . -- cgit v0.10.2 From cc82ab88fec7ee4151304674ec0106ce7216ac55 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Jun 2014 09:28:59 -0300 Subject: [media] DocBook media: add missing dqevent src_change field The v4l2_event union has a new src_change field, but that was never added to the VIDIOC_DQEVENT documentation of that union. Fixed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml index f3767fb..cb77325 100644 --- a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml +++ b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml @@ -100,6 +100,12 @@ + &v4l2-event-src-change; + src_change + Event data for event V4L2_EVENT_SOURCE_CHANGE. + + + __u8 data[64] Event data. Defined by the event type. The union -- cgit v0.10.2 From d9cdebd0faf2f04d1f5e06f478a7dc7a1861c970 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 17 Jun 2014 10:43:46 -0300 Subject: [media] DocBook media: fix incorrect header reference The text referred to videodev.h when videodev2.h was meant. Fixed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index 87ce7f3..12112cd 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -279,7 +279,7 @@ has just as many pad bytes after it as the other rows. In V4L2 each format has an identifier which looks like PIX_FMT_XXX, defined in the videodev.h header file. These identifiers +linkend="videodev">videodev2.h header file. These identifiers represent four character (FourCC) codes which are also listed below, however they are not the same as those used in the Windows world. -- cgit v0.10.2 From ce71bbc91e2440a399f2f96b96cfbf263a6629c1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 12 Jul 2014 07:54:26 -0300 Subject: [media] v4l2-ioctl.c: check vfl_type in ENUM_FMT The other format ioctls (g/s/try_fmt) all check if the passed buffer type makes sense for the device node's vfl_type. E.g. it makes no sense for a VBI buffer type to be passed through a video node instead of a vbi node. But this check was missing in ENUM_FMT which can cause a problem if you have both video and sdr device nodes. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index e0bafda..cd9e94c 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1098,32 +1098,34 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, { struct v4l2_fmtdesc *p = arg; struct video_device *vfd = video_devdata(file); + bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER; + bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (unlikely(!is_rx || !ops->vidioc_enum_fmt_vid_cap)) + if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap)) break; return ops->vidioc_enum_fmt_vid_cap(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (unlikely(!is_rx || !ops->vidioc_enum_fmt_vid_cap_mplane)) + if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap_mplane)) break; return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (unlikely(!is_rx || !ops->vidioc_enum_fmt_vid_overlay)) + if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_overlay)) break; return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (unlikely(!is_tx || !ops->vidioc_enum_fmt_vid_out)) + if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out)) break; return ops->vidioc_enum_fmt_vid_out(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (unlikely(!is_tx || !ops->vidioc_enum_fmt_vid_out_mplane)) + if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out_mplane)) break; return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg); case V4L2_BUF_TYPE_SDR_CAPTURE: - if (unlikely(!is_rx || !ops->vidioc_enum_fmt_sdr_cap)) + if (unlikely(!is_rx || !is_sdr || !ops->vidioc_enum_fmt_sdr_cap)) break; return ops->vidioc_enum_fmt_sdr_cap(file, fh, arg); } -- cgit v0.10.2 From a7f404af8a435b940055a04be19a3304da93a76d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Jul 2014 07:01:39 -0300 Subject: [media] v4l2-ioctl.c: fix enum_freq_bands handling If the driver supports enum_freq_bands, but only for certain device nodes, then it may return -ENOTTY. But in that case the code should fall into the fall-back case where the current tuner/modulator range is returned. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index cd9e94c..aef588c 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2042,8 +2042,11 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops, if (type != p->type) return -EINVAL; } - if (ops->vidioc_enum_freq_bands) - return ops->vidioc_enum_freq_bands(file, fh, p); + if (ops->vidioc_enum_freq_bands) { + err = ops->vidioc_enum_freq_bands(file, fh, p); + if (err != -ENOTTY) + return err; + } if (is_valid_ioctl(vfd, VIDIOC_G_TUNER)) { struct v4l2_tuner t = { .index = p->tuner, -- cgit v0.10.2 From 72be89c84c42b54f4478a5fc3048cfbae314c4d3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 10 Jul 2014 19:51:26 -0300 Subject: [media] v4l2-dev: streamon/off is only a valid ioctl for video, vbi and sdr The VIDIOC_STREAMON/OFF ioctls are not valid for radio devices, just like the other streaming I/O ioctls. Add the streamon/off ioctls to the other streaming I/O ioctls. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 1cfdbdd..fbe45b8 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -567,8 +567,6 @@ static void determine_valid_ioctls(struct video_device *vdev) set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); if (ops->vidioc_s_priority) set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); - SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); - SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); /* Note: the control handler can also be passed through the filehandle, and that can't be tested here. If the bit for these control ioctls is set, then the ioctl is valid. But if it is 0, then it can still @@ -684,6 +682,8 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); + SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); + SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); } if (is_vid || is_vbi) { -- cgit v0.10.2 From e34c4db8fe5c8d47753057b2409f9b8388f1896e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 26 Feb 2014 18:22:05 -0300 Subject: [media] videodev2.h: add V4L2_FIELD_HAS_T_OR_B macro Add a macro to test if the field consists of a single top or bottom field. Anyone who needs to work with fields as opposed to frame will need this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index b73e8cd..1f1a65c 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -124,6 +124,10 @@ enum v4l2_field { (field) == V4L2_FIELD_INTERLACED_BT ||\ (field) == V4L2_FIELD_SEQ_TB ||\ (field) == V4L2_FIELD_SEQ_BT) +#define V4L2_FIELD_HAS_T_OR_B(field) \ + ((field) == V4L2_FIELD_BOTTOM ||\ + (field) == V4L2_FIELD_TOP ||\ + (field) == V4L2_FIELD_ALTERNATE) enum v4l2_buf_type { V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, -- cgit v0.10.2 From 57fc8eb19adc794e0d267b241561c8211a50d2a8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Mar 2014 06:47:38 -0300 Subject: [media] v4l2-dev: don't debug poll unless the debug level > 2 Some applications poll a lot, so prevent the poll message from flooding the log. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index fbe45b8..33617c3 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -335,7 +335,7 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) return DEFAULT_POLLMASK; if (video_is_registered(vdev)) res = vdev->fops->poll(filp, poll); - if (vdev->debug) + if (vdev->debug > 2) printk(KERN_DEBUG "%s: poll: %08x\n", video_device_node_name(vdev), res); return res; -- cgit v0.10.2 From f9402a94e7ca0df34e7dc17d763bb49afbd89c3a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 14 Mar 2014 11:52:38 -0300 Subject: [media] v4l2-ioctl: remove pointless INFO_FL_CLEAR The edid field is the last field of the struct, so there is nothing to clear. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index aef588c..e8296ba 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2164,8 +2164,8 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)), IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0), IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, INFO_FL_CLEAR(v4l2_edid, edid)), - IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_edid, edid)), + IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, 0), + IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0), IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), -- cgit v0.10.2 From 865c4642e3750a257e4e199c2eb60495fd66f530 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Mar 2014 09:51:34 -0300 Subject: [media] v4l2-ioctl: clear reserved field of G/S_SELECTION Be sure that the reserved fields are cleared. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index e8296ba..dca8ca9 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2178,8 +2178,8 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)), IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)), IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0), - IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, INFO_FL_CLEAR(v4l2_selection, r)), + IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_selection, r)), IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0), IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0), -- cgit v0.10.2 From 9409945c7ff7ba39727df8ede2551bd22e76b58b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Jun 2014 04:31:06 -0300 Subject: [media] v4l2-ioctl: call g_selection before calling cropcap If the vidioc_cropcap op is implemented by the driver then the v4l2 core will call that directly. If g_selection is available, then the core cropcap implementation uses g_selection to fill in the bounds and defrect and it sets the pixelaspect to 1x1. But if both are available, then I would like to use g_selection to fill in defrect and bounds before calling cropcap. That way the driver's cropcap implementation doesn't have to set defrect or bounds. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index dca8ca9..46f45f0 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1842,37 +1842,41 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_cropcap *p = arg; - struct v4l2_selection s = { .type = p->type }; - int ret; - if (ops->vidioc_cropcap) - return ops->vidioc_cropcap(file, fh, p); + if (ops->vidioc_g_selection) { + struct v4l2_selection s = { .type = p->type }; + int ret; - /* obtaining bounds */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; - else - s.target = V4L2_SEL_TGT_CROP_BOUNDS; + /* obtaining bounds */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; + else + s.target = V4L2_SEL_TGT_CROP_BOUNDS; - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->bounds = s.r; + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->bounds = s.r; - /* obtaining defrect */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; - else - s.target = V4L2_SEL_TGT_CROP_DEFAULT; + /* obtaining defrect */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; + else + s.target = V4L2_SEL_TGT_CROP_DEFAULT; - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->defrect = s.r; + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->defrect = s.r; + } /* setting trivial pixelaspect */ p->pixelaspect.numerator = 1; p->pixelaspect.denominator = 1; + + if (ops->vidioc_cropcap) + return ops->vidioc_cropcap(file, fh, p); + return 0; } -- cgit v0.10.2 From e5ce558a6157e49374ba762471b77ce89ada4df0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jul 2014 18:45:45 -0300 Subject: [media] v4l2-ioctl: clips, clipcount and bitmap should not be zeroed Otherwise you cannot get the current clip and bitmap information from an overlay. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 46f45f0..e620387 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1145,6 +1145,30 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + /* + * fmt can't be cleared for these overlay types due to the 'clips' + * 'clipcount' and 'bitmap' pointers in struct v4l2_window. + * Those are provided by the user. So handle these two overlay types + * first, and then just do a simple memset for the other types. + */ + switch (p->type) { + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: { + struct v4l2_clip *clips = p->fmt.win.clips; + u32 clipcount = p->fmt.win.clipcount; + void *bitmap = p->fmt.win.bitmap; + + memset(&p->fmt, 0, sizeof(p->fmt)); + p->fmt.win.clips = clips; + p->fmt.win.clipcount = clipcount; + p->fmt.win.bitmap = bitmap; + break; + } + default: + memset(&p->fmt, 0, sizeof(p->fmt)); + break; + } + switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap)) @@ -2140,7 +2164,7 @@ struct v4l2_ioctl_info { static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0), IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)), - IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)), + IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, 0), IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)), -- cgit v0.10.2 From cce11b093f8a7f5e689f250d5b219f69a45e5db3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Jun 2014 11:15:42 -0300 Subject: [media] cx23885: add support for Hauppauge ImpactVCB-e Add support for Hauppauge model 71100: WinTV-ImpactVCB-e (PCIe, Retail, half height) Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 79f20c8..9723067 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -649,7 +649,26 @@ struct cx23885_board cx23885_boards[] = { CX25840_NONE1_CH3, .amux = CX25840_AUDIO6, } }, - } + }, + [CX23885_BOARD_HAUPPAUGE_IMPACTVCBE] = { + .name = "Hauppauge ImpactVCB-e", + .tuner_type = TUNER_ABSENT, + .porta = CX23885_ANALOG_VIDEO, + .input = {{ + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN4_CH2 | + CX25840_VIN6_CH1, + .amux = CX25840_AUDIO7, + }, { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN4_CH2 | + CX25840_VIN8_CH1 | + CX25840_SVIDEO_ON, + .amux = CX25840_AUDIO7, + } }, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -897,6 +916,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x1461, .subdevice = 0xd939, .card = CX23885_BOARD_AVERMEDIA_HC81R, + }, { + .subvendor = 0x0070, + .subdevice = 0x7133, + .card = CX23885_BOARD_HAUPPAUGE_IMPACTVCBE, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -977,6 +1000,9 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) case 71009: /* WinTV-HVR1200 (PCIe, Retail, full height) * DVB-T and basic analog */ + case 71100: + /* WinTV-ImpactVCB-e (PCIe, Retail, half height) + * Basic analog */ case 71359: /* WinTV-HVR1200 (PCIe, OEM, half height) * DVB-T and basic analog */ @@ -1701,6 +1727,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: case CX23885_BOARD_HAUPPAUGE_HVR4400: + case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -1807,6 +1834,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1200: case CX23885_BOARD_HAUPPAUGE_HVR1700: case CX23885_BOARD_HAUPPAUGE_HVR1400: + case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000: @@ -1835,6 +1863,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1800: + case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE: case CX23885_BOARD_HAUPPAUGE_HVR1800lp: case CX23885_BOARD_HAUPPAUGE_HVR1700: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index e0a5952..71e3ec3 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -507,6 +507,7 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) || (dev->board == CX23885_BOARD_MPX885) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1250) || + (dev->board == CX23885_BOARD_HAUPPAUGE_IMPACTVCBE) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) || diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 0fa4048..6a4b20e 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -96,6 +96,7 @@ #define CX23885_BOARD_TBS_6981 40 #define CX23885_BOARD_TBS_6980 41 #define CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200 42 +#define CX23885_BOARD_HAUPPAUGE_IMPACTVCBE 43 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 -- cgit v0.10.2 From 93e6a855fff6bbf95486975e193c13faba47775d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 5 Jul 2014 05:51:55 -0300 Subject: [media] hdpvr: fix reported HDTV colorspace The colorspace for HDTV is REC709, not SMPTE240M. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 4b7653f..62f289a 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -1028,7 +1028,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *_fh, f->fmt.pix.field = V4L2_FIELD_INTERLACED; } else { /* HDTV formats */ - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE240M; + f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; f->fmt.pix.field = V4L2_FIELD_NONE; } return 0; -- cgit v0.10.2 From d169e86857c075e0e27b0df6c9cff84f454b4689 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 6 Jul 2014 05:58:41 -0300 Subject: [media] staging/solo6x10: SOLO6X10 should select BITREVERSE If CONFIG_SOLO6X10=y, but CONFIG_BITREVERSE=m: drivers/built-in.o: In function `solo_osd_print': (.text+0x1c7a1f): undefined reference to `byte_rev_table' make: *** [vmlinux] Error 1 Reported-by: kbuild test robot Signed-off-by: Geert Uytterhoeven Signed-off-by: Ismael Luceno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/solo6x10/Kconfig b/drivers/staging/media/solo6x10/Kconfig index 6a1906f..1ce2819 100644 --- a/drivers/staging/media/solo6x10/Kconfig +++ b/drivers/staging/media/solo6x10/Kconfig @@ -1,6 +1,7 @@ config SOLO6X10 tristate "Bluecherry / Softlogic 6x10 capture cards (MPEG-4/H.264)" depends on PCI && VIDEO_DEV && SND && I2C + select BITREVERSE select FONT_SUPPORT select FONT_8x16 select VIDEOBUF2_DMA_SG -- cgit v0.10.2 From 3ffd7166e41de75faf6fd908c63feffa45596ea4 Mon Sep 17 00:00:00 2001 From: Andreas Weber Date: Mon, 7 Jul 2014 11:00:05 -0300 Subject: [media] DocBook media: fix number of bits filled with zeros for SRGBB12 The total number of bits is 16 (12+4). Fix it. Signed-off-by: Andreas Weber Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb12.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb12.xml index 9ba4fb6..96947f1 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-srggb12.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb12.xml @@ -18,7 +18,7 @@ Description The following four pixel formats are raw sRGB / Bayer formats with -12 bits per colour. Each colour component is stored in a 16-bit word, with 6 +12 bits per colour. Each colour component is stored in a 16-bit word, with 4 unused high bits filled with zeros. Each n-pixel row contains n/2 green samples and n/2 blue or red samples, with alternating red and blue rows. Bytes are stored in memory in little endian order. They are conventionally described -- cgit v0.10.2 From 569811160561225d05b945d829ab831b87a044b1 Mon Sep 17 00:00:00 2001 From: Andrey Utkin Date: Tue, 8 Jul 2014 12:23:32 -0300 Subject: [media] solo6x10: expose encoder quantization setting as V4L2 control solo6*10 boards have configurable quantization parameter which takes values from 0 to 31, inclusively. This change enables setting it with ioctl VIDIOC_S_CTRL with id V4L2_CID_MPEG_VIDEO_H264_MIN_QP. Signed-off-by: Andrey Utkin Signed-off-by: Ismael Luceno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index 968666a..748a73a 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -1121,6 +1121,9 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_GOP_SIZE: solo_enc->gop = ctrl->val; return 0; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + solo_enc->qp = ctrl->val; + return 0; case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: solo_enc->motion_thresh = ctrl->val << 8; if (!solo_enc->motion_global || !solo_enc->motion_enabled) @@ -1271,6 +1274,8 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, V4L2_CID_SHARPNESS, 0, 15, 1, 0); v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, solo_dev->fps); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 31, 1, SOLO_DEFAULT_QP); v4l2_ctrl_new_std_menu(hdl, &solo_ctrl_ops, V4L2_CID_DETECT_MD_MODE, V4L2_DETECT_MD_MODE_THRESHOLD_GRID, 0, -- cgit v0.10.2 From 63e9b45b03a375acce2d65191083084e01f7e60a Mon Sep 17 00:00:00 2001 From: Andrey Utkin Date: Tue, 8 Jul 2014 12:23:33 -0300 Subject: [media] solo6x10: update GOP size, QP immediately Previously, it was needed to reopen device to update GOP size and quantization parameter. Now we update device registers with new values immediately. Signed-off-by: Andrey Utkin Signed-off-by: Ismael Luceno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index 748a73a..1fd6bec 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -1120,9 +1120,13 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl) ctrl->val); case V4L2_CID_MPEG_VIDEO_GOP_SIZE: solo_enc->gop = ctrl->val; + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(solo_enc->ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(solo_enc->ch), solo_enc->gop); return 0; case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: solo_enc->qp = ctrl->val; + solo_reg_write(solo_dev, SOLO_VE_CH_QP(solo_enc->ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(solo_enc->ch), solo_enc->qp); return 0; case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: solo_enc->motion_thresh = ctrl->val << 8; -- cgit v0.10.2 From a0f7fe5b14b4c031810aae06a9cbcee3bea00a65 Mon Sep 17 00:00:00 2001 From: Anil Belur Date: Wed, 9 Jul 2014 03:06:37 -0300 Subject: [media] staging: media: bcm2048: radio-bcm2048.c - removed IRQF_DISABLED macro - this patch removes IRQF_DISABLED macro, as this is deprecated/noop. Signed-off-by: Anil Belur Acked-by: Pavel Machek Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index bbf236e..8760eca 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -2618,7 +2618,7 @@ static int bcm2048_i2c_driver_probe(struct i2c_client *client, if (client->irq) { err = request_irq(client->irq, - bcm2048_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED, + bcm2048_handler, IRQF_TRIGGER_FALLING, client->name, bdev); if (err < 0) { dev_err(&client->dev, "Could not request IRQ\n"); -- cgit v0.10.2 From 599bc36a79dc03c18360d9b5bc08c18a6dde323e Mon Sep 17 00:00:00 2001 From: Andrey Utkin Date: Thu, 10 Jul 2014 09:32:25 -0300 Subject: [media] media: pvrusb2: make logging code sane The issue was discovered by static analysis. It turns out that code is somewhat insane, being if (x) {...} else { if (x) {...} } Edited it to do the only reasonable thing, which is to log the information about the failed call. The most descriptive logging commands set is taken from original code. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=79801 Reported-by: David Binderman Signed-off-by: Andrey Utkin Acked-by: Mike Isely Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index 7c280f3..1b158f1 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -951,15 +951,9 @@ static long pvr2_v4l2_ioctl(struct file *file, if (ret < 0) { if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl failure, ret=%ld", ret); - } else { - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl failure, ret=%ld" - " command was:", ret); - v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), - cmd); - } + "pvr2_v4l2_do_ioctl failure, ret=%ld" + " command was:", ret); + v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); } } else { pvr2_trace(PVR2_TRACE_V4LIOCTL, -- cgit v0.10.2 From b317d0f585229df6407befbeb415f60bcfe74ff0 Mon Sep 17 00:00:00 2001 From: Luke Hart Date: Sat, 12 Jul 2014 15:48:22 -0300 Subject: [media] radio-bcm2048.c: Fix some checkpatch.pl errors Fix the following warnings reported in radio-bcm2048.c: WARNING: else is not generally useful after a break or return 374: FILE: radio-bcm2048.c:374: + return 0; + } else { WARNING: else is not generally useful after a break or return 728: FILE: radio-bcm2048.c:728: + return BCM2048_DE_EMPHASIS_75us; + else WARNING: unchecked sscanf return value 1974: FILE: radio-bcm2048.c:1974: + sscanf(buf, mask, &value); \ WARNING: Missing a blank line after declarations 2245: FILE: radio-bcm2048.c:2245: + unsigned char tmpbuf[3]; + tmpbuf[i] = bdev->rds_info.radio_text[bdev->rd_index+i+2]; WARNING: Possible unnecessary 'out of memory' message 2601: FILE: radio-bcm2048.c:2601: + if (!bdev) { + dev_dbg(&client->dev, "Failed to alloc video device.\n"); The following error was left since it seems to be a false positive: ERROR: Macros with complex values should be enclosed in parenthesis +#define DEFINE_SYSFS_PROPERTY(prop, signal, size, mask, check) \ +property_write(prop, signal size, mask, check) \ +property_read(prop, size, mask) Signed-off-by: Luke Hart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index 8760eca..2bba370 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -369,13 +369,12 @@ static int bcm2048_send_command(struct bcm2048_device *bdev, unsigned int reg, data[0] = reg & 0xff; data[1] = value & 0xff; - if (i2c_master_send(client, data, 2) == 2) { + if (i2c_master_send(client, data, 2) == 2) return 0; - } else { - dev_err(&bdev->client->dev, "BCM I2C error!\n"); - dev_err(&bdev->client->dev, "Is Bluetooth up and running?\n"); - return -EIO; - } + + dev_err(&bdev->client->dev, "BCM I2C error!\n"); + dev_err(&bdev->client->dev, "Is Bluetooth up and running?\n"); + return -EIO; } static int bcm2048_recv_command(struct bcm2048_device *bdev, unsigned int reg, @@ -725,8 +724,8 @@ static int bcm2048_get_fm_deemphasis(struct bcm2048_device *bdev) if (!err) { if (value & BCM2048_DE_EMPHASIS_SELECT) return BCM2048_DE_EMPHASIS_75us; - else - return BCM2048_DE_EMPHASIS_50us; + + return BCM2048_DE_EMPHASIS_50us; } return err; @@ -1971,7 +1970,8 @@ static ssize_t bcm2048_##prop##_write(struct device *dev, \ if (!bdev) \ return -ENODEV; \ \ - sscanf(buf, mask, &value); \ + if (sscanf(buf, mask, &value) != 1) \ + return -EINVAL; \ \ if (check) \ return -EDOM; \ @@ -2242,6 +2242,7 @@ static ssize_t bcm2048_fops_read(struct file *file, char __user *buf, i = 0; while (i < count) { unsigned char tmpbuf[3]; + tmpbuf[i] = bdev->rds_info.radio_text[bdev->rd_index+i+2]; tmpbuf[i+1] = bdev->rds_info.radio_text[bdev->rd_index+i+1]; tmpbuf[i+2] = ((bdev->rds_info.radio_text[bdev->rd_index+i] @@ -2598,7 +2599,6 @@ static int bcm2048_i2c_driver_probe(struct i2c_client *client, bdev = kzalloc(sizeof(*bdev), GFP_KERNEL); if (!bdev) { - dev_dbg(&client->dev, "Failed to alloc video device.\n"); err = -ENOMEM; goto exit; } -- cgit v0.10.2 From 2ec4e84a5fabfb08b03644a9f89ba1d9bb7f34b1 Mon Sep 17 00:00:00 2001 From: Himangi Saraogi Date: Tue, 15 Jul 2014 18:29:53 -0300 Subject: [media] saa7164-dvb: Remove unnecessary null test This patch removes the null test on dvb. dvb is initialized at the beginning of the function to &port->dvb. Since port is dereferenced prior to the null test, port must be a valid pointer, and &port->dvb cannot be null. The following Coccinelle script is used for detecting the change: @r@ expression e,f; identifier g,y; statement S1,S2; @@ *e = &f->g <+... f->y ...+> *if (e != NULL || ...) S1 else S2 Signed-off-by: Himangi Saraogi Acked-by: Julia Lawall Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7164/saa7164-dvb.c b/drivers/media/pci/saa7164/saa7164-dvb.c index 5c5cc3e..16ae715 100644 --- a/drivers/media/pci/saa7164/saa7164-dvb.c +++ b/drivers/media/pci/saa7164/saa7164-dvb.c @@ -242,16 +242,14 @@ static int saa7164_dvb_start_feed(struct dvb_demux_feed *feed) if (!demux->dmx.frontend) return -EINVAL; - if (dvb) { - mutex_lock(&dvb->lock); - if (dvb->feeding++ == 0) { - /* Start transport */ - ret = saa7164_dvb_start_port(port); - } - mutex_unlock(&dvb->lock); - dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", - __func__, port->nr, dvb->feeding); + mutex_lock(&dvb->lock); + if (dvb->feeding++ == 0) { + /* Start transport */ + ret = saa7164_dvb_start_port(port); } + mutex_unlock(&dvb->lock); + dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", + __func__, port->nr, dvb->feeding); return ret; } @@ -266,16 +264,14 @@ static int saa7164_dvb_stop_feed(struct dvb_demux_feed *feed) dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); - if (dvb) { - mutex_lock(&dvb->lock); - if (--dvb->feeding == 0) { - /* Stop transport */ - ret = saa7164_dvb_stop_streaming(port); - } - mutex_unlock(&dvb->lock); - dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", - __func__, port->nr, dvb->feeding); + mutex_lock(&dvb->lock); + if (--dvb->feeding == 0) { + /* Stop transport */ + ret = saa7164_dvb_stop_streaming(port); } + mutex_unlock(&dvb->lock); + dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", + __func__, port->nr, dvb->feeding); return ret; } -- cgit v0.10.2 From 0ca1ba2aac5f6b26672099b13040c5b40db93486 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 9 Jun 2014 12:21:35 -0300 Subject: [media] zoran: remove duplicate ZR050_MO_COMP define The ZR050_MO_COMP define is cut and pasted twice so we can delete the second instance. Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/zoran/zr36050.h b/drivers/media/pci/zoran/zr36050.h index 9f52f0c..ea083ad 100644 --- a/drivers/media/pci/zoran/zr36050.h +++ b/drivers/media/pci/zoran/zr36050.h @@ -126,7 +126,6 @@ struct zr36050 { /* zr36050 mode register bits */ #define ZR050_MO_COMP 0x80 -#define ZR050_MO_COMP 0x80 #define ZR050_MO_ATP 0x40 #define ZR050_MO_PASS2 0x20 #define ZR050_MO_TLM 0x10 -- cgit v0.10.2 From 69b9fe22b19b31d9de96d4a181f7206fd29ab743 Mon Sep 17 00:00:00 2001 From: Maurizio Lombardi Date: Fri, 27 Jun 2014 06:28:31 -0300 Subject: [media] s5p: fix error code path when failing to allocate DMA memory In the s5p_mfc_alloc_firmware() function there are some mistakes where the code checks whether the DMA memory is properly allocated or not. First of all dma_alloc_coherent() returns NULL in case of error. The code also checked two times fw_virt_addr, ignoring the bank2_virt pointer. Signed-off-by: Maurizio Lombardi Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index 6c3f8f7..390ca20 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -38,8 +38,7 @@ int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev) dev->fw_virt_addr = dma_alloc_coherent(dev->mem_dev_l, dev->fw_size, &dev->bank1, GFP_KERNEL); - if (IS_ERR_OR_NULL(dev->fw_virt_addr)) { - dev->fw_virt_addr = NULL; + if (!dev->fw_virt_addr) { mfc_err("Allocating bitprocessor buffer failed\n"); return -ENOMEM; } @@ -48,7 +47,7 @@ int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev) bank2_virt = dma_alloc_coherent(dev->mem_dev_r, 1 << MFC_BASE_ALIGN_ORDER, &bank2_dma_addr, GFP_KERNEL); - if (IS_ERR(dev->fw_virt_addr)) { + if (!bank2_virt) { mfc_err("Allocating bank2 base failed\n"); dma_free_coherent(dev->mem_dev_l, dev->fw_size, dev->fw_virt_addr, dev->bank1); -- cgit v0.10.2 From d19f405a5a8d2ed942b40f8cf7929a5a50d0cc59 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Thu, 10 Jul 2014 06:00:39 -0300 Subject: [media] s5p-mfc: Fix selective sclk_mfc init fc906b6d "Remove special clock usage in driver" removed initialization of MFC special clock, arguing that there's no need to do it explicitly, since it's one of MFC gate clock's dependencies and gets enabled along with it. However, there's no promise of keeping this hierarchy across Exynos SoC releases, therefore this approach fails to provide a stable, portable solution. Out of all MFC versions, only v6 doesn't use special clock at all. For other versions log a message only in case clk_get fails, as not all the devices with the same MFC version require initializing the clock explicitly. Signed-off-by: Mateusz Zalega Signed-off-by: Seung-Woo Kim Signed-off-by: Jacek Anaszewski Signed-off-by: Kyungmin Park Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 11d5f1d..b6a8be9 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c @@ -21,6 +21,8 @@ #include "s5p_mfc_pm.h" #define MFC_GATE_CLK_NAME "mfc" +#define MFC_SCLK_NAME "sclk-mfc" +#define MFC_SCLK_RATE (200 * 1000000) #define CLK_DEBUG @@ -50,6 +52,20 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) goto err_p_ip_clk; } + if (dev->variant->version != MFC_VERSION_V6) { + pm->clock = clk_get(&dev->plat_dev->dev, MFC_SCLK_NAME); + if (IS_ERR(pm->clock)) { + mfc_info("Failed to get MFC special clock control\n"); + } else { + clk_set_rate(pm->clock, MFC_SCLK_RATE); + ret = clk_prepare_enable(pm->clock); + if (ret) { + mfc_err("Failed to enable MFC special clock\n"); + goto err_s_clk; + } + } + } + atomic_set(&pm->power, 0); #ifdef CONFIG_PM_RUNTIME pm->device = &dev->plat_dev->dev; @@ -59,6 +75,9 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) atomic_set(&clk_ref, 0); #endif return 0; + +err_s_clk: + clk_put(pm->clock); err_p_ip_clk: clk_put(pm->clock_gate); err_g_ip_clk: @@ -67,6 +86,11 @@ err_g_ip_clk: void s5p_mfc_final_pm(struct s5p_mfc_dev *dev) { + if (dev->variant->version != MFC_VERSION_V6 && + pm->clock) { + clk_disable_unprepare(pm->clock); + clk_put(pm->clock); + } clk_unprepare(pm->clock_gate); clk_put(pm->clock_gate); #ifdef CONFIG_PM_RUNTIME -- cgit v0.10.2 From 1a8b381995e62e347a8bf012002a2e3e24ffb7c7 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:12 -0300 Subject: [media] coda: fix decoder I/P/B frame detection Currently the rotator unit is used to copy decoded frames out into buffers provided by videobuf2. Since the CODA reports the I/P/B frame type of the last decoded frame, and this frame will be copied out in a later device_run, depending on display order, we have to store the frame type until such time. This patch also adds the B-frame type. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 5488660..fc99d74 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -209,6 +209,7 @@ struct coda_ctx { struct coda_aux_buf psbuf; struct coda_aux_buf slicebuf; struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS]; + u32 frame_types[CODA_MAX_FRAMEBUFFERS]; struct coda_aux_buf workbuf; int num_internal_frames; int idx; @@ -2691,15 +2692,6 @@ static void coda_finish_decode(struct coda_ctx *ctx) q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - val = coda_read(dev, CODA_RET_DEC_PIC_TYPE); - if ((val & 0x7) == 0) { - dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; - dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME; - } else { - dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; - dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; - } - val = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB); if (val > 0) v4l2_err(&dev->v4l2_dev, @@ -2746,6 +2738,14 @@ static void coda_finish_decode(struct coda_ctx *ctx) } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) { v4l2_err(&dev->v4l2_dev, "decoded frame index out of range: %d\n", decoded_idx); + } else { + val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7; + if (val == 0) + ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_KEYFRAME; + else if (val == 1) + ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_PFRAME; + else + ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_BFRAME; } if (display_idx == -1) { @@ -2768,6 +2768,11 @@ static void coda_finish_decode(struct coda_ctx *ctx) dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); dst_buf->v4l2_buf.sequence = ctx->osequence++; + dst_buf->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME); + dst_buf->v4l2_buf.flags |= ctx->frame_types[ctx->display_idx]; + vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2); v4l2_m2m_buf_done(dst_buf, success ? VB2_BUF_STATE_DONE : -- cgit v0.10.2 From a500a9388ab6277a507cda0a1eb437d00bdd6840 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:13 -0300 Subject: [media] coda: fix readback of CODA_RET_DEC_SEQ_FRAME_NEED Previously we'd add one to this value, allocating one additional, superfluous internal buffer. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index fc99d74..6c9d18c 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -1887,7 +1887,7 @@ static int coda_start_decoding(struct coda_ctx *ctx) v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s instance %d now: %dx%d\n", __func__, ctx->idx, width, height); - ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED) + 1; + ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED); if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) { v4l2_err(&dev->v4l2_dev, "not enough framebuffers to decode (%d < %d)\n", -- cgit v0.10.2 From 594a750b37efb21a6967851fd19b6a9b6aca2c48 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:14 -0300 Subject: [media] coda: fix h.264 quantization parameter range If bitrate is not set, the encoder is running in VBR mode, with the I- and P-frame quantization parameters configured from userspace. For the quantization parameters, 0 is a valid value. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 6c9d18c..d777801 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -2383,9 +2383,9 @@ static int coda_ctrls_setup(struct coda_ctx *ctx) v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 25); + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 25); + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, -- cgit v0.10.2 From aed14b0a625dee567dcc5d94022ddd2951294cfc Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:15 -0300 Subject: [media] coda: fix internal framebuffer allocation size This error was introduced by 5677e3b04d3b3961200aa2bb9cc715e709eafeb9 "[media] coda: update CODA7541 to firmware 1.4.50". Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index d777801..21c272e 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -1525,10 +1525,10 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d for (i = 0; i < ctx->num_internal_frames; i++) { size_t size; - size = q_data->sizeimage; + size = ysize + ysize / 2; if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && dev->devtype->product != CODA_DX6) - ctx->internal_frames[i].size += ysize/4; + size += ysize / 4; ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i], size); if (ret < 0) { coda_free_framebuffers(ctx); -- cgit v0.10.2 From b313bcc9a46795c0233a765411cef9a15caaa7fb Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:16 -0300 Subject: [media] coda: simplify IRAM setup OVL and BTP IRAM buffers are never used, setup the bits for for DBK/BIT/IP usage depending on CODA version in one place. Also, use a simple allocator function and group IRAM addresses and size in a coda_aux_buf structure. This is done in preparation for CODA960 support. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 21c272e..31a1848 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -135,9 +135,7 @@ struct coda_dev { struct coda_aux_buf tempbuf; struct coda_aux_buf workbuf; struct gen_pool *iram_pool; - long unsigned int iram_vaddr; - long unsigned int iram_paddr; - unsigned long iram_size; + struct coda_aux_buf iram; spinlock_t irqlock; struct mutex dev_mutex; @@ -175,6 +173,8 @@ struct coda_iram_info { phys_addr_t buf_btp_use; phys_addr_t search_ram_paddr; int search_ram_size; + int remaining; + phys_addr_t next_paddr; }; struct coda_ctx { @@ -1578,23 +1578,43 @@ static int coda_h264_padding(int size, char *p) return nal_size; } +static phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size) +{ + phys_addr_t ret; + + size = round_up(size, 1024); + if (size > iram->remaining) + return 0; + iram->remaining -= size; + + ret = iram->next_paddr; + iram->next_paddr += size; + + return ret; +} + static void coda_setup_iram(struct coda_ctx *ctx) { struct coda_iram_info *iram_info = &ctx->iram_info; struct coda_dev *dev = ctx->dev; - int ipacdc_size; - int bitram_size; - int dbk_size; - int ovl_size; int mb_width; - int me_size; - int size; + int dbk_bits; + int bit_bits; + int ip_bits; memset(iram_info, 0, sizeof(*iram_info)); - size = dev->iram_size; + iram_info->next_paddr = dev->iram.paddr; + iram_info->remaining = dev->iram.size; - if (dev->devtype->product == CODA_DX6) + switch (dev->devtype->product) { + case CODA_7541: + dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE; + bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; + ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; + break; + default: /* CODA_DX6 */ return; + } if (ctx->inst_type == CODA_INST_ENCODER) { struct coda_q_data *q_data_src; @@ -1603,111 +1623,63 @@ static void coda_setup_iram(struct coda_ctx *ctx) mb_width = DIV_ROUND_UP(q_data_src->width, 16); /* Prioritize in case IRAM is too small for everything */ - me_size = round_up(round_up(q_data_src->width, 16) * 36 + 2048, - 1024); - iram_info->search_ram_size = me_size; - if (size >= iram_info->search_ram_size) { - if (dev->devtype->product == CODA_7541) - iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE; - iram_info->search_ram_paddr = dev->iram_paddr; - size -= iram_info->search_ram_size; - } else { - pr_err("IRAM is smaller than the search ram size\n"); - goto out; + if (dev->devtype->product == CODA_7541) { + iram_info->search_ram_size = round_up(mb_width * 16 * + 36 + 2048, 1024); + iram_info->search_ram_paddr = coda_iram_alloc(iram_info, + iram_info->search_ram_size); + if (!iram_info->search_ram_paddr) { + pr_err("IRAM is smaller than the search ram size\n"); + goto out; + } + iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE | + CODA7_USE_ME_ENABLE; } /* Only H.264BP and H.263P3 are considered */ - dbk_size = round_up(128 * mb_width, 1024); - if (size >= dbk_size) { - iram_info->axi_sram_use |= CODA7_USE_HOST_DBK_ENABLE; - iram_info->buf_dbk_y_use = dev->iram_paddr + - iram_info->search_ram_size; - iram_info->buf_dbk_c_use = iram_info->buf_dbk_y_use + - dbk_size / 2; - size -= dbk_size; - } else { + iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, 64 * mb_width); + iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, 64 * mb_width); + if (!iram_info->buf_dbk_c_use) goto out; - } + iram_info->axi_sram_use |= dbk_bits; - bitram_size = round_up(128 * mb_width, 1024); - if (size >= bitram_size) { - iram_info->axi_sram_use |= CODA7_USE_HOST_BIT_ENABLE; - iram_info->buf_bit_use = iram_info->buf_dbk_c_use + - dbk_size / 2; - size -= bitram_size; - } else { + iram_info->buf_bit_use = coda_iram_alloc(iram_info, 128 * mb_width); + if (!iram_info->buf_bit_use) goto out; - } + iram_info->axi_sram_use |= bit_bits; - ipacdc_size = round_up(128 * mb_width, 1024); - if (size >= ipacdc_size) { - iram_info->axi_sram_use |= CODA7_USE_HOST_IP_ENABLE; - iram_info->buf_ip_ac_dc_use = iram_info->buf_bit_use + - bitram_size; - size -= ipacdc_size; - } + iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, 128 * mb_width); + if (!iram_info->buf_ip_ac_dc_use) + goto out; + iram_info->axi_sram_use |= ip_bits; /* OVL and BTP disabled for encoder */ } else if (ctx->inst_type == CODA_INST_DECODER) { struct coda_q_data *q_data_dst; - int mb_height; q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); mb_width = DIV_ROUND_UP(q_data_dst->width, 16); - mb_height = DIV_ROUND_UP(q_data_dst->height, 16); - - dbk_size = round_up(256 * mb_width, 1024); - if (size >= dbk_size) { - iram_info->axi_sram_use |= CODA7_USE_HOST_DBK_ENABLE; - iram_info->buf_dbk_y_use = dev->iram_paddr; - iram_info->buf_dbk_c_use = dev->iram_paddr + - dbk_size / 2; - size -= dbk_size; - } else { + + iram_info->buf_dbk_y_use = coda_iram_alloc(iram_info, 128 * mb_width); + iram_info->buf_dbk_c_use = coda_iram_alloc(iram_info, 128 * mb_width); + if (!iram_info->buf_dbk_c_use) goto out; - } + iram_info->axi_sram_use |= dbk_bits; - bitram_size = round_up(128 * mb_width, 1024); - if (size >= bitram_size) { - iram_info->axi_sram_use |= CODA7_USE_HOST_BIT_ENABLE; - iram_info->buf_bit_use = iram_info->buf_dbk_c_use + - dbk_size / 2; - size -= bitram_size; - } else { + iram_info->buf_bit_use = coda_iram_alloc(iram_info, 128 * mb_width); + if (!iram_info->buf_bit_use) goto out; - } + iram_info->axi_sram_use |= bit_bits; - ipacdc_size = round_up(128 * mb_width, 1024); - if (size >= ipacdc_size) { - iram_info->axi_sram_use |= CODA7_USE_HOST_IP_ENABLE; - iram_info->buf_ip_ac_dc_use = iram_info->buf_bit_use + - bitram_size; - size -= ipacdc_size; - } else { + iram_info->buf_ip_ac_dc_use = coda_iram_alloc(iram_info, 128 * mb_width); + if (!iram_info->buf_ip_ac_dc_use) goto out; - } + iram_info->axi_sram_use |= ip_bits; - ovl_size = round_up(80 * mb_width, 1024); + /* OVL and BTP unused as there is no VC1 support yet */ } out: - switch (dev->devtype->product) { - case CODA_DX6: - break; - case CODA_7541: - /* i.MX53 uses secondary AXI for IRAM access */ - if (iram_info->axi_sram_use & CODA7_USE_HOST_BIT_ENABLE) - iram_info->axi_sram_use |= CODA7_USE_BIT_ENABLE; - if (iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE) - iram_info->axi_sram_use |= CODA7_USE_IP_ENABLE; - if (iram_info->axi_sram_use & CODA7_USE_HOST_DBK_ENABLE) - iram_info->axi_sram_use |= CODA7_USE_DBK_ENABLE; - if (iram_info->axi_sram_use & CODA7_USE_HOST_OVL_ENABLE) - iram_info->axi_sram_use |= CODA7_USE_OVL_ENABLE; - if (iram_info->axi_sram_use & CODA7_USE_HOST_ME_ENABLE) - iram_info->axi_sram_use |= CODA7_USE_ME_ENABLE; - } - if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE)) v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "IRAM smaller than needed\n"); @@ -2063,7 +2035,7 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) if (dev->devtype->product == CODA_DX6) { /* Configure the coda */ - coda_write(dev, dev->iram_paddr, CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR); + coda_write(dev, dev->iram.paddr, CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR); } /* Could set rotation here if needed */ @@ -3296,15 +3268,15 @@ static int coda_probe(struct platform_device *pdev) switch (dev->devtype->product) { case CODA_DX6: - dev->iram_size = CODADX6_IRAM_SIZE; + dev->iram.size = CODADX6_IRAM_SIZE; break; case CODA_7541: - dev->iram_size = CODA7_IRAM_SIZE; + dev->iram.size = CODA7_IRAM_SIZE; break; } - dev->iram_vaddr = (unsigned long)gen_pool_dma_alloc(dev->iram_pool, - dev->iram_size, (dma_addr_t *)&dev->iram_paddr); - if (!dev->iram_vaddr) { + dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size, + &dev->iram.paddr); + if (!dev->iram.vaddr) { dev_err(&pdev->dev, "unable to alloc iram\n"); return -ENOMEM; } @@ -3324,8 +3296,9 @@ static int coda_remove(struct platform_device *pdev) if (dev->alloc_ctx) vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); v4l2_device_unregister(&dev->v4l2_dev); - if (dev->iram_vaddr) - gen_pool_free(dev->iram_pool, dev->iram_vaddr, dev->iram_size); + if (dev->iram.vaddr) + gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr, + dev->iram.size); coda_free_aux_buf(dev, &dev->codebuf); coda_free_aux_buf(dev, &dev->tempbuf); coda_free_aux_buf(dev, &dev->workbuf); -- cgit v0.10.2 From f8600689de949f79309221be2bca36b4d2b953d1 Mon Sep 17 00:00:00 2001 From: panpan liu Date: Tue, 15 Jul 2014 22:51:33 -0300 Subject: [media] s5p-mfc: limit the size of the CPB The CPB size is limited by the hardware. Add this limit to the s_fmt. Signed-off-by: panpan liu Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 4d93835..9103258 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -436,6 +436,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); int ret = 0; struct v4l2_pix_format_mplane *pix_mp; + struct s5p_mfc_buf_size *buf_size = dev->variant->buf_size; mfc_debug_enter(); ret = vidioc_try_fmt(file, priv, f); @@ -459,11 +460,13 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_debug(2, "The codec number is: %d\n", ctx->codec_mode); pix_mp->height = 0; pix_mp->width = 0; - if (pix_mp->plane_fmt[0].sizeimage) - ctx->dec_src_buf_size = pix_mp->plane_fmt[0].sizeimage; - else + if (pix_mp->plane_fmt[0].sizeimage == 0) pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size = DEF_CPB_SIZE; + else if (pix_mp->plane_fmt[0].sizeimage > buf_size->cpb) + ctx->dec_src_buf_size = buf_size->cpb; + else + ctx->dec_src_buf_size = pix_mp->plane_fmt[0].sizeimage; pix_mp->plane_fmt[0].bytesperline = 0; ctx->state = MFCINST_INIT; ret = 0; -- cgit v0.10.2 From 632f2b0db9dabbaa5835b50a75a3a1639d6f6f38 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Sat, 5 Jul 2014 19:26:20 -0300 Subject: [media] adv7180: Remove duplicate unregister call This driver moved over to v4l2_async_unregister_subdev() but still retained a call to v4l2_unregister_subdev(). Remove. Signed-off-by: Ian Molton Signed-off-by: Ben Dooks Acked-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index ac1cdbe..821178d 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -663,7 +663,6 @@ static int adv7180_remove(struct i2c_client *client) if (state->irq > 0) free_irq(client->irq, state); - v4l2_device_unregister_subdev(sd); adv7180_exit_controls(state); mutex_destroy(&state->mutex); return 0; -- cgit v0.10.2 From 7fcec4c14b15684b0853c9ebc4090c19adc9a1f6 Mon Sep 17 00:00:00 2001 From: Andrey Utkin Date: Thu, 17 Jul 2014 13:27:16 -0300 Subject: [media] staging/media/davinci_vpfe/dm365_ipipeif.c: fix negativity check [linux-3.16-rc5/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c:210]: (style) Checking if unsigned variable 'val' is less than zero. val = get_oneshot_mode(ipipeif->input); if (val < 0) { pr_err("ipipeif: links setup required"); return -EINVAL; } but static int get_oneshot_mode(enum ipipeif_input_entity input) Introduced temporary variable for negativity check. "val" is afterwards used in a lot of bitwise operations, so changing its type to signed is not safe, according to CERT C Secure Coding Standards chapter INT13-C: "Use bitwise operators only on unsigned operands" https://www.securecoding.cert.org/confluence/display/seccode/INT13-C.+Use+bitwise+operators+only+on+unsigned+operands Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=80521 Reported-by: David Binderman Signed-off-by: Andrey Utkin Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c index 59540cd..6d4893b 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c @@ -196,6 +196,7 @@ static int ipipeif_hw_setup(struct v4l2_subdev *sd) int data_shift; int pack_mode; int source1; + int tmp; ipipeif_base_addr = ipipeif->ipipeif_base_addr; @@ -206,8 +207,8 @@ static int ipipeif_hw_setup(struct v4l2_subdev *sd) outformat = &ipipeif->formats[IPIPEIF_PAD_SOURCE]; /* Combine all the fields to make CFG1 register of IPIPEIF */ - val = get_oneshot_mode(ipipeif->input); - if (val < 0) { + tmp = val = get_oneshot_mode(ipipeif->input); + if (tmp < 0) { pr_err("ipipeif: links setup required"); return -EINVAL; } -- cgit v0.10.2 From c9adcb73b2da5eddfcb96bfa264bb61b6b83ac96 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jul 2014 15:27:39 -0300 Subject: [media] saa7146: fix compile warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fall-out from the recent struct v4l2_framebuffer change. drivers/media/common/saa7146/saa7146_fops.c: In function ‘saa7146_vv_init’: drivers/media/common/saa7146/saa7146_fops.c:536:6: warning: assignment from incompatible pointer type [enabled by default] fmt = &vv->ov_fb.fmt; ^ Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index f2cc521..d9e1d63 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -533,13 +533,12 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) saa7146_vbi_uops.init(dev,vv); - fmt = &vv->ov_fb.fmt; - fmt->width = vv->standard->h_max_out; - fmt->height = vv->standard->v_max_out; - fmt->pixelformat = V4L2_PIX_FMT_RGB565; - fmt->bytesperline = 2 * fmt->width; - fmt->sizeimage = fmt->bytesperline * fmt->height; - fmt->colorspace = V4L2_COLORSPACE_SRGB; + vv->ov_fb.fmt.width = vv->standard->h_max_out; + vv->ov_fb.fmt.height = vv->standard->v_max_out; + vv->ov_fb.fmt.pixelformat = V4L2_PIX_FMT_RGB565; + vv->ov_fb.fmt.bytesperline = 2 * vv->ov_fb.fmt.width; + vv->ov_fb.fmt.sizeimage = vv->ov_fb.fmt.bytesperline * vv->ov_fb.fmt.height; + vv->ov_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB; fmt = &vv->video_fmt; fmt->width = 384; -- cgit v0.10.2 From 605b38403b710ca01a242448241bc45c722b341b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Jun 2014 13:09:39 -0300 Subject: [media] v4l: ctrls: Move control lock/unlock above the control access functions The v4l2_ctrl_{,un}lock will be needed elsewhere. Define them before the functions that perform operations on controls. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index c630345..4546233 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -367,6 +367,24 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl, */ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl); +/** v4l2_ctrl_lock() - Helper function to lock the handler + * associated with the control. + * @ctrl: The control to lock. + */ +static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl) +{ + mutex_lock(ctrl->handler->lock); +} + +/** v4l2_ctrl_unlock() - Helper function to unlock the handler + * associated with the control. + * @ctrl: The control to unlock. + */ +static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl) +{ + mutex_unlock(ctrl->handler->lock); +} + /** v4l2_ctrl_handler_setup() - Call the s_ctrl op for all controls belonging * to the handler to initialize the hardware to the current control values. * @hdl: The control handler. @@ -622,24 +640,6 @@ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, s64 min, s64 max, u64 step, s64 def); -/** v4l2_ctrl_lock() - Helper function to lock the handler - * associated with the control. - * @ctrl: The control to lock. - */ -static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl) -{ - mutex_lock(ctrl->handler->lock); -} - -/** v4l2_ctrl_unlock() - Helper function to unlock the handler - * associated with the control. - * @ctrl: The control to unlock. - */ -static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl) -{ - mutex_unlock(ctrl->handler->lock); -} - /** v4l2_ctrl_notify() - Function to set a notify callback for a control. * @ctrl: The control. * @notify: The callback function. -- cgit v0.10.2 From 5a573925159aeec1dd159627d849dc6c66000faf Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Jun 2014 13:09:40 -0300 Subject: [media] v4l: ctrls: Provide an unlocked variant of v4l2_ctrl_modify_range() Drivers may use the v4l2_ctrl_modify_range() internally as part of other operations that need to be both serialised using a driver's lock which can also be used to serialise access to the control handler. Provide an unlocked version of the function, __v4l2_ctrl_modify_range() which then may be used by drivers for the purpose. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 8552c83..9cd9783 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3213,12 +3213,14 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void } EXPORT_SYMBOL(v4l2_ctrl_notify); -int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, +int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, s64 min, s64 max, u64 step, s64 def) { int ret; struct v4l2_ext_control c; + lockdep_assert_held(ctrl->handler->lock); + switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER64: @@ -3237,7 +3239,6 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, default: return -EINVAL; } - v4l2_ctrl_lock(ctrl); ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; @@ -3249,10 +3250,9 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE); else send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE); - v4l2_ctrl_unlock(ctrl); return ret; } -EXPORT_SYMBOL(v4l2_ctrl_modify_range); +EXPORT_SYMBOL(__v4l2_ctrl_modify_range); static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) { diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 4546233..5d1a30c 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -620,6 +620,11 @@ void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active); */ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); + +/** __v4l2_ctrl_modify_range() - Unlocked variant of v4l2_ctrl_modify_range() */ +int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, + s64 min, s64 max, u64 step, s64 def); + /** v4l2_ctrl_modify_range() - Update the range of a control. * @ctrl: The control to update. * @min: The control's minimum value. @@ -637,8 +642,17 @@ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); * This function assumes that the control handler is not locked and will * take the lock itself. */ -int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, - s64 min, s64 max, u64 step, s64 def); +static inline int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, + s64 min, s64 max, u64 step, s64 def) +{ + int rval; + + v4l2_ctrl_lock(ctrl); + rval = __v4l2_ctrl_modify_range(ctrl, min, max, step, def); + v4l2_ctrl_unlock(ctrl); + + return rval; +} /** v4l2_ctrl_notify() - Function to set a notify callback for a control. * @ctrl: The control. -- cgit v0.10.2 From 0c4348ada001181637b8f73482242166ba2fb56e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Jun 2014 13:09:42 -0300 Subject: [media] v4l: ctrls: Unlocked variants of v4l2_ctrl_s_ctrl{,_int64}() Implement unlocked variants of v4l2_ctrl_s_ctrl() and v4l2_ctrl_s_ctrl_int64(). As drivers need to set controls as they access driver internal state elsewhere than in the control framework unlocked variants of these functions become handy. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 9cd9783..1acc7aa 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3175,27 +3175,41 @@ int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control) } EXPORT_SYMBOL(v4l2_subdev_s_ctrl); -int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) +int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) { struct v4l2_ext_control c; + int rval; + + lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(!ctrl->is_int); c.value = val; - return set_ctrl_lock(NULL, ctrl, &c); + rval = set_ctrl(NULL, ctrl, &c, 0); + if (!rval) + cur_to_user(&c, ctrl); + + return rval; } -EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl); -int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) +int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) { struct v4l2_ext_control c; + int rval; + + lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); c.value64 = val; - return set_ctrl_lock(NULL, ctrl, &c); + rval = set_ctrl(NULL, ctrl, &c, 0); + if (!rval) + cur_to_user(&c, ctrl); + + return rval; } -EXPORT_SYMBOL(v4l2_ctrl_s_ctrl_int64); +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_int64); void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv) { diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 5d1a30c..8c4edd6 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -679,6 +679,8 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void */ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl); +/** __v4l2_ctrl_s_ctrl() - Unlocked variant of v4l2_ctrl_s_ctrl(). */ +int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val); /** v4l2_ctrl_s_ctrl() - Helper function to set the control's value from within a driver. * @ctrl: The control. * @val: The new value. @@ -689,7 +691,16 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl); * * This function is for integer type controls only. */ -int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val); +static inline int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) +{ + int rval; + + v4l2_ctrl_lock(ctrl); + rval = __v4l2_ctrl_s_ctrl(ctrl, val); + v4l2_ctrl_unlock(ctrl); + + return rval; +} /** v4l2_ctrl_g_ctrl_int64() - Helper function to get a 64-bit control's value from within a driver. * @ctrl: The control. @@ -702,6 +713,9 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val); */ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl); +/** __v4l2_ctrl_s_ctrl_int64() - Unlocked variant of v4l2_ctrl_s_ctrl_int64(). */ +int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val); + /** v4l2_ctrl_s_ctrl_int64() - Helper function to set a 64-bit control's value from within a driver. * @ctrl: The control. * @val: The new value. @@ -712,7 +726,16 @@ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl); * * This function is for 64-bit integer type controls only. */ -int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val); +static inline int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) +{ + int rval; + + v4l2_ctrl_lock(ctrl); + rval = __v4l2_ctrl_s_ctrl_int64(ctrl, val); + v4l2_ctrl_unlock(ctrl); + + return rval; +} /* Internal helper functions that deal with control events. */ extern const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops; -- cgit v0.10.2 From 9e9e3927ea76c95873f153c19b7c0d7f319dabc4 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 27 Jun 2014 17:42:18 -0300 Subject: [media] v4l: uapi: add SDR format RU12LE V4L2_SDR_FMT_RU12LE - Real unsigned 12-bit little endian sample inside 16-bit (2 byte). V4L2 FourCC: RU12. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 1f1a65c..e8f01cd 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -453,6 +453,7 @@ struct v4l2_pix_format { /* SDR formats - used only for Software Defined Radio devices */ #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ +#define V4L2_SDR_FMT_RU12LE v4l2_fourcc('R', 'U', '1', '2') /* real u12le */ /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe -- cgit v0.10.2 From dd6b66d039d8a8942ef7e658e6b913ec5bccd201 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 11 Jul 2014 13:14:27 -0300 Subject: [media] DocBook: V4L: add V4L2_SDR_FMT_RU12LE - 'RU12' Document V4L2_SDR_FMT_RU12LE format. It is real unsigned 12-bit little endian sample inside 16-bit space. Used by software defined radio devices. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/pixfmt-sdr-ru12le.xml b/Documentation/DocBook/media/v4l/pixfmt-sdr-ru12le.xml new file mode 100644 index 0000000..3df076b --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-sdr-ru12le.xml @@ -0,0 +1,40 @@ + + + V4L2_SDR_FMT_RU12LE ('RU12') + &manvol; + + + + V4L2_SDR_FMT_RU12LE + + Real unsigned 12-bit little endian sample + + + Description + +This format contains sequence of real number samples. Each sample is +represented as a 12 bit unsigned little endian number. Sample is stored +in 16 bit space with unused high bits padded with 0. + + + <constant>V4L2_SDR_FMT_RU12LE</constant> 1 sample + + Byte Order. + Each cell is one byte. + + + + + + start + 0: + I'0[7:0] + I'0[11:8] + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index 12112cd..808be13 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -859,6 +859,7 @@ interface only. &sub-sdr-cu08; &sub-sdr-cu16le; + &sub-sdr-ru12le; -- cgit v0.10.2 From 634fe5033951b80ef4b98d8f047cb1083d29170d Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 27 Jun 2014 21:30:55 -0300 Subject: [media] airspy: AirSpy SDR driver AirSpy SDR driver. Thanks to Youssef Touil and Benjamin Vernoux for support, help and hardware! http://airspy.com/ Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index cd2af376..5942414 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -19,6 +19,7 @@ menuconfig STAGING_MEDIA if STAGING_MEDIA # Please keep them in alphabetic order +source "drivers/staging/media/airspy/Kconfig" source "drivers/staging/media/as102/Kconfig" source "drivers/staging/media/bcm2048/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 2766a3e..730c265 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_USB_AIRSPY) += airspy/ obj-$(CONFIG_DVB_AS102) += as102/ obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ diff --git a/drivers/staging/media/airspy/Kconfig b/drivers/staging/media/airspy/Kconfig new file mode 100644 index 0000000..59a829b --- /dev/null +++ b/drivers/staging/media/airspy/Kconfig @@ -0,0 +1,5 @@ +config USB_AIRSPY + tristate "AirSpy" + depends on USB && VIDEO_DEV && VIDEO_V4L2 && I2C + select VIDEOBUF2_VMALLOC + default m if !MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/staging/media/airspy/Makefile b/drivers/staging/media/airspy/Makefile new file mode 100644 index 0000000..8d8e61c --- /dev/null +++ b/drivers/staging/media/airspy/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_AIRSPY) += airspy.o diff --git a/drivers/staging/media/airspy/airspy.c b/drivers/staging/media/airspy/airspy.c new file mode 100644 index 0000000..daecd91 --- /dev/null +++ b/drivers/staging/media/airspy/airspy.c @@ -0,0 +1,1120 @@ +/* + * AirSpy SDR driver + * + * Copyright (C) 2014 Antti Palosaari + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* AirSpy USB API commands (from AirSpy Library) */ +enum { + CMD_INVALID = 0x00, + CMD_RECEIVER_MODE = 0x01, + CMD_SI5351C_WRITE = 0x02, + CMD_SI5351C_READ = 0x03, + CMD_R820T_WRITE = 0x04, + CMD_R820T_READ = 0x05, + CMD_SPIFLASH_ERASE = 0x06, + CMD_SPIFLASH_WRITE = 0x07, + CMD_SPIFLASH_READ = 0x08, + CMD_BOARD_ID_READ = 0x09, + CMD_VERSION_STRING_READ = 0x0a, + CMD_BOARD_PARTID_SERIALNO_READ = 0x0b, + CMD_SET_SAMPLE_RATE = 0x0c, + CMD_SET_FREQ = 0x0d, + CMD_SET_LNA_GAIN = 0x0e, + CMD_SET_MIXER_GAIN = 0x0f, + CMD_SET_VGA_GAIN = 0x10, + CMD_SET_LNA_AGC = 0x11, + CMD_SET_MIXER_AGC = 0x12, + CMD_SET_PACKING = 0x13, +}; + +/* + * bEndpointAddress 0x81 EP 1 IN + * Transfer Type Bulk + * wMaxPacketSize 0x0200 1x 512 bytes + */ +#define MAX_BULK_BUFS (6) +#define BULK_BUFFER_SIZE (128 * 512) + +static const struct v4l2_frequency_band bands[] = { + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 20000000, + .rangehigh = 20000001, /* FIXME: make v4l2-compliance happy */ + }, +}; + +static const struct v4l2_frequency_band bands_rf[] = { + { + .tuner = 1, + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 24000000, + .rangehigh = 1750000000, + }, +}; + +/* stream formats */ +struct airspy_format { + char *name; + u32 pixelformat; +}; + +/* format descriptions for capture and preview */ +static struct airspy_format formats[] = { + { + .name = "Real U12LE", + .pixelformat = V4L2_SDR_FMT_RU12LE, + }, +}; + +static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); + +/* intermediate buffers with raw data from the USB device */ +struct airspy_frame_buf { + struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ + struct list_head list; +}; + +struct airspy { +#define POWER_ON (1 << 1) +#define URB_BUF (1 << 2) +#define USB_STATE_URB_BUF (1 << 3) + unsigned long flags; + + struct usb_device *udev; + struct video_device vdev; + struct v4l2_device v4l2_dev; + + /* videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + unsigned sequence; /* Buffer sequence counter */ + unsigned int vb_full; /* vb is full and packets dropped */ + + /* Note if taking both locks v4l2_lock must always be locked first! */ + struct mutex v4l2_lock; /* Protects everything else */ + struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ + + struct urb *urb_list[MAX_BULK_BUFS]; + int buf_num; + unsigned long buf_size; + u8 *buf_list[MAX_BULK_BUFS]; + dma_addr_t dma_addr[MAX_BULK_BUFS]; + int urbs_initialized; + int urbs_submitted; + + /* USB control message buffer */ + #define BUF_SIZE 24 + u8 buf[BUF_SIZE]; + + /* Current configuration */ + unsigned int f_adc; + unsigned int f_rf; + u32 pixelformat; + + /* Controls */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *lna_gain_auto; + struct v4l2_ctrl *lna_gain; + struct v4l2_ctrl *mixer_gain_auto; + struct v4l2_ctrl *mixer_gain; + struct v4l2_ctrl *if_gain; + + /* Sample rate calc */ + unsigned long jiffies_next; + unsigned int sample; + unsigned int sample_measured; +}; + +#define airspy_dbg_usb_control_msg(_udev, _r, _t, _v, _i, _b, _l) { \ + char *_direction; \ + if (_t & USB_DIR_IN) \ + _direction = "<<<"; \ + else \ + _direction = ">>>"; \ + dev_dbg(&_udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ + "%s %*ph\n", __func__, _t, _r, _v & 0xff, _v >> 8, \ + _i & 0xff, _i >> 8, _l & 0xff, _l >> 8, _direction, \ + _l, _b); \ +} + +/* execute firmware command */ +static int airspy_ctrl_msg(struct airspy *s, u8 request, u16 value, u16 index, + u8 *data, u16 size) +{ + int ret; + unsigned int pipe; + u8 requesttype; + + switch (request) { + case CMD_RECEIVER_MODE: + case CMD_SET_FREQ: + pipe = usb_sndctrlpipe(s->udev, 0); + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + break; + case CMD_BOARD_ID_READ: + case CMD_VERSION_STRING_READ: + case CMD_BOARD_PARTID_SERIALNO_READ: + case CMD_SET_LNA_GAIN: + case CMD_SET_MIXER_GAIN: + case CMD_SET_VGA_GAIN: + case CMD_SET_LNA_AGC: + case CMD_SET_MIXER_AGC: + pipe = usb_rcvctrlpipe(s->udev, 0); + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + break; + default: + dev_err(&s->udev->dev, "Unknown command %02x\n", request); + ret = -EINVAL; + goto err; + } + + /* write request */ + if (!(requesttype & USB_DIR_IN)) + memcpy(s->buf, data, size); + + ret = usb_control_msg(s->udev, pipe, request, requesttype, value, + index, s->buf, size, 1000); + airspy_dbg_usb_control_msg(s->udev, request, requesttype, value, + index, s->buf, size); + if (ret < 0) { + dev_err(&s->udev->dev, + "usb_control_msg() failed %d request %02x\n", + ret, request); + goto err; + } + + /* read request */ + if (requesttype & USB_DIR_IN) + memcpy(data, s->buf, size); + + return 0; +err: + return ret; +} + +/* Private functions */ +static struct airspy_frame_buf *airspy_get_next_fill_buf(struct airspy *s) +{ + unsigned long flags = 0; + struct airspy_frame_buf *buf = NULL; + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + if (list_empty(&s->queued_bufs)) + goto leave; + + buf = list_entry(s->queued_bufs.next, + struct airspy_frame_buf, list); + list_del(&buf->list); +leave: + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); + return buf; +} + +static unsigned int airspy_convert_stream(struct airspy *s, + void *dst, void *src, unsigned int src_len) +{ + unsigned int dst_len; + + if (s->pixelformat == V4L2_SDR_FMT_RU12LE) { + memcpy(dst, src, src_len); + dst_len = src_len; + } else { + dst_len = 0; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if (unlikely(time_is_before_jiffies(s->jiffies_next))) { + #define MSECS 10000UL + unsigned int samples = s->sample - s->sample_measured; + s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); + s->sample_measured = s->sample; + dev_dbg(&s->udev->dev, + "slen=%d samples=%u msecs=%lu sample rate=%lu\n", + src_len, samples, MSECS, + samples * 1000UL / MSECS); + } + + /* total number of samples */ + s->sample += src_len / 2; + + return dst_len; +} + +/* + * This gets called for the bulk stream pipe. This is done in interrupt + * time, so it has to be fast, not crash, and not stall. Neat. + */ +static void airspy_urb_complete(struct urb *urb) +{ + struct airspy *s = urb->context; + struct airspy_frame_buf *fbuf; + + dev_dbg_ratelimited(&s->udev->dev, + "%s: status=%d length=%d/%d errors=%d\n", + __func__, urb->status, urb->actual_length, + urb->transfer_buffer_length, urb->error_count); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dev_err_ratelimited(&s->udev->dev, "URB failed %d\n", + urb->status); + break; + } + + if (likely(urb->actual_length > 0)) { + void *ptr; + unsigned int len; + /* get free framebuffer */ + fbuf = airspy_get_next_fill_buf(s); + if (unlikely(fbuf == NULL)) { + s->vb_full++; + dev_notice_ratelimited(&s->udev->dev, + "videobuf is full, %d packets dropped\n", + s->vb_full); + goto skip; + } + + /* fill framebuffer */ + ptr = vb2_plane_vaddr(&fbuf->vb, 0); + len = airspy_convert_stream(s, ptr, urb->transfer_buffer, + urb->actual_length); + vb2_set_plane_payload(&fbuf->vb, 0, len); + v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp); + fbuf->vb.v4l2_buf.sequence = s->sequence++; + vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + } +skip: + usb_submit_urb(urb, GFP_ATOMIC); +} + +static int airspy_kill_urbs(struct airspy *s) +{ + int i; + + for (i = s->urbs_submitted - 1; i >= 0; i--) { + dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i); + /* stop the URB */ + usb_kill_urb(s->urb_list[i]); + } + s->urbs_submitted = 0; + + return 0; +} + +static int airspy_submit_urbs(struct airspy *s) +{ + int i, ret; + + for (i = 0; i < s->urbs_initialized; i++) { + dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i); + ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC); + if (ret) { + dev_err(&s->udev->dev, + "Could not submit URB no. %d - get them all back\n", + i); + airspy_kill_urbs(s); + return ret; + } + s->urbs_submitted++; + } + + return 0; +} + +static int airspy_free_stream_bufs(struct airspy *s) +{ + if (s->flags & USB_STATE_URB_BUF) { + while (s->buf_num) { + s->buf_num--; + dev_dbg(&s->udev->dev, "%s: free buf=%d\n", + __func__, s->buf_num); + usb_free_coherent(s->udev, s->buf_size, + s->buf_list[s->buf_num], + s->dma_addr[s->buf_num]); + } + } + s->flags &= ~USB_STATE_URB_BUF; + + return 0; +} + +static int airspy_alloc_stream_bufs(struct airspy *s) +{ + s->buf_num = 0; + s->buf_size = BULK_BUFFER_SIZE; + + dev_dbg(&s->udev->dev, + "%s: all in all I will use %u bytes for streaming\n", + __func__, MAX_BULK_BUFS * BULK_BUFFER_SIZE); + + for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) { + s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev, + BULK_BUFFER_SIZE, GFP_ATOMIC, + &s->dma_addr[s->buf_num]); + if (!s->buf_list[s->buf_num]) { + dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n", + __func__, s->buf_num); + airspy_free_stream_bufs(s); + return -ENOMEM; + } + + dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n", + __func__, s->buf_num, + s->buf_list[s->buf_num], + (long long)s->dma_addr[s->buf_num]); + s->flags |= USB_STATE_URB_BUF; + } + + return 0; +} + +static int airspy_free_urbs(struct airspy *s) +{ + int i; + + airspy_kill_urbs(s); + + for (i = s->urbs_initialized - 1; i >= 0; i--) { + if (s->urb_list[i]) { + dev_dbg(&s->udev->dev, "%s: free urb=%d\n", + __func__, i); + /* free the URBs */ + usb_free_urb(s->urb_list[i]); + } + } + s->urbs_initialized = 0; + + return 0; +} + +static int airspy_alloc_urbs(struct airspy *s) +{ + int i, j; + + /* allocate the URBs */ + for (i = 0; i < MAX_BULK_BUFS; i++) { + dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i); + s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + if (!s->urb_list[i]) { + dev_dbg(&s->udev->dev, "%s: failed\n", __func__); + for (j = 0; j < i; j++) + usb_free_urb(s->urb_list[j]); + return -ENOMEM; + } + usb_fill_bulk_urb(s->urb_list[i], + s->udev, + usb_rcvbulkpipe(s->udev, 0x81), + s->buf_list[i], + BULK_BUFFER_SIZE, + airspy_urb_complete, s); + + s->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + s->urb_list[i]->transfer_dma = s->dma_addr[i]; + s->urbs_initialized++; + } + + return 0; +} + +/* Must be called with vb_queue_lock hold */ +static void airspy_cleanup_queued_bufs(struct airspy *s) +{ + unsigned long flags = 0; + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + while (!list_empty(&s->queued_bufs)) { + struct airspy_frame_buf *buf; + buf = list_entry(s->queued_bufs.next, + struct airspy_frame_buf, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); +} + +/* The user yanked out the cable... */ +static void airspy_disconnect(struct usb_interface *intf) +{ + struct v4l2_device *v = usb_get_intfdata(intf); + struct airspy *s = container_of(v, struct airspy, v4l2_dev); + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + mutex_lock(&s->vb_queue_lock); + mutex_lock(&s->v4l2_lock); + /* No need to keep the urbs around after disconnection */ + s->udev = NULL; + v4l2_device_disconnect(&s->v4l2_dev); + video_unregister_device(&s->vdev); + mutex_unlock(&s->v4l2_lock); + mutex_unlock(&s->vb_queue_lock); + + v4l2_device_put(&s->v4l2_dev); +} + +/* Videobuf2 operations */ +static int airspy_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) +{ + struct airspy *s = vb2_get_drv_priv(vq); + + dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); + + /* Need at least 8 buffers */ + if (vq->num_buffers + *nbuffers < 8) + *nbuffers = 8 - vq->num_buffers; + *nplanes = 1; + sizes[0] = PAGE_ALIGN(BULK_BUFFER_SIZE); + + dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", + __func__, *nbuffers, sizes[0]); + return 0; +} + +static void airspy_buf_queue(struct vb2_buffer *vb) +{ + struct airspy *s = vb2_get_drv_priv(vb->vb2_queue); + struct airspy_frame_buf *buf = + container_of(vb, struct airspy_frame_buf, vb); + unsigned long flags = 0; + + /* Check the device has not disconnected between prep and queuing */ + if (unlikely(!s->udev)) { + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + return; + } + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + list_add_tail(&buf->list, &s->queued_bufs); + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); +} + +static int airspy_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct airspy *s = vb2_get_drv_priv(vq); + int ret; + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + if (!s->udev) + return -ENODEV; + + mutex_lock(&s->v4l2_lock); + + set_bit(POWER_ON, &s->flags); + + s->sequence = 0; + + ret = airspy_alloc_stream_bufs(s); + if (ret) + goto err; + + ret = airspy_alloc_urbs(s); + if (ret) + goto err; + + ret = airspy_submit_urbs(s); + if (ret) + goto err; + + /* start hardware streaming */ + ret = airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 1, 0, NULL, 0); + if (ret) + goto err; +err: + mutex_unlock(&s->v4l2_lock); + + return ret; +} + +static void airspy_stop_streaming(struct vb2_queue *vq) +{ + struct airspy *s = vb2_get_drv_priv(vq); + int ret; + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + mutex_lock(&s->v4l2_lock); + + /* stop hardware streaming */ + ret = airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 0, 0, NULL, 0); + + airspy_kill_urbs(s); + airspy_free_urbs(s); + airspy_free_stream_bufs(s); + + airspy_cleanup_queued_bufs(s); + + clear_bit(POWER_ON, &s->flags); + + mutex_unlock(&s->v4l2_lock); +} + +static struct vb2_ops airspy_vb2_ops = { + .queue_setup = airspy_queue_setup, + .buf_queue = airspy_buf_queue, + .start_streaming = airspy_start_streaming, + .stop_streaming = airspy_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int airspy_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct airspy *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); + usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int airspy_enum_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct airspy *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index); + + if (f->index >= NUM_FORMATS) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, sizeof(f->description)); + f->pixelformat = formats[f->index].pixelformat; + + return 0; +} + +static int airspy_g_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct airspy *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + (char *)&s->pixelformat); + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + f->fmt.sdr.pixelformat = s->pixelformat; + + return 0; +} + +static int airspy_s_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct airspy *s = video_drvdata(file); + struct vb2_queue *q = &s->vb_queue; + int i; + + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + (char *)&f->fmt.sdr.pixelformat); + + if (vb2_is_busy(q)) + return -EBUSY; + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + s->pixelformat = f->fmt.sdr.pixelformat; + return 0; + } + } + + f->fmt.sdr.pixelformat = formats[0].pixelformat; + s->pixelformat = formats[0].pixelformat; + + return 0; +} + +static int airspy_try_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct airspy *s = video_drvdata(file); + int i; + + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + (char *)&f->fmt.sdr.pixelformat); + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) + return 0; + } + + f->fmt.sdr.pixelformat = formats[0].pixelformat; + + return 0; +} + +static int airspy_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *v) +{ + struct airspy *s = video_drvdata(file); + int ret; + + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); + + if (v->index == 0) + ret = 0; + else if (v->index == 1) + ret = 0; + else + ret = -EINVAL; + + return ret; +} + +static int airspy_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) +{ + struct airspy *s = video_drvdata(file); + int ret; + + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); + + if (v->index == 0) { + strlcpy(v->name, "AirSpy ADC", sizeof(v->name)); + v->type = V4L2_TUNER_ADC; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = bands[0].rangelow; + v->rangehigh = bands[0].rangehigh; + ret = 0; + } else if (v->index == 1) { + strlcpy(v->name, "AirSpy RF", sizeof(v->name)); + v->type = V4L2_TUNER_RF; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = bands_rf[0].rangelow; + v->rangehigh = bands_rf[0].rangehigh; + ret = 0; + } else { + ret = -EINVAL; + } + + return ret; +} + +static int airspy_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct airspy *s = video_drvdata(file); + int ret = 0; + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", + __func__, f->tuner, f->type); + + if (f->tuner == 0) { + f->type = V4L2_TUNER_ADC; + f->frequency = s->f_adc; + ret = 0; + } else if (f->tuner == 1) { + f->type = V4L2_TUNER_RF; + f->frequency = s->f_rf; + } else { + ret = -EINVAL; + } + + return ret; +} + +static int airspy_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct airspy *s = video_drvdata(file); + int ret; + u8 buf[4]; + + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", + __func__, f->tuner, f->type, f->frequency); + + if (f->tuner == 0) { + s->f_adc = clamp_t(unsigned int, f->frequency, + bands[0].rangelow, + bands[0].rangehigh); + dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n", + __func__, s->f_adc); + ret = 0; + } else if (f->tuner == 1) { + s->f_rf = clamp_t(unsigned int, f->frequency, + bands_rf[0].rangelow, + bands_rf[0].rangehigh); + dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n", + __func__, s->f_rf); + buf[0] = (s->f_rf >> 0) & 0xff; + buf[1] = (s->f_rf >> 8) & 0xff; + buf[2] = (s->f_rf >> 16) & 0xff; + buf[3] = (s->f_rf >> 24) & 0xff; + ret = airspy_ctrl_msg(s, CMD_SET_FREQ, 0, 0, buf, 4); + } else { + ret = -EINVAL; + } + + return ret; +} + +static int airspy_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + struct airspy *s = video_drvdata(file); + int ret; + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", + __func__, band->tuner, band->type, band->index); + + if (band->tuner == 0) { + if (band->index >= ARRAY_SIZE(bands)) { + ret = -EINVAL; + } else { + *band = bands[band->index]; + ret = 0; + } + } else if (band->tuner == 1) { + if (band->index >= ARRAY_SIZE(bands_rf)) { + ret = -EINVAL; + } else { + *band = bands_rf[band->index]; + ret = 0; + } + } else { + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ioctl_ops airspy_ioctl_ops = { + .vidioc_querycap = airspy_querycap, + + .vidioc_enum_fmt_sdr_cap = airspy_enum_fmt_sdr_cap, + .vidioc_g_fmt_sdr_cap = airspy_g_fmt_sdr_cap, + .vidioc_s_fmt_sdr_cap = airspy_s_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = airspy_try_fmt_sdr_cap, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_g_tuner = airspy_g_tuner, + .vidioc_s_tuner = airspy_s_tuner, + + .vidioc_g_frequency = airspy_g_frequency, + .vidioc_s_frequency = airspy_s_frequency, + .vidioc_enum_freq_bands = airspy_enum_freq_bands, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_log_status = v4l2_ctrl_log_status, +}; + +static const struct v4l2_file_operations airspy_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static struct video_device airspy_template = { + .name = "AirSpy SDR", + .release = video_device_release_empty, + .fops = &airspy_fops, + .ioctl_ops = &airspy_ioctl_ops, +}; + +static void airspy_video_release(struct v4l2_device *v) +{ + struct airspy *s = container_of(v, struct airspy, v4l2_dev); + + v4l2_ctrl_handler_free(&s->hdl); + v4l2_device_unregister(&s->v4l2_dev); + kfree(s); +} + +static int airspy_set_lna_gain(struct airspy *s) +{ + int ret; + u8 u8tmp; + + dev_dbg(&s->udev->dev, "%s: lna auto=%d->%d val=%d->%d\n", + __func__, s->lna_gain_auto->cur.val, + s->lna_gain_auto->val, s->lna_gain->cur.val, + s->lna_gain->val); + + ret = airspy_ctrl_msg(s, CMD_SET_LNA_AGC, 0, s->lna_gain_auto->val, + &u8tmp, 1); + if (ret) + goto err; + + if (s->lna_gain_auto->val == false) { + ret = airspy_ctrl_msg(s, CMD_SET_LNA_GAIN, 0, s->lna_gain->val, + &u8tmp, 1); + if (ret) + goto err; + } +err: + if (ret) + dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int airspy_set_mixer_gain(struct airspy *s) +{ + int ret; + u8 u8tmp; + + dev_dbg(&s->udev->dev, "%s: mixer auto=%d->%d val=%d->%d\n", + __func__, s->mixer_gain_auto->cur.val, + s->mixer_gain_auto->val, s->mixer_gain->cur.val, + s->mixer_gain->val); + + ret = airspy_ctrl_msg(s, CMD_SET_MIXER_AGC, 0, s->mixer_gain_auto->val, + &u8tmp, 1); + if (ret) + goto err; + + if (s->mixer_gain_auto->val == false) { + ret = airspy_ctrl_msg(s, CMD_SET_MIXER_GAIN, 0, + s->mixer_gain->val, &u8tmp, 1); + if (ret) + goto err; + } +err: + if (ret) + dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int airspy_set_if_gain(struct airspy *s) +{ + int ret; + u8 u8tmp; + + dev_dbg(&s->udev->dev, "%s: val=%d->%d\n", + __func__, s->if_gain->cur.val, s->if_gain->val); + + ret = airspy_ctrl_msg(s, CMD_SET_VGA_GAIN, 0, s->if_gain->val, + &u8tmp, 1); + if (ret) + goto err; +err: + if (ret) + dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int airspy_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct airspy *s = container_of(ctrl->handler, struct airspy, hdl); + int ret; + + switch (ctrl->id) { + case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: + case V4L2_CID_RF_TUNER_LNA_GAIN: + ret = airspy_set_lna_gain(s); + break; + case V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO: + case V4L2_CID_RF_TUNER_MIXER_GAIN: + ret = airspy_set_mixer_gain(s); + break; + case V4L2_CID_RF_TUNER_IF_GAIN: + ret = airspy_set_if_gain(s); + break; + default: + dev_dbg(&s->udev->dev, "%s: unknown ctrl: id=%d name=%s\n", + __func__, ctrl->id, ctrl->name); + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ctrl_ops airspy_ctrl_ops = { + .s_ctrl = airspy_s_ctrl, +}; + +static int airspy_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct airspy *s = NULL; + int ret; + u8 u8tmp, buf[BUF_SIZE]; + + s = kzalloc(sizeof(struct airspy), GFP_KERNEL); + if (s == NULL) { + dev_err(&udev->dev, + "Could not allocate memory for airspy state\n"); + return -ENOMEM; + } + + mutex_init(&s->v4l2_lock); + mutex_init(&s->vb_queue_lock); + spin_lock_init(&s->queued_bufs_lock); + INIT_LIST_HEAD(&s->queued_bufs); + s->udev = udev; + s->f_adc = bands[0].rangelow; + s->f_rf = bands_rf[0].rangelow; + s->pixelformat = V4L2_SDR_FMT_RU12LE; + + /* Detect device */ + ret = airspy_ctrl_msg(s, CMD_BOARD_ID_READ, 0, 0, &u8tmp, 1); + if (ret == 0) + ret = airspy_ctrl_msg(s, CMD_VERSION_STRING_READ, 0, 0, + buf, BUF_SIZE); + if (ret) { + dev_err(&s->udev->dev, "Could not detect board\n"); + goto err_free_mem; + } + + buf[BUF_SIZE - 1] = '\0'; + + dev_info(&s->udev->dev, "Board ID: %02x\n", u8tmp); + dev_info(&s->udev->dev, "Firmware version: %s\n", buf); + + /* Init videobuf2 queue structure */ + s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; + s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + s->vb_queue.drv_priv = s; + s->vb_queue.buf_struct_size = sizeof(struct airspy_frame_buf); + s->vb_queue.ops = &airspy_vb2_ops; + s->vb_queue.mem_ops = &vb2_vmalloc_memops; + s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&s->vb_queue); + if (ret) { + dev_err(&s->udev->dev, "Could not initialize vb2 queue\n"); + goto err_free_mem; + } + + /* Init video_device structure */ + s->vdev = airspy_template; + s->vdev.queue = &s->vb_queue; + s->vdev.queue->lock = &s->vb_queue_lock; + set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev.flags); + video_set_drvdata(&s->vdev, s); + + /* Register the v4l2_device structure */ + s->v4l2_dev.release = airspy_video_release; + ret = v4l2_device_register(&intf->dev, &s->v4l2_dev); + if (ret) { + dev_err(&s->udev->dev, + "Failed to register v4l2-device (%d)\n", ret); + goto err_free_mem; + } + + /* Register controls */ + v4l2_ctrl_handler_init(&s->hdl, 5); + s->lna_gain_auto = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, + V4L2_CID_RF_TUNER_LNA_GAIN_AUTO, 0, 1, 1, 0); + s->lna_gain = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, + V4L2_CID_RF_TUNER_LNA_GAIN, 0, 14, 1, 8); + v4l2_ctrl_auto_cluster(2, &s->lna_gain_auto, 0, false); + s->mixer_gain_auto = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, + V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO, 0, 1, 1, 0); + s->mixer_gain = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, + V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 15, 1, 8); + v4l2_ctrl_auto_cluster(2, &s->mixer_gain_auto, 0, false); + s->if_gain = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, + V4L2_CID_RF_TUNER_IF_GAIN, 0, 15, 1, 0); + if (s->hdl.error) { + ret = s->hdl.error; + dev_err(&s->udev->dev, "Could not initialize controls\n"); + goto err_free_controls; + } + + v4l2_ctrl_handler_setup(&s->hdl); + + s->v4l2_dev.ctrl_handler = &s->hdl; + s->vdev.v4l2_dev = &s->v4l2_dev; + s->vdev.lock = &s->v4l2_lock; + + ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); + if (ret) { + dev_err(&s->udev->dev, + "Failed to register as video device (%d)\n", + ret); + goto err_unregister_v4l2_dev; + } + dev_info(&s->udev->dev, "Registered as %s\n", + video_device_node_name(&s->vdev)); + + return 0; + +err_free_controls: + v4l2_ctrl_handler_free(&s->hdl); +err_unregister_v4l2_dev: + v4l2_device_unregister(&s->v4l2_dev); +err_free_mem: + kfree(s); + return ret; +} + +/* USB device ID list */ +static struct usb_device_id airspy_id_table[] = { + { USB_DEVICE(0x1d50, 0x60a1) }, /* AirSpy */ + { } +}; +MODULE_DEVICE_TABLE(usb, airspy_id_table); + +/* USB subsystem interface */ +static struct usb_driver airspy_driver = { + .name = KBUILD_MODNAME, + .probe = airspy_probe, + .disconnect = airspy_disconnect, + .id_table = airspy_id_table, +}; + +module_usb_driver(airspy_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("AirSpy SDR"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From c0c03886763f4d5ccfc5d854fd8dfe77814753be Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 11 Jul 2014 13:25:14 -0300 Subject: [media] v4l: uapi: add SDR format CS8 V4L2_SDR_FMT_CS8 - Complex signed 8-bit IQ sample Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index e8f01cd..e943e4c 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -453,6 +453,7 @@ struct v4l2_pix_format { /* SDR formats - used only for Software Defined Radio devices */ #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ +#define V4L2_SDR_FMT_CS8 v4l2_fourcc('C', 'S', '0', '8') /* complex s8 */ #define V4L2_SDR_FMT_RU12LE v4l2_fourcc('R', 'U', '1', '2') /* real u12le */ /* priv field value to indicates that subsequent fields are valid. */ -- cgit v0.10.2 From fd1e70bffd30d7487342a72bb4a03287bc00058a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 11 Jul 2014 15:35:05 -0300 Subject: [media] DocBook: V4L: add V4L2_SDR_FMT_CS8 - 'CS08' V4L2_SDR_FMT_CS8 is complex signed 8-bit sample format, used for software defined radio devices. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/pixfmt-sdr-cs08.xml b/Documentation/DocBook/media/v4l/pixfmt-sdr-cs08.xml new file mode 100644 index 0000000..6118d8f --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-sdr-cs08.xml @@ -0,0 +1,44 @@ + + + V4L2_SDR_FMT_CS8 ('CS08') + &manvol; + + + + V4L2_SDR_FMT_CS8 + + Complex signed 8-bit IQ sample + + + Description + +This format contains sequence of complex number samples. Each complex number +consist two parts, called In-phase and Quadrature (IQ). Both I and Q are +represented as a 8 bit signed number. I value comes first and Q value after +that. + + + <constant>V4L2_SDR_FMT_CS8</constant> 1 sample + + Byte Order. + Each cell is one byte. + + + + + + start + 0: + I'0 + + + start + 1: + Q'0 + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index 808be13..0cd0c7a 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -859,6 +859,7 @@ interface only. &sub-sdr-cu08; &sub-sdr-cu16le; + &sub-sdr-cs08; &sub-sdr-ru12le; -- cgit v0.10.2 From 5c5be570043f864b8373ed26fd68698fe352a0ef Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 11 Jul 2014 16:35:47 -0300 Subject: [media] v4l: uapi: add SDR format CS14 V4L2_SDR_FMT_CS14LE - Complex signed 14-bit IQ sample Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index e943e4c..6da302d 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -454,6 +454,7 @@ struct v4l2_pix_format { #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ #define V4L2_SDR_FMT_CS8 v4l2_fourcc('C', 'S', '0', '8') /* complex s8 */ +#define V4L2_SDR_FMT_CS14LE v4l2_fourcc('C', 'S', '1', '4') /* complex s14le */ #define V4L2_SDR_FMT_RU12LE v4l2_fourcc('R', 'U', '1', '2') /* real u12le */ /* priv field value to indicates that subsequent fields are valid. */ -- cgit v0.10.2 From 565092c6cde439f2cd377673e75ea91facd1ec88 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 11 Jul 2014 17:04:42 -0300 Subject: [media] DocBook: V4L: add V4L2_SDR_FMT_CS14LE - 'CS14' V4L2_SDR_FMT_CS14LE is complex signed 14-bit sample format, used for software defined radio devices. [m.chehab@samsung.com: remove V4L2_FL_USE_FH_PRIO setting to avoid compilation breakage, as this flag doesn't exist anymore] Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/pixfmt-sdr-cs14le.xml b/Documentation/DocBook/media/v4l/pixfmt-sdr-cs14le.xml new file mode 100644 index 0000000..e4b494c --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-sdr-cs14le.xml @@ -0,0 +1,47 @@ + + + V4L2_SDR_FMT_CS14LE ('CS14') + &manvol; + + + + V4L2_SDR_FMT_CS14LE + + Complex signed 14-bit little endian IQ sample + + + Description + +This format contains sequence of complex number samples. Each complex number +consist two parts, called In-phase and Quadrature (IQ). Both I and Q are +represented as a 14 bit signed little endian number. I value comes first +and Q value after that. 14 bit value is stored in 16 bit space with unused +high bits padded with 0. + + + <constant>V4L2_SDR_FMT_CS14LE</constant> 1 sample + + Byte Order. + Each cell is one byte. + + + + + + start + 0: + I'0[7:0] + I'0[13:8] + + + start + 2: + Q'0[7:0] + Q'0[13:8] + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index 0cd0c7a..df5b23d 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -860,6 +860,7 @@ interface only. &sub-sdr-cu08; &sub-sdr-cu16le; &sub-sdr-cs08; + &sub-sdr-cs14le; &sub-sdr-ru12le; diff --git a/drivers/staging/media/airspy/airspy.c b/drivers/staging/media/airspy/airspy.c index daecd91..0875808 100644 --- a/drivers/staging/media/airspy/airspy.c +++ b/drivers/staging/media/airspy/airspy.c @@ -1039,7 +1039,6 @@ static int airspy_probe(struct usb_interface *intf, s->vdev = airspy_template; s->vdev.queue = &s->vb_queue; s->vdev.queue->lock = &s->vb_queue_lock; - set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev.flags); video_set_drvdata(&s->vdev, s); /* Register the v4l2_device structure */ -- cgit v0.10.2 From d9d889d3249a6fee8ba61d0ea0e6f0a8e857c832 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Jul 2014 20:29:54 -0300 Subject: staging/airspy: fix a compilation warning drivers/staging/media/airspy/airspy.c: In function 'airspy_stop_streaming': drivers/staging/media/airspy/airspy.c:569:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] int ret; ^ Cc: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/airspy/airspy.c b/drivers/staging/media/airspy/airspy.c index 0875808..91857e7 100644 --- a/drivers/staging/media/airspy/airspy.c +++ b/drivers/staging/media/airspy/airspy.c @@ -566,14 +566,13 @@ err: static void airspy_stop_streaming(struct vb2_queue *vq) { struct airspy *s = vb2_get_drv_priv(vq); - int ret; dev_dbg(&s->udev->dev, "%s:\n", __func__); mutex_lock(&s->v4l2_lock); /* stop hardware streaming */ - ret = airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 0, 0, NULL, 0); + airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 0, 0, NULL, 0); airspy_kill_urbs(s); airspy_free_urbs(s); -- cgit v0.10.2 From 150dcf5575a937d88ef137f8cddc4f8bd7bb9c8c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 12 Jul 2014 14:53:22 +0300 Subject: msi001: move out of staging Move MSi001 driver from staging to drivers/media/tuners/. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 22b6b8b..906461d 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -71,6 +71,12 @@ config MEDIA_TUNER_TEA5767 help Say Y here to include support for the Philips TEA5767 radio tuner. +config MEDIA_TUNER_MSI001 + tristate "Mirics MSi001" + depends on MEDIA_SUPPORT && SPI && VIDEO_V4L2 + help + Mirics MSi001 silicon tuner driver. + config MEDIA_TUNER_MT20XX tristate "Microtune 2032 / 2050 tuners" depends on MEDIA_SUPPORT && I2C diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile index a6ff0c6..5591699 100644 --- a/drivers/media/tuners/Makefile +++ b/drivers/media/tuners/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o obj-$(CONFIG_MEDIA_TUNER_XC4000) += xc4000.o +obj-$(CONFIG_MEDIA_TUNER_MSI001) += msi001.o obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o obj-$(CONFIG_MEDIA_TUNER_MT2063) += mt2063.o obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o diff --git a/drivers/media/tuners/msi001.c b/drivers/media/tuners/msi001.c new file mode 100644 index 0000000..ee99e37 --- /dev/null +++ b/drivers/media/tuners/msi001.c @@ -0,0 +1,500 @@ +/* + * Mirics MSi001 silicon tuner driver + * + * Copyright (C) 2013 Antti Palosaari + * Copyright (C) 2014 Antti Palosaari + * + * 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 +#include +#include +#include + +static const struct v4l2_frequency_band bands[] = { + { + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 49000000, + .rangehigh = 263000000, + }, { + .type = V4L2_TUNER_RF, + .index = 1, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 390000000, + .rangehigh = 960000000, + }, +}; + +struct msi001 { + struct spi_device *spi; + struct v4l2_subdev sd; + + /* Controls */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *bandwidth_auto; + struct v4l2_ctrl *bandwidth; + struct v4l2_ctrl *lna_gain; + struct v4l2_ctrl *mixer_gain; + struct v4l2_ctrl *if_gain; + + unsigned int f_tuner; +}; + +static inline struct msi001 *sd_to_msi001(struct v4l2_subdev *sd) +{ + return container_of(sd, struct msi001, sd); +} + +static int msi001_wreg(struct msi001 *s, u32 data) +{ + /* Register format: 4 bits addr + 20 bits value */ + return spi_write(s->spi, &data, 3); +}; + +static int msi001_set_gain(struct msi001 *s, int lna_gain, int mixer_gain, + int if_gain) +{ + int ret; + u32 reg; + dev_dbg(&s->spi->dev, "%s: lna=%d mixer=%d if=%d\n", __func__, + lna_gain, mixer_gain, if_gain); + + reg = 1 << 0; + reg |= (59 - if_gain) << 4; + reg |= 0 << 10; + reg |= (1 - mixer_gain) << 12; + reg |= (1 - lna_gain) << 13; + reg |= 4 << 14; + reg |= 0 << 17; + ret = msi001_wreg(s, reg); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret); + return ret; +}; + +static int msi001_set_tuner(struct msi001 *s) +{ + int ret, i; + unsigned int n, m, thresh, frac, vco_step, tmp, f_if1; + u32 reg; + u64 f_vco, tmp64; + u8 mode, filter_mode, lo_div; + static const struct { + u32 rf; + u8 mode; + u8 lo_div; + } band_lut[] = { + { 50000000, 0xe1, 16}, /* AM_MODE2, antenna 2 */ + {108000000, 0x42, 32}, /* VHF_MODE */ + {330000000, 0x44, 16}, /* B3_MODE */ + {960000000, 0x48, 4}, /* B45_MODE */ + { ~0U, 0x50, 2}, /* BL_MODE */ + }; + static const struct { + u32 freq; + u8 filter_mode; + } if_freq_lut[] = { + { 0, 0x03}, /* Zero IF */ + { 450000, 0x02}, /* 450 kHz IF */ + {1620000, 0x01}, /* 1.62 MHz IF */ + {2048000, 0x00}, /* 2.048 MHz IF */ + }; + static const struct { + u32 freq; + u8 val; + } bandwidth_lut[] = { + { 200000, 0x00}, /* 200 kHz */ + { 300000, 0x01}, /* 300 kHz */ + { 600000, 0x02}, /* 600 kHz */ + {1536000, 0x03}, /* 1.536 MHz */ + {5000000, 0x04}, /* 5 MHz */ + {6000000, 0x05}, /* 6 MHz */ + {7000000, 0x06}, /* 7 MHz */ + {8000000, 0x07}, /* 8 MHz */ + }; + + unsigned int f_rf = s->f_tuner; + + /* + * bandwidth (Hz) + * 200000, 300000, 600000, 1536000, 5000000, 6000000, 7000000, 8000000 + */ + unsigned int bandwidth; + + /* + * intermediate frequency (Hz) + * 0, 450000, 1620000, 2048000 + */ + unsigned int f_if = 0; + #define F_REF 24000000 + #define R_REF 4 + #define F_OUT_STEP 1 + + dev_dbg(&s->spi->dev, + "%s: f_rf=%d f_if=%d\n", + __func__, f_rf, f_if); + + for (i = 0; i < ARRAY_SIZE(band_lut); i++) { + if (f_rf <= band_lut[i].rf) { + mode = band_lut[i].mode; + lo_div = band_lut[i].lo_div; + break; + } + } + + if (i == ARRAY_SIZE(band_lut)) { + ret = -EINVAL; + goto err; + } + + /* AM_MODE is upconverted */ + if ((mode >> 0) & 0x1) + f_if1 = 5 * F_REF; + else + f_if1 = 0; + + for (i = 0; i < ARRAY_SIZE(if_freq_lut); i++) { + if (f_if == if_freq_lut[i].freq) { + filter_mode = if_freq_lut[i].filter_mode; + break; + } + } + + if (i == ARRAY_SIZE(if_freq_lut)) { + ret = -EINVAL; + goto err; + } + + /* filters */ + bandwidth = s->bandwidth->val; + bandwidth = clamp(bandwidth, 200000U, 8000000U); + + for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) { + if (bandwidth <= bandwidth_lut[i].freq) { + bandwidth = bandwidth_lut[i].val; + break; + } + } + + if (i == ARRAY_SIZE(bandwidth_lut)) { + ret = -EINVAL; + goto err; + } + + s->bandwidth->val = bandwidth_lut[i].freq; + + dev_dbg(&s->spi->dev, "%s: bandwidth selected=%d\n", + __func__, bandwidth_lut[i].freq); + + f_vco = (u64) (f_rf + f_if + f_if1) * lo_div; + tmp64 = f_vco; + m = do_div(tmp64, F_REF * R_REF); + n = (unsigned int) tmp64; + + vco_step = F_OUT_STEP * lo_div; + thresh = (F_REF * R_REF) / vco_step; + frac = 1ul * thresh * m / (F_REF * R_REF); + + /* Find out greatest common divisor and divide to smaller. */ + tmp = gcd(thresh, frac); + thresh /= tmp; + frac /= tmp; + + /* Force divide to reg max. Resolution will be reduced. */ + tmp = DIV_ROUND_UP(thresh, 4095); + thresh = DIV_ROUND_CLOSEST(thresh, tmp); + frac = DIV_ROUND_CLOSEST(frac, tmp); + + /* calc real RF set */ + tmp = 1ul * F_REF * R_REF * n; + tmp += 1ul * F_REF * R_REF * frac / thresh; + tmp /= lo_div; + + dev_dbg(&s->spi->dev, + "%s: rf=%u:%u n=%d thresh=%d frac=%d\n", + __func__, f_rf, tmp, n, thresh, frac); + + ret = msi001_wreg(s, 0x00000e); + if (ret) + goto err; + + ret = msi001_wreg(s, 0x000003); + if (ret) + goto err; + + reg = 0 << 0; + reg |= mode << 4; + reg |= filter_mode << 12; + reg |= bandwidth << 14; + reg |= 0x02 << 17; + reg |= 0x00 << 20; + ret = msi001_wreg(s, reg); + if (ret) + goto err; + + reg = 5 << 0; + reg |= thresh << 4; + reg |= 1 << 19; + reg |= 1 << 21; + ret = msi001_wreg(s, reg); + if (ret) + goto err; + + reg = 2 << 0; + reg |= frac << 4; + reg |= n << 16; + ret = msi001_wreg(s, reg); + if (ret) + goto err; + + ret = msi001_set_gain(s, s->lna_gain->cur.val, s->mixer_gain->cur.val, + s->if_gain->cur.val); + if (ret) + goto err; + + reg = 6 << 0; + reg |= 63 << 4; + reg |= 4095 << 10; + ret = msi001_wreg(s, reg); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret); + return ret; +}; + +static int msi001_s_power(struct v4l2_subdev *sd, int on) +{ + struct msi001 *s = sd_to_msi001(sd); + int ret; + dev_dbg(&s->spi->dev, "%s: on=%d\n", __func__, on); + + if (on) + ret = 0; + else + ret = msi001_wreg(s, 0x000000); + + return ret; +} + +static const struct v4l2_subdev_core_ops msi001_core_ops = { + .s_power = msi001_s_power, +}; + +static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) +{ + struct msi001 *s = sd_to_msi001(sd); + dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index); + + strlcpy(v->name, "Mirics MSi001", sizeof(v->name)); + v->type = V4L2_TUNER_RF; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = 49000000; + v->rangehigh = 960000000; + + return 0; +} + +static int msi001_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v) +{ + struct msi001 *s = sd_to_msi001(sd); + dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index); + return 0; +} + +static int msi001_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct msi001 *s = sd_to_msi001(sd); + dev_dbg(&s->spi->dev, "%s: tuner=%d\n", __func__, f->tuner); + f->frequency = s->f_tuner; + return 0; +} + +static int msi001_s_frequency(struct v4l2_subdev *sd, + const struct v4l2_frequency *f) +{ + struct msi001 *s = sd_to_msi001(sd); + unsigned int band; + dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d frequency=%u\n", + __func__, f->tuner, f->type, f->frequency); + + if (f->frequency < ((bands[0].rangehigh + bands[1].rangelow) / 2)) + band = 0; + else + band = 1; + s->f_tuner = clamp_t(unsigned int, f->frequency, + bands[band].rangelow, bands[band].rangehigh); + + return msi001_set_tuner(s); +} + +static int msi001_enum_freq_bands(struct v4l2_subdev *sd, + struct v4l2_frequency_band *band) +{ + struct msi001 *s = sd_to_msi001(sd); + dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d index=%d\n", + __func__, band->tuner, band->type, band->index); + + if (band->index >= ARRAY_SIZE(bands)) + return -EINVAL; + + band->capability = bands[band->index].capability; + band->rangelow = bands[band->index].rangelow; + band->rangehigh = bands[band->index].rangehigh; + + return 0; +} + +static const struct v4l2_subdev_tuner_ops msi001_tuner_ops = { + .g_tuner = msi001_g_tuner, + .s_tuner = msi001_s_tuner, + .g_frequency = msi001_g_frequency, + .s_frequency = msi001_s_frequency, + .enum_freq_bands = msi001_enum_freq_bands, +}; + +static const struct v4l2_subdev_ops msi001_ops = { + .core = &msi001_core_ops, + .tuner = &msi001_tuner_ops, +}; + +static int msi001_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct msi001 *s = container_of(ctrl->handler, struct msi001, hdl); + + int ret; + dev_dbg(&s->spi->dev, + "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", + __func__, ctrl->id, ctrl->name, ctrl->val, + ctrl->minimum, ctrl->maximum, ctrl->step); + + switch (ctrl->id) { + case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: + case V4L2_CID_RF_TUNER_BANDWIDTH: + ret = msi001_set_tuner(s); + break; + case V4L2_CID_RF_TUNER_LNA_GAIN: + ret = msi001_set_gain(s, s->lna_gain->val, + s->mixer_gain->cur.val, s->if_gain->cur.val); + break; + case V4L2_CID_RF_TUNER_MIXER_GAIN: + ret = msi001_set_gain(s, s->lna_gain->cur.val, + s->mixer_gain->val, s->if_gain->cur.val); + break; + case V4L2_CID_RF_TUNER_IF_GAIN: + ret = msi001_set_gain(s, s->lna_gain->cur.val, + s->mixer_gain->cur.val, s->if_gain->val); + break; + default: + dev_dbg(&s->spi->dev, "%s: unkown control %d\n", + __func__, ctrl->id); + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ctrl_ops msi001_ctrl_ops = { + .s_ctrl = msi001_s_ctrl, +}; + +static int msi001_probe(struct spi_device *spi) +{ + struct msi001 *s; + int ret; + dev_dbg(&spi->dev, "%s:\n", __func__); + + s = kzalloc(sizeof(struct msi001), GFP_KERNEL); + if (s == NULL) { + ret = -ENOMEM; + dev_dbg(&spi->dev, "Could not allocate memory for msi001\n"); + goto err_kfree; + } + + s->spi = spi; + s->f_tuner = bands[0].rangelow; + v4l2_spi_subdev_init(&s->sd, spi, &msi001_ops); + + /* Register controls */ + v4l2_ctrl_handler_init(&s->hdl, 5); + s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1); + s->bandwidth = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + V4L2_CID_RF_TUNER_BANDWIDTH, 200000, 8000000, 1, 200000); + v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); + s->lna_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + V4L2_CID_RF_TUNER_LNA_GAIN, 0, 1, 1, 1); + s->mixer_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1); + s->if_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + V4L2_CID_RF_TUNER_IF_GAIN, 0, 59, 1, 0); + if (s->hdl.error) { + ret = s->hdl.error; + dev_err(&s->spi->dev, "Could not initialize controls\n"); + /* control init failed, free handler */ + goto err_ctrl_handler_free; + } + + s->sd.ctrl_handler = &s->hdl; + return 0; + +err_ctrl_handler_free: + v4l2_ctrl_handler_free(&s->hdl); +err_kfree: + kfree(s); + return ret; +} + +static int msi001_remove(struct spi_device *spi) +{ + struct v4l2_subdev *sd = spi_get_drvdata(spi); + struct msi001 *s = sd_to_msi001(sd); + dev_dbg(&spi->dev, "%s:\n", __func__); + + /* + * Registered by v4l2_spi_new_subdev() from master driver, but we must + * unregister it from here. Weird. + */ + v4l2_device_unregister_subdev(&s->sd); + v4l2_ctrl_handler_free(&s->hdl); + kfree(s); + return 0; +} + +static const struct spi_device_id msi001_id[] = { + {"msi001", 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, msi001_id); + +static struct spi_driver msi001_driver = { + .driver = { + .name = "msi001", + .owner = THIS_MODULE, + }, + .probe = msi001_probe, + .remove = msi001_remove, + .id_table = msi001_id, +}; +module_spi_driver(msi001_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Mirics MSi001"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/msi3101/Kconfig b/drivers/staging/media/msi3101/Kconfig index de0b3bb..399dbbb 100644 --- a/drivers/staging/media/msi3101/Kconfig +++ b/drivers/staging/media/msi3101/Kconfig @@ -5,6 +5,3 @@ config USB_MSI3101 select VIDEOBUF2_VMALLOC select MEDIA_TUNER_MSI001 -config MEDIA_TUNER_MSI001 - tristate "Mirics MSi001" - depends on VIDEO_V4L2 && SPI diff --git a/drivers/staging/media/msi3101/Makefile b/drivers/staging/media/msi3101/Makefile index daf4f58..3730654 100644 --- a/drivers/staging/media/msi3101/Makefile +++ b/drivers/staging/media/msi3101/Makefile @@ -1,2 +1 @@ obj-$(CONFIG_USB_MSI3101) += sdr-msi3101.o -obj-$(CONFIG_MEDIA_TUNER_MSI001) += msi001.o diff --git a/drivers/staging/media/msi3101/msi001.c b/drivers/staging/media/msi3101/msi001.c deleted file mode 100644 index ee99e37..0000000 --- a/drivers/staging/media/msi3101/msi001.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Mirics MSi001 silicon tuner driver - * - * Copyright (C) 2013 Antti Palosaari - * Copyright (C) 2014 Antti Palosaari - * - * 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 -#include -#include -#include - -static const struct v4l2_frequency_band bands[] = { - { - .type = V4L2_TUNER_RF, - .index = 0, - .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 49000000, - .rangehigh = 263000000, - }, { - .type = V4L2_TUNER_RF, - .index = 1, - .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 390000000, - .rangehigh = 960000000, - }, -}; - -struct msi001 { - struct spi_device *spi; - struct v4l2_subdev sd; - - /* Controls */ - struct v4l2_ctrl_handler hdl; - struct v4l2_ctrl *bandwidth_auto; - struct v4l2_ctrl *bandwidth; - struct v4l2_ctrl *lna_gain; - struct v4l2_ctrl *mixer_gain; - struct v4l2_ctrl *if_gain; - - unsigned int f_tuner; -}; - -static inline struct msi001 *sd_to_msi001(struct v4l2_subdev *sd) -{ - return container_of(sd, struct msi001, sd); -} - -static int msi001_wreg(struct msi001 *s, u32 data) -{ - /* Register format: 4 bits addr + 20 bits value */ - return spi_write(s->spi, &data, 3); -}; - -static int msi001_set_gain(struct msi001 *s, int lna_gain, int mixer_gain, - int if_gain) -{ - int ret; - u32 reg; - dev_dbg(&s->spi->dev, "%s: lna=%d mixer=%d if=%d\n", __func__, - lna_gain, mixer_gain, if_gain); - - reg = 1 << 0; - reg |= (59 - if_gain) << 4; - reg |= 0 << 10; - reg |= (1 - mixer_gain) << 12; - reg |= (1 - lna_gain) << 13; - reg |= 4 << 14; - reg |= 0 << 17; - ret = msi001_wreg(s, reg); - if (ret) - goto err; - - return 0; -err: - dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret); - return ret; -}; - -static int msi001_set_tuner(struct msi001 *s) -{ - int ret, i; - unsigned int n, m, thresh, frac, vco_step, tmp, f_if1; - u32 reg; - u64 f_vco, tmp64; - u8 mode, filter_mode, lo_div; - static const struct { - u32 rf; - u8 mode; - u8 lo_div; - } band_lut[] = { - { 50000000, 0xe1, 16}, /* AM_MODE2, antenna 2 */ - {108000000, 0x42, 32}, /* VHF_MODE */ - {330000000, 0x44, 16}, /* B3_MODE */ - {960000000, 0x48, 4}, /* B45_MODE */ - { ~0U, 0x50, 2}, /* BL_MODE */ - }; - static const struct { - u32 freq; - u8 filter_mode; - } if_freq_lut[] = { - { 0, 0x03}, /* Zero IF */ - { 450000, 0x02}, /* 450 kHz IF */ - {1620000, 0x01}, /* 1.62 MHz IF */ - {2048000, 0x00}, /* 2.048 MHz IF */ - }; - static const struct { - u32 freq; - u8 val; - } bandwidth_lut[] = { - { 200000, 0x00}, /* 200 kHz */ - { 300000, 0x01}, /* 300 kHz */ - { 600000, 0x02}, /* 600 kHz */ - {1536000, 0x03}, /* 1.536 MHz */ - {5000000, 0x04}, /* 5 MHz */ - {6000000, 0x05}, /* 6 MHz */ - {7000000, 0x06}, /* 7 MHz */ - {8000000, 0x07}, /* 8 MHz */ - }; - - unsigned int f_rf = s->f_tuner; - - /* - * bandwidth (Hz) - * 200000, 300000, 600000, 1536000, 5000000, 6000000, 7000000, 8000000 - */ - unsigned int bandwidth; - - /* - * intermediate frequency (Hz) - * 0, 450000, 1620000, 2048000 - */ - unsigned int f_if = 0; - #define F_REF 24000000 - #define R_REF 4 - #define F_OUT_STEP 1 - - dev_dbg(&s->spi->dev, - "%s: f_rf=%d f_if=%d\n", - __func__, f_rf, f_if); - - for (i = 0; i < ARRAY_SIZE(band_lut); i++) { - if (f_rf <= band_lut[i].rf) { - mode = band_lut[i].mode; - lo_div = band_lut[i].lo_div; - break; - } - } - - if (i == ARRAY_SIZE(band_lut)) { - ret = -EINVAL; - goto err; - } - - /* AM_MODE is upconverted */ - if ((mode >> 0) & 0x1) - f_if1 = 5 * F_REF; - else - f_if1 = 0; - - for (i = 0; i < ARRAY_SIZE(if_freq_lut); i++) { - if (f_if == if_freq_lut[i].freq) { - filter_mode = if_freq_lut[i].filter_mode; - break; - } - } - - if (i == ARRAY_SIZE(if_freq_lut)) { - ret = -EINVAL; - goto err; - } - - /* filters */ - bandwidth = s->bandwidth->val; - bandwidth = clamp(bandwidth, 200000U, 8000000U); - - for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) { - if (bandwidth <= bandwidth_lut[i].freq) { - bandwidth = bandwidth_lut[i].val; - break; - } - } - - if (i == ARRAY_SIZE(bandwidth_lut)) { - ret = -EINVAL; - goto err; - } - - s->bandwidth->val = bandwidth_lut[i].freq; - - dev_dbg(&s->spi->dev, "%s: bandwidth selected=%d\n", - __func__, bandwidth_lut[i].freq); - - f_vco = (u64) (f_rf + f_if + f_if1) * lo_div; - tmp64 = f_vco; - m = do_div(tmp64, F_REF * R_REF); - n = (unsigned int) tmp64; - - vco_step = F_OUT_STEP * lo_div; - thresh = (F_REF * R_REF) / vco_step; - frac = 1ul * thresh * m / (F_REF * R_REF); - - /* Find out greatest common divisor and divide to smaller. */ - tmp = gcd(thresh, frac); - thresh /= tmp; - frac /= tmp; - - /* Force divide to reg max. Resolution will be reduced. */ - tmp = DIV_ROUND_UP(thresh, 4095); - thresh = DIV_ROUND_CLOSEST(thresh, tmp); - frac = DIV_ROUND_CLOSEST(frac, tmp); - - /* calc real RF set */ - tmp = 1ul * F_REF * R_REF * n; - tmp += 1ul * F_REF * R_REF * frac / thresh; - tmp /= lo_div; - - dev_dbg(&s->spi->dev, - "%s: rf=%u:%u n=%d thresh=%d frac=%d\n", - __func__, f_rf, tmp, n, thresh, frac); - - ret = msi001_wreg(s, 0x00000e); - if (ret) - goto err; - - ret = msi001_wreg(s, 0x000003); - if (ret) - goto err; - - reg = 0 << 0; - reg |= mode << 4; - reg |= filter_mode << 12; - reg |= bandwidth << 14; - reg |= 0x02 << 17; - reg |= 0x00 << 20; - ret = msi001_wreg(s, reg); - if (ret) - goto err; - - reg = 5 << 0; - reg |= thresh << 4; - reg |= 1 << 19; - reg |= 1 << 21; - ret = msi001_wreg(s, reg); - if (ret) - goto err; - - reg = 2 << 0; - reg |= frac << 4; - reg |= n << 16; - ret = msi001_wreg(s, reg); - if (ret) - goto err; - - ret = msi001_set_gain(s, s->lna_gain->cur.val, s->mixer_gain->cur.val, - s->if_gain->cur.val); - if (ret) - goto err; - - reg = 6 << 0; - reg |= 63 << 4; - reg |= 4095 << 10; - ret = msi001_wreg(s, reg); - if (ret) - goto err; - - return 0; -err: - dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret); - return ret; -}; - -static int msi001_s_power(struct v4l2_subdev *sd, int on) -{ - struct msi001 *s = sd_to_msi001(sd); - int ret; - dev_dbg(&s->spi->dev, "%s: on=%d\n", __func__, on); - - if (on) - ret = 0; - else - ret = msi001_wreg(s, 0x000000); - - return ret; -} - -static const struct v4l2_subdev_core_ops msi001_core_ops = { - .s_power = msi001_s_power, -}; - -static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) -{ - struct msi001 *s = sd_to_msi001(sd); - dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index); - - strlcpy(v->name, "Mirics MSi001", sizeof(v->name)); - v->type = V4L2_TUNER_RF; - v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; - v->rangelow = 49000000; - v->rangehigh = 960000000; - - return 0; -} - -static int msi001_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v) -{ - struct msi001 *s = sd_to_msi001(sd); - dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index); - return 0; -} - -static int msi001_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) -{ - struct msi001 *s = sd_to_msi001(sd); - dev_dbg(&s->spi->dev, "%s: tuner=%d\n", __func__, f->tuner); - f->frequency = s->f_tuner; - return 0; -} - -static int msi001_s_frequency(struct v4l2_subdev *sd, - const struct v4l2_frequency *f) -{ - struct msi001 *s = sd_to_msi001(sd); - unsigned int band; - dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d frequency=%u\n", - __func__, f->tuner, f->type, f->frequency); - - if (f->frequency < ((bands[0].rangehigh + bands[1].rangelow) / 2)) - band = 0; - else - band = 1; - s->f_tuner = clamp_t(unsigned int, f->frequency, - bands[band].rangelow, bands[band].rangehigh); - - return msi001_set_tuner(s); -} - -static int msi001_enum_freq_bands(struct v4l2_subdev *sd, - struct v4l2_frequency_band *band) -{ - struct msi001 *s = sd_to_msi001(sd); - dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d index=%d\n", - __func__, band->tuner, band->type, band->index); - - if (band->index >= ARRAY_SIZE(bands)) - return -EINVAL; - - band->capability = bands[band->index].capability; - band->rangelow = bands[band->index].rangelow; - band->rangehigh = bands[band->index].rangehigh; - - return 0; -} - -static const struct v4l2_subdev_tuner_ops msi001_tuner_ops = { - .g_tuner = msi001_g_tuner, - .s_tuner = msi001_s_tuner, - .g_frequency = msi001_g_frequency, - .s_frequency = msi001_s_frequency, - .enum_freq_bands = msi001_enum_freq_bands, -}; - -static const struct v4l2_subdev_ops msi001_ops = { - .core = &msi001_core_ops, - .tuner = &msi001_tuner_ops, -}; - -static int msi001_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct msi001 *s = container_of(ctrl->handler, struct msi001, hdl); - - int ret; - dev_dbg(&s->spi->dev, - "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", - __func__, ctrl->id, ctrl->name, ctrl->val, - ctrl->minimum, ctrl->maximum, ctrl->step); - - switch (ctrl->id) { - case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: - case V4L2_CID_RF_TUNER_BANDWIDTH: - ret = msi001_set_tuner(s); - break; - case V4L2_CID_RF_TUNER_LNA_GAIN: - ret = msi001_set_gain(s, s->lna_gain->val, - s->mixer_gain->cur.val, s->if_gain->cur.val); - break; - case V4L2_CID_RF_TUNER_MIXER_GAIN: - ret = msi001_set_gain(s, s->lna_gain->cur.val, - s->mixer_gain->val, s->if_gain->cur.val); - break; - case V4L2_CID_RF_TUNER_IF_GAIN: - ret = msi001_set_gain(s, s->lna_gain->cur.val, - s->mixer_gain->cur.val, s->if_gain->val); - break; - default: - dev_dbg(&s->spi->dev, "%s: unkown control %d\n", - __func__, ctrl->id); - ret = -EINVAL; - } - - return ret; -} - -static const struct v4l2_ctrl_ops msi001_ctrl_ops = { - .s_ctrl = msi001_s_ctrl, -}; - -static int msi001_probe(struct spi_device *spi) -{ - struct msi001 *s; - int ret; - dev_dbg(&spi->dev, "%s:\n", __func__); - - s = kzalloc(sizeof(struct msi001), GFP_KERNEL); - if (s == NULL) { - ret = -ENOMEM; - dev_dbg(&spi->dev, "Could not allocate memory for msi001\n"); - goto err_kfree; - } - - s->spi = spi; - s->f_tuner = bands[0].rangelow; - v4l2_spi_subdev_init(&s->sd, spi, &msi001_ops); - - /* Register controls */ - v4l2_ctrl_handler_init(&s->hdl, 5); - s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, - V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1); - s->bandwidth = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, - V4L2_CID_RF_TUNER_BANDWIDTH, 200000, 8000000, 1, 200000); - v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); - s->lna_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, - V4L2_CID_RF_TUNER_LNA_GAIN, 0, 1, 1, 1); - s->mixer_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, - V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1); - s->if_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, - V4L2_CID_RF_TUNER_IF_GAIN, 0, 59, 1, 0); - if (s->hdl.error) { - ret = s->hdl.error; - dev_err(&s->spi->dev, "Could not initialize controls\n"); - /* control init failed, free handler */ - goto err_ctrl_handler_free; - } - - s->sd.ctrl_handler = &s->hdl; - return 0; - -err_ctrl_handler_free: - v4l2_ctrl_handler_free(&s->hdl); -err_kfree: - kfree(s); - return ret; -} - -static int msi001_remove(struct spi_device *spi) -{ - struct v4l2_subdev *sd = spi_get_drvdata(spi); - struct msi001 *s = sd_to_msi001(sd); - dev_dbg(&spi->dev, "%s:\n", __func__); - - /* - * Registered by v4l2_spi_new_subdev() from master driver, but we must - * unregister it from here. Weird. - */ - v4l2_device_unregister_subdev(&s->sd); - v4l2_ctrl_handler_free(&s->hdl); - kfree(s); - return 0; -} - -static const struct spi_device_id msi001_id[] = { - {"msi001", 0}, - {} -}; -MODULE_DEVICE_TABLE(spi, msi001_id); - -static struct spi_driver msi001_driver = { - .driver = { - .name = "msi001", - .owner = THIS_MODULE, - }, - .probe = msi001_probe, - .remove = msi001_remove, - .id_table = msi001_id, -}; -module_spi_driver(msi001_driver); - -MODULE_AUTHOR("Antti Palosaari "); -MODULE_DESCRIPTION("Mirics MSi001"); -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 0185e197dc656292de4a69b3c82bfa2a39385051 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 12 Jul 2014 08:58:32 -0300 Subject: [media] MAINTAINERS: update MSI001 driver location The driver moved out of staging. Update MAINTAINERS accordingly. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index c771406..7c6a7ad 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5957,7 +5957,7 @@ W: http://palosaari.fi/linux/ Q: http://patchwork.linuxtv.org/project/linux-media/list/ T: git git://linuxtv.org/anttip/media_tree.git S: Maintained -F: drivers/staging/media/msi3101/msi001* +F: drivers/media/tuners/msi001* MSI3101 MEDIA DRIVER M: Antti Palosaari -- cgit v0.10.2 From 435ef148cabb65644032cb08d1ac3e7611cb24ae Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 12 Jul 2014 21:43:12 -0300 Subject: [media] Kconfig: add SDR support Add software defined radio device support for media Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 1d0758a..3c89fcb 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -59,6 +59,13 @@ config MEDIA_RADIO_SUPPORT support radio reception. Disabling this option will disable support for them. +config MEDIA_SDR_SUPPORT + bool "Software defined radio support" + ---help--- + Enable software defined radio support. + + Say Y when you have a software defined radio device. + config MEDIA_RC_SUPPORT bool "Remote Controller support" depends on INPUT @@ -95,7 +102,7 @@ config MEDIA_CONTROLLER config VIDEO_DEV tristate depends on MEDIA_SUPPORT - depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT + depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT default y config VIDEO_V4L2_SUBDEV_API @@ -171,7 +178,7 @@ comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" config MEDIA_SUBDRV_AUTOSELECT bool "Autoselect ancillary drivers (tuners, sensors, i2c, frontends)" - depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_CAMERA_SUPPORT + depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_CAMERA_SUPPORT || MEDIA_SDR_SUPPORT depends on HAS_IOMEM select I2C select I2C_MUX diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 39d824e..f8c1099 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -52,5 +52,9 @@ if (MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) source "drivers/media/usb/em28xx/Kconfig" endif +if MEDIA_SDR_SUPPORT + comment "Software defined radio USB devices" +endif + endif #MEDIA_USB_SUPPORT endif #USB -- cgit v0.10.2 From e4462ffc1602d9df21c00a0381dca9080474e27a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 12 Jul 2014 21:48:39 -0300 Subject: [media] Kconfig: sub-driver auto-select SPI bus Mirics MSi001 and MSi2500 drivers uses SPI bus. Due to that we need auto-select it too. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 3c89fcb..f60bad4 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -182,6 +182,7 @@ config MEDIA_SUBDRV_AUTOSELECT depends on HAS_IOMEM select I2C select I2C_MUX + select SPI default y help By default, a media driver auto-selects all possible ancillary -- cgit v0.10.2 From 77bbb2b049c1c3e935f5bec510bec337d94ae8f8 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 15 Jul 2014 02:08:38 +0300 Subject: rtl2832_sdr: move from staging to media Move rtl2832_sdr driver module from staging to media. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 1469d44..7225f05 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -446,6 +446,15 @@ config DVB_RTL2832 help Say Y when you want to support this frontend. +config DVB_RTL2832_SDR + tristate "Realtek RTL2832 SDR" + depends on DVB_CORE && I2C && VIDEO_V4L2 + select DVB_RTL2832 + select VIDEOBUF2_VMALLOC + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this SDR module. + config DVB_SI2168 tristate "Silicon Labs Si2168" depends on DVB_CORE && I2C && I2C_MUX diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index dda0bee..655e3c8 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -5,6 +5,11 @@ ccflags-y += -I$(srctree)/drivers/media/dvb-core/ ccflags-y += -I$(srctree)/drivers/media/tuners/ +# FIXME: RTL2832 SDR driver uses power management directly from USB IF driver +ifdef CONFIG_DVB_RTL2832_SDR + ccflags-y += -I$(srctree)/drivers/media/usb/dvb-usb-v2 +endif + stb0899-objs := stb0899_drv.o stb0899_algo.o stv0900-objs := stv0900_core.o stv0900_sw.o drxd-objs := drxd_firm.o drxd_hard.o @@ -104,6 +109,7 @@ obj-$(CONFIG_DVB_A8293) += a8293.o obj-$(CONFIG_DVB_TDA10071) += tda10071.o obj-$(CONFIG_DVB_RTL2830) += rtl2830.o obj-$(CONFIG_DVB_RTL2832) += rtl2832.o +obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o obj-$(CONFIG_DVB_AF9033) += af9033.o diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c new file mode 100644 index 0000000..f81b1c8 --- /dev/null +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -0,0 +1,1528 @@ +/* + * Realtek RTL2832U SDR driver + * + * Copyright (C) 2013 Antti Palosaari + * + * 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. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * GNU Radio plugin "gr-kernel" for device usage will be on: + * http://git.linuxtv.org/anttip/gr-kernel.git + * + */ + +#include "dvb_frontend.h" +#include "rtl2832_sdr.h" +#include "dvb_usb.h" + +#include +#include +#include +#include +#include + +#include +#include + +#define MAX_BULK_BUFS (10) +#define BULK_BUFFER_SIZE (128 * 512) + +static const struct v4l2_frequency_band bands_adc[] = { + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 300000, + .rangehigh = 300000, + }, + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 1, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 900001, + .rangehigh = 2800000, + }, + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 2, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 3200000, + .rangehigh = 3200000, + }, +}; + +static const struct v4l2_frequency_band bands_fm[] = { + { + .tuner = 1, + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 50000000, + .rangehigh = 2000000000, + }, +}; + +/* stream formats */ +struct rtl2832_sdr_format { + char *name; + u32 pixelformat; +}; + +static struct rtl2832_sdr_format formats[] = { + { + .name = "IQ U8", + .pixelformat = V4L2_SDR_FMT_CU8, + }, { + .name = "IQ U16LE (emulated)", + .pixelformat = V4L2_SDR_FMT_CU16LE, + }, +}; + +static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); + +/* intermediate buffers with raw data from the USB device */ +struct rtl2832_sdr_frame_buf { + struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ + struct list_head list; +}; + +struct rtl2832_sdr_state { +#define POWER_ON (1 << 1) +#define URB_BUF (1 << 2) + unsigned long flags; + + const struct rtl2832_config *cfg; + struct dvb_frontend *fe; + struct dvb_usb_device *d; + struct i2c_adapter *i2c; + u8 bank; + + struct video_device vdev; + struct v4l2_device v4l2_dev; + + /* videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + unsigned sequence; /* buffer sequence counter */ + + /* Note if taking both locks v4l2_lock must always be locked first! */ + struct mutex v4l2_lock; /* Protects everything else */ + struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ + + /* Pointer to our usb_device, will be NULL after unplug */ + struct usb_device *udev; /* Both mutexes most be hold when setting! */ + + unsigned int vb_full; /* vb is full and packets dropped */ + + struct urb *urb_list[MAX_BULK_BUFS]; + int buf_num; + unsigned long buf_size; + u8 *buf_list[MAX_BULK_BUFS]; + dma_addr_t dma_addr[MAX_BULK_BUFS]; + int urbs_initialized; + int urbs_submitted; + + unsigned int f_adc, f_tuner; + u32 pixelformat; + + /* Controls */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *bandwidth_auto; + struct v4l2_ctrl *bandwidth; + + /* for sample rate calc */ + unsigned int sample; + unsigned int sample_measured; + unsigned long jiffies_next; +}; + +/* write multiple hardware registers */ +static int rtl2832_sdr_wr(struct rtl2832_sdr_state *s, u8 reg, const u8 *val, + int len) +{ + int ret; +#define MAX_WR_LEN 24 +#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1) + u8 buf[MAX_WR_XFER_LEN]; + struct i2c_msg msg[1] = { + { + .addr = s->cfg->i2c_addr, + .flags = 0, + .len = 1 + len, + .buf = buf, + } + }; + + if (WARN_ON(len > MAX_WR_LEN)) + return -EINVAL; + + buf[0] = reg; + memcpy(&buf[1], val, len); + + ret = i2c_transfer(s->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + dev_err(&s->i2c->dev, + "%s: I2C wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + ret = -EREMOTEIO; + } + return ret; +} + +/* read multiple hardware registers */ +static int rtl2832_sdr_rd(struct rtl2832_sdr_state *s, u8 reg, u8 *val, int len) +{ + int ret; + struct i2c_msg msg[2] = { + { + .addr = s->cfg->i2c_addr, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = s->cfg->i2c_addr, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(s->i2c, msg, 2); + if (ret == 2) { + ret = 0; + } else { + dev_err(&s->i2c->dev, + "%s: I2C rd failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + ret = -EREMOTEIO; + } + return ret; +} + +/* write multiple registers */ +static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_state *s, u16 reg, + const u8 *val, int len) +{ + int ret; + u8 reg2 = (reg >> 0) & 0xff; + u8 bank = (reg >> 8) & 0xff; + + /* switch bank if needed */ + if (bank != s->bank) { + ret = rtl2832_sdr_wr(s, 0x00, &bank, 1); + if (ret) + return ret; + + s->bank = bank; + } + + return rtl2832_sdr_wr(s, reg2, val, len); +} + +/* read multiple registers */ +static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_state *s, u16 reg, u8 *val, + int len) +{ + int ret; + u8 reg2 = (reg >> 0) & 0xff; + u8 bank = (reg >> 8) & 0xff; + + /* switch bank if needed */ + if (bank != s->bank) { + ret = rtl2832_sdr_wr(s, 0x00, &bank, 1); + if (ret) + return ret; + + s->bank = bank; + } + + return rtl2832_sdr_rd(s, reg2, val, len); +} + +/* write single register */ +static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_state *s, u16 reg, u8 val) +{ + return rtl2832_sdr_wr_regs(s, reg, &val, 1); +} + +#if 0 +/* read single register */ +static int rtl2832_sdr_rd_reg(struct rtl2832_sdr_state *s, u16 reg, u8 *val) +{ + return rtl2832_sdr_rd_regs(s, reg, val, 1); +} +#endif + +/* write single register with mask */ +static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_state *s, u16 reg, + u8 val, u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return rtl2832_sdr_wr_regs(s, reg, &val, 1); +} + +#if 0 +/* read single register with mask */ +static int rtl2832_sdr_rd_reg_mask(struct rtl2832_sdr_state *s, u16 reg, + u8 *val, u8 mask) +{ + int ret, i; + u8 tmp; + + ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1); + if (ret) + return ret; + + tmp &= mask; + + /* find position of the first bit */ + for (i = 0; i < 8; i++) { + if ((mask >> i) & 0x01) + break; + } + *val = tmp >> i; + + return 0; +} +#endif + +/* Private functions */ +static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf( + struct rtl2832_sdr_state *s) +{ + unsigned long flags = 0; + struct rtl2832_sdr_frame_buf *buf = NULL; + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + if (list_empty(&s->queued_bufs)) + goto leave; + + buf = list_entry(s->queued_bufs.next, + struct rtl2832_sdr_frame_buf, list); + list_del(&buf->list); +leave: + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); + return buf; +} + +static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s, + void *dst, const u8 *src, unsigned int src_len) +{ + unsigned int dst_len; + + if (s->pixelformat == V4L2_SDR_FMT_CU8) { + /* native stream, no need to convert */ + memcpy(dst, src, src_len); + dst_len = src_len; + } else if (s->pixelformat == V4L2_SDR_FMT_CU16LE) { + /* convert u8 to u16 */ + unsigned int i; + u16 *u16dst = dst; + + for (i = 0; i < src_len; i++) + *u16dst++ = (src[i] << 8) | (src[i] >> 0); + dst_len = 2 * src_len; + } else { + dst_len = 0; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if (unlikely(time_is_before_jiffies(s->jiffies_next))) { +#define MSECS 10000UL + unsigned int samples = s->sample - s->sample_measured; + + s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); + s->sample_measured = s->sample; + dev_dbg(&s->udev->dev, + "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", + src_len, samples, MSECS, + samples * 1000UL / MSECS); + } + + /* total number of I+Q pairs */ + s->sample += src_len / 2; + + return dst_len; +} + +/* + * This gets called for the bulk stream pipe. This is done in interrupt + * time, so it has to be fast, not crash, and not stall. Neat. + */ +static void rtl2832_sdr_urb_complete(struct urb *urb) +{ + struct rtl2832_sdr_state *s = urb->context; + struct rtl2832_sdr_frame_buf *fbuf; + + dev_dbg_ratelimited(&s->udev->dev, + "%s: status=%d length=%d/%d errors=%d\n", + __func__, urb->status, urb->actual_length, + urb->transfer_buffer_length, urb->error_count); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dev_err_ratelimited(&s->udev->dev, "urb failed=%d\n", + urb->status); + break; + } + + if (likely(urb->actual_length > 0)) { + void *ptr; + unsigned int len; + /* get free framebuffer */ + fbuf = rtl2832_sdr_get_next_fill_buf(s); + if (unlikely(fbuf == NULL)) { + s->vb_full++; + dev_notice_ratelimited(&s->udev->dev, + "videobuf is full, %d packets dropped\n", + s->vb_full); + goto skip; + } + + /* fill framebuffer */ + ptr = vb2_plane_vaddr(&fbuf->vb, 0); + len = rtl2832_sdr_convert_stream(s, ptr, urb->transfer_buffer, + urb->actual_length); + vb2_set_plane_payload(&fbuf->vb, 0, len); + v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp); + fbuf->vb.v4l2_buf.sequence = s->sequence++; + vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + } +skip: + usb_submit_urb(urb, GFP_ATOMIC); +} + +static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_state *s) +{ + int i; + + for (i = s->urbs_submitted - 1; i >= 0; i--) { + dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i); + /* stop the URB */ + usb_kill_urb(s->urb_list[i]); + } + s->urbs_submitted = 0; + + return 0; +} + +static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_state *s) +{ + int i, ret; + + for (i = 0; i < s->urbs_initialized; i++) { + dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i); + ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC); + if (ret) { + dev_err(&s->udev->dev, + "Could not submit urb no. %d - get them all back\n", + i); + rtl2832_sdr_kill_urbs(s); + return ret; + } + s->urbs_submitted++; + } + + return 0; +} + +static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_state *s) +{ + if (s->flags & USB_STATE_URB_BUF) { + while (s->buf_num) { + s->buf_num--; + dev_dbg(&s->udev->dev, "%s: free buf=%d\n", + __func__, s->buf_num); + usb_free_coherent(s->udev, s->buf_size, + s->buf_list[s->buf_num], + s->dma_addr[s->buf_num]); + } + } + s->flags &= ~USB_STATE_URB_BUF; + + return 0; +} + +static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_state *s) +{ + s->buf_num = 0; + s->buf_size = BULK_BUFFER_SIZE; + + dev_dbg(&s->udev->dev, + "%s: all in all I will use %u bytes for streaming\n", + __func__, MAX_BULK_BUFS * BULK_BUFFER_SIZE); + + for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) { + s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev, + BULK_BUFFER_SIZE, GFP_ATOMIC, + &s->dma_addr[s->buf_num]); + if (!s->buf_list[s->buf_num]) { + dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n", + __func__, s->buf_num); + rtl2832_sdr_free_stream_bufs(s); + return -ENOMEM; + } + + dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n", + __func__, s->buf_num, + s->buf_list[s->buf_num], + (long long)s->dma_addr[s->buf_num]); + s->flags |= USB_STATE_URB_BUF; + } + + return 0; +} + +static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_state *s) +{ + int i; + + rtl2832_sdr_kill_urbs(s); + + for (i = s->urbs_initialized - 1; i >= 0; i--) { + if (s->urb_list[i]) { + dev_dbg(&s->udev->dev, "%s: free urb=%d\n", + __func__, i); + /* free the URBs */ + usb_free_urb(s->urb_list[i]); + } + } + s->urbs_initialized = 0; + + return 0; +} + +static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s) +{ + int i, j; + + /* allocate the URBs */ + for (i = 0; i < MAX_BULK_BUFS; i++) { + dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i); + s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + if (!s->urb_list[i]) { + dev_dbg(&s->udev->dev, "%s: failed\n", __func__); + for (j = 0; j < i; j++) + usb_free_urb(s->urb_list[j]); + return -ENOMEM; + } + usb_fill_bulk_urb(s->urb_list[i], + s->udev, + usb_rcvbulkpipe(s->udev, 0x81), + s->buf_list[i], + BULK_BUFFER_SIZE, + rtl2832_sdr_urb_complete, s); + + s->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + s->urb_list[i]->transfer_dma = s->dma_addr[i]; + s->urbs_initialized++; + } + + return 0; +} + +/* Must be called with vb_queue_lock hold */ +static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s) +{ + unsigned long flags = 0; + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + while (!list_empty(&s->queued_bufs)) { + struct rtl2832_sdr_frame_buf *buf; + + buf = list_entry(s->queued_bufs.next, + struct rtl2832_sdr_frame_buf, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); +} + +/* The user yanked out the cable... */ +static void rtl2832_sdr_release_sec(struct dvb_frontend *fe) +{ + struct rtl2832_sdr_state *s = fe->sec_priv; + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + mutex_lock(&s->vb_queue_lock); + mutex_lock(&s->v4l2_lock); + /* No need to keep the urbs around after disconnection */ + s->udev = NULL; + + v4l2_device_disconnect(&s->v4l2_dev); + video_unregister_device(&s->vdev); + mutex_unlock(&s->v4l2_lock); + mutex_unlock(&s->vb_queue_lock); + + v4l2_device_put(&s->v4l2_dev); + + fe->sec_priv = NULL; +} + +static int rtl2832_sdr_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct rtl2832_sdr_state *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); + usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +/* Videobuf2 operations */ +static int rtl2832_sdr_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) +{ + struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); + + dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); + + /* Need at least 8 buffers */ + if (vq->num_buffers + *nbuffers < 8) + *nbuffers = 8 - vq->num_buffers; + *nplanes = 1; + /* 2 = max 16-bit sample returned */ + sizes[0] = PAGE_ALIGN(BULK_BUFFER_SIZE * 2); + dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", + __func__, *nbuffers, sizes[0]); + return 0; +} + +static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb) +{ + struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue); + + /* Don't allow queing new buffers after device disconnection */ + if (!s->udev) + return -ENODEV; + + return 0; +} + +static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb) +{ + struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue); + struct rtl2832_sdr_frame_buf *buf = + container_of(vb, struct rtl2832_sdr_frame_buf, vb); + unsigned long flags = 0; + + /* Check the device has not disconnected between prep and queuing */ + if (!s->udev) { + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + return; + } + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + list_add_tail(&buf->list, &s->queued_bufs); + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); +} + +static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s) +{ + struct dvb_frontend *fe = s->fe; + int ret; + unsigned int f_sr, f_if; + u8 buf[4], u8tmp1, u8tmp2; + u64 u64tmp; + u32 u32tmp; + + dev_dbg(&s->udev->dev, "%s: f_adc=%u\n", __func__, s->f_adc); + + if (!test_bit(POWER_ON, &s->flags)) + return 0; + + if (s->f_adc == 0) + return 0; + + f_sr = s->f_adc; + + ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x00\x00", 2); + if (ret) + goto err; + + ret = rtl2832_sdr_wr_regs(s, 0x115, "\x00\x00\x00\x00", 4); + if (ret) + goto err; + + /* get IF from tuner */ + if (fe->ops.tuner_ops.get_if_frequency) + ret = fe->ops.tuner_ops.get_if_frequency(fe, &f_if); + else + ret = -EINVAL; + + if (ret) + goto err; + + /* program IF */ + u64tmp = f_if % s->cfg->xtal; + u64tmp *= 0x400000; + u64tmp = div_u64(u64tmp, s->cfg->xtal); + u64tmp = -u64tmp; + u32tmp = u64tmp & 0x3fffff; + + dev_dbg(&s->udev->dev, "%s: f_if=%u if_ctl=%08x\n", + __func__, f_if, u32tmp); + + buf[0] = (u32tmp >> 16) & 0xff; + buf[1] = (u32tmp >> 8) & 0xff; + buf[2] = (u32tmp >> 0) & 0xff; + + ret = rtl2832_sdr_wr_regs(s, 0x119, buf, 3); + if (ret) + goto err; + + /* BB / IF mode */ + /* POR: 0x1b1=0x1f, 0x008=0x0d, 0x006=0x80 */ + if (f_if) { + u8tmp1 = 0x1a; /* disable Zero-IF */ + u8tmp2 = 0x8d; /* enable ADC I */ + } else { + u8tmp1 = 0x1b; /* enable Zero-IF, DC, IQ */ + u8tmp2 = 0xcd; /* enable ADC I, ADC Q */ + } + + ret = rtl2832_sdr_wr_reg(s, 0x1b1, u8tmp1); + if (ret) + goto err; + + ret = rtl2832_sdr_wr_reg(s, 0x008, u8tmp2); + if (ret) + goto err; + + ret = rtl2832_sdr_wr_reg(s, 0x006, 0x80); + if (ret) + goto err; + + /* program sampling rate (resampling down) */ + u32tmp = div_u64(s->cfg->xtal * 0x400000ULL, f_sr * 4U); + u32tmp <<= 2; + buf[0] = (u32tmp >> 24) & 0xff; + buf[1] = (u32tmp >> 16) & 0xff; + buf[2] = (u32tmp >> 8) & 0xff; + buf[3] = (u32tmp >> 0) & 0xff; + ret = rtl2832_sdr_wr_regs(s, 0x19f, buf, 4); + if (ret) + goto err; + + /* low-pass filter */ + ret = rtl2832_sdr_wr_regs(s, 0x11c, + "\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5", + 20); + if (ret) + goto err; + + ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2); + if (ret) + goto err; + + /* mode */ + ret = rtl2832_sdr_wr_regs(s, 0x019, "\x05", 1); + if (ret) + goto err; + + ret = rtl2832_sdr_wr_regs(s, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6); + if (ret) + goto err; + + /* FSM */ + ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\xf0\x0f", 3); + if (ret) + goto err; + + /* PID filter */ + ret = rtl2832_sdr_wr_regs(s, 0x061, "\x60", 1); + if (ret) + goto err; + + /* used RF tuner based settings */ + switch (s->cfg->tuner) { + case RTL2832_TUNER_E4000: + ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1); + ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1); + ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x30", 1); + ret = rtl2832_sdr_wr_regs(s, 0x104, "\xd0", 1); + ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x18", 1); + ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1); + ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1); + ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1); + ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1); + ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(s, 0x011, "\xd4", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x14", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xec", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1); + ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x83", 1); + ret = rtl2832_sdr_wr_regs(s, 0x010, "\x49", 1); + ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x87", 1); + ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x85", 1); + ret = rtl2832_sdr_wr_regs(s, 0x013, "\x02", 1); + break; + case RTL2832_TUNER_FC0012: + case RTL2832_TUNER_FC0013: + ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1); + ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1); + ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x2c", 1); + ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1); + ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x16", 1); + ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1); + ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1); + ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1); + ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1); + ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(s, 0x011, "\xe9\xbf", 2); + ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x11", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xef", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1); + break; + case RTL2832_TUNER_R820T: + ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1); + ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1); + ret = rtl2832_sdr_wr_regs(s, 0x115, "\x01", 1); + ret = rtl2832_sdr_wr_regs(s, 0x103, "\x80", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x24", 1); + ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1); + ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x14", 1); + ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1); + ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1); + ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1); + ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1); + ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1); + ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(s, 0x011, "\xf4", 1); + break; + default: + dev_notice(&s->udev->dev, "Unsupported tuner\n"); + } + + /* software reset */ + ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x04, 0x04); + if (ret) + goto err; + + ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x00, 0x04); + if (ret) + goto err; +err: + return ret; +}; + +static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_state *s) +{ + int ret; + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + /* PID filter */ + ret = rtl2832_sdr_wr_regs(s, 0x061, "\xe0", 1); + if (ret) + goto err; + + /* mode */ + ret = rtl2832_sdr_wr_regs(s, 0x019, "\x20", 1); + if (ret) + goto err; + + ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2); + if (ret) + goto err; + + /* FSM */ + ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\x0f\xff", 3); + if (ret) + goto err; + + ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x40\x00", 2); + if (ret) + goto err; + + ret = rtl2832_sdr_wr_regs(s, 0x115, "\x06\x3f\xce\xcc", 4); + if (ret) + goto err; +err: + return; +}; + +static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s) +{ + struct dvb_frontend *fe = s->fe; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct v4l2_ctrl *bandwidth_auto; + struct v4l2_ctrl *bandwidth; + + /* + * tuner RF (Hz) + */ + if (s->f_tuner == 0) + return 0; + + /* + * bandwidth (Hz) + */ + bandwidth_auto = v4l2_ctrl_find(&s->hdl, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); + bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); + if (v4l2_ctrl_g_ctrl(bandwidth_auto)) { + c->bandwidth_hz = s->f_adc; + v4l2_ctrl_s_ctrl(bandwidth, s->f_adc); + } else { + c->bandwidth_hz = v4l2_ctrl_g_ctrl(bandwidth); + } + + c->frequency = s->f_tuner; + c->delivery_system = SYS_DVBT; + + dev_dbg(&s->udev->dev, "%s: frequency=%u bandwidth=%d\n", + __func__, c->frequency, c->bandwidth_hz); + + if (!test_bit(POWER_ON, &s->flags)) + return 0; + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + return 0; +}; + +static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_state *s) +{ + struct dvb_frontend *fe = s->fe; + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + if (fe->ops.tuner_ops.init) + fe->ops.tuner_ops.init(fe); + + return 0; +}; + +static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_state *s) +{ + struct dvb_frontend *fe = s->fe; + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + if (fe->ops.tuner_ops.sleep) + fe->ops.tuner_ops.sleep(fe); + + return; +}; + +static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); + int ret; + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + if (!s->udev) + return -ENODEV; + + if (mutex_lock_interruptible(&s->v4l2_lock)) + return -ERESTARTSYS; + + if (s->d->props->power_ctrl) + s->d->props->power_ctrl(s->d, 1); + + set_bit(POWER_ON, &s->flags); + + ret = rtl2832_sdr_set_tuner(s); + if (ret) + goto err; + + ret = rtl2832_sdr_set_tuner_freq(s); + if (ret) + goto err; + + ret = rtl2832_sdr_set_adc(s); + if (ret) + goto err; + + ret = rtl2832_sdr_alloc_stream_bufs(s); + if (ret) + goto err; + + ret = rtl2832_sdr_alloc_urbs(s); + if (ret) + goto err; + + s->sequence = 0; + + ret = rtl2832_sdr_submit_urbs(s); + if (ret) + goto err; + +err: + mutex_unlock(&s->v4l2_lock); + + return ret; +} + +static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) +{ + struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + mutex_lock(&s->v4l2_lock); + + rtl2832_sdr_kill_urbs(s); + rtl2832_sdr_free_urbs(s); + rtl2832_sdr_free_stream_bufs(s); + rtl2832_sdr_cleanup_queued_bufs(s); + rtl2832_sdr_unset_adc(s); + rtl2832_sdr_unset_tuner(s); + + clear_bit(POWER_ON, &s->flags); + + if (s->d->props->power_ctrl) + s->d->props->power_ctrl(s->d, 0); + + mutex_unlock(&s->v4l2_lock); +} + +static struct vb2_ops rtl2832_sdr_vb2_ops = { + .queue_setup = rtl2832_sdr_queue_setup, + .buf_prepare = rtl2832_sdr_buf_prepare, + .buf_queue = rtl2832_sdr_buf_queue, + .start_streaming = rtl2832_sdr_start_streaming, + .stop_streaming = rtl2832_sdr_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int rtl2832_sdr_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct rtl2832_sdr_state *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s: index=%d type=%d\n", + __func__, v->index, v->type); + + if (v->index == 0) { + strlcpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name)); + v->type = V4L2_TUNER_ADC; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = 300000; + v->rangehigh = 3200000; + } else if (v->index == 1) { + strlcpy(v->name, "RF: ", sizeof(v->name)); + v->type = V4L2_TUNER_RF; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = 50000000; + v->rangehigh = 2000000000; + } else { + return -EINVAL; + } + + return 0; +} + +static int rtl2832_sdr_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *v) +{ + struct rtl2832_sdr_state *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + if (v->index > 1) + return -EINVAL; + return 0; +} + +static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + struct rtl2832_sdr_state *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", + __func__, band->tuner, band->type, band->index); + + if (band->tuner == 0) { + if (band->index >= ARRAY_SIZE(bands_adc)) + return -EINVAL; + + *band = bands_adc[band->index]; + } else if (band->tuner == 1) { + if (band->index >= ARRAY_SIZE(bands_fm)) + return -EINVAL; + + *band = bands_fm[band->index]; + } else { + return -EINVAL; + } + + return 0; +} + +static int rtl2832_sdr_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct rtl2832_sdr_state *s = video_drvdata(file); + int ret = 0; + + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", + __func__, f->tuner, f->type); + + if (f->tuner == 0) { + f->frequency = s->f_adc; + f->type = V4L2_TUNER_ADC; + } else if (f->tuner == 1) { + f->frequency = s->f_tuner; + f->type = V4L2_TUNER_RF; + } else { + return -EINVAL; + } + + return ret; +} + +static int rtl2832_sdr_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct rtl2832_sdr_state *s = video_drvdata(file); + int ret, band; + + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", + __func__, f->tuner, f->type, f->frequency); + + /* ADC band midpoints */ + #define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2) + #define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2) + + if (f->tuner == 0 && f->type == V4L2_TUNER_ADC) { + if (f->frequency < BAND_ADC_0) + band = 0; + else if (f->frequency < BAND_ADC_1) + band = 1; + else + band = 2; + + s->f_adc = clamp_t(unsigned int, f->frequency, + bands_adc[band].rangelow, + bands_adc[band].rangehigh); + + dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n", + __func__, s->f_adc); + ret = rtl2832_sdr_set_adc(s); + } else if (f->tuner == 1) { + s->f_tuner = clamp_t(unsigned int, f->frequency, + bands_fm[0].rangelow, + bands_fm[0].rangehigh); + dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n", + __func__, f->frequency); + + ret = rtl2832_sdr_set_tuner_freq(s); + } else { + ret = -EINVAL; + } + + return ret; +} + +static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct rtl2832_sdr_state *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + if (f->index >= NUM_FORMATS) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, sizeof(f->description)); + f->pixelformat = formats[f->index].pixelformat; + + return 0; +} + +static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rtl2832_sdr_state *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + f->fmt.sdr.pixelformat = s->pixelformat; + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + + return 0; +} + +static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rtl2832_sdr_state *s = video_drvdata(file); + struct vb2_queue *q = &s->vb_queue; + int i; + + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + (char *)&f->fmt.sdr.pixelformat); + + if (vb2_is_busy(q)) + return -EBUSY; + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + s->pixelformat = f->fmt.sdr.pixelformat; + return 0; + } + } + + f->fmt.sdr.pixelformat = formats[0].pixelformat; + s->pixelformat = formats[0].pixelformat; + + return 0; +} + +static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rtl2832_sdr_state *s = video_drvdata(file); + int i; + + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + (char *)&f->fmt.sdr.pixelformat); + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) + return 0; + } + + f->fmt.sdr.pixelformat = formats[0].pixelformat; + + return 0; +} + +static const struct v4l2_ioctl_ops rtl2832_sdr_ioctl_ops = { + .vidioc_querycap = rtl2832_sdr_querycap, + + .vidioc_enum_fmt_sdr_cap = rtl2832_sdr_enum_fmt_sdr_cap, + .vidioc_g_fmt_sdr_cap = rtl2832_sdr_g_fmt_sdr_cap, + .vidioc_s_fmt_sdr_cap = rtl2832_sdr_s_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = rtl2832_sdr_try_fmt_sdr_cap, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_g_tuner = rtl2832_sdr_g_tuner, + .vidioc_s_tuner = rtl2832_sdr_s_tuner, + + .vidioc_enum_freq_bands = rtl2832_sdr_enum_freq_bands, + .vidioc_g_frequency = rtl2832_sdr_g_frequency, + .vidioc_s_frequency = rtl2832_sdr_s_frequency, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_log_status = v4l2_ctrl_log_status, +}; + +static const struct v4l2_file_operations rtl2832_sdr_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static struct video_device rtl2832_sdr_template = { + .name = "Realtek RTL2832 SDR", + .release = video_device_release_empty, + .fops = &rtl2832_sdr_fops, + .ioctl_ops = &rtl2832_sdr_ioctl_ops, +}; + +static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rtl2832_sdr_state *s = + container_of(ctrl->handler, struct rtl2832_sdr_state, + hdl); + struct dvb_frontend *fe = s->fe; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + + dev_dbg(&s->udev->dev, + "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", + __func__, ctrl->id, ctrl->name, ctrl->val, + ctrl->minimum, ctrl->maximum, ctrl->step); + + switch (ctrl->id) { + case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: + case V4L2_CID_RF_TUNER_BANDWIDTH: + /* TODO: these controls should be moved to tuner drivers */ + if (s->bandwidth_auto->val) { + /* Round towards the closest legal value */ + s32 val = s->f_adc + s->bandwidth->step / 2; + u32 offset; + + val = clamp_t(s32, val, s->bandwidth->minimum, + s->bandwidth->maximum); + offset = val - s->bandwidth->minimum; + offset = s->bandwidth->step * + (offset / s->bandwidth->step); + s->bandwidth->val = s->bandwidth->minimum + offset; + } + + c->bandwidth_hz = s->bandwidth->val; + + if (!test_bit(POWER_ON, &s->flags)) + return 0; + + if (fe->ops.tuner_ops.set_params) + ret = fe->ops.tuner_ops.set_params(fe); + else + ret = 0; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ctrl_ops rtl2832_sdr_ctrl_ops = { + .s_ctrl = rtl2832_sdr_s_ctrl, +}; + +static void rtl2832_sdr_video_release(struct v4l2_device *v) +{ + struct rtl2832_sdr_state *s = + container_of(v, struct rtl2832_sdr_state, v4l2_dev); + + v4l2_ctrl_handler_free(&s->hdl); + v4l2_device_unregister(&s->v4l2_dev); + kfree(s); +} + +struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, const struct rtl2832_config *cfg, + struct v4l2_subdev *sd) +{ + int ret; + struct rtl2832_sdr_state *s; + const struct v4l2_ctrl_ops *ops = &rtl2832_sdr_ctrl_ops; + struct dvb_usb_device *d = i2c_get_adapdata(i2c); + + s = kzalloc(sizeof(struct rtl2832_sdr_state), GFP_KERNEL); + if (s == NULL) { + dev_err(&d->udev->dev, + "Could not allocate memory for rtl2832_sdr_state\n"); + return NULL; + } + + /* setup the state */ + s->fe = fe; + s->d = d; + s->udev = d->udev; + s->i2c = i2c; + s->cfg = cfg; + s->f_adc = bands_adc[0].rangelow; + s->f_tuner = bands_fm[0].rangelow; + s->pixelformat = V4L2_SDR_FMT_CU8; + + mutex_init(&s->v4l2_lock); + mutex_init(&s->vb_queue_lock); + spin_lock_init(&s->queued_bufs_lock); + INIT_LIST_HEAD(&s->queued_bufs); + + /* Init videobuf2 queue structure */ + s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; + s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + s->vb_queue.drv_priv = s; + s->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf); + s->vb_queue.ops = &rtl2832_sdr_vb2_ops; + s->vb_queue.mem_ops = &vb2_vmalloc_memops; + s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&s->vb_queue); + if (ret) { + dev_err(&s->udev->dev, "Could not initialize vb2 queue\n"); + goto err_free_mem; + } + + /* Register controls */ + switch (s->cfg->tuner) { + case RTL2832_TUNER_E4000: + v4l2_ctrl_handler_init(&s->hdl, 9); + if (sd) + v4l2_ctrl_add_handler(&s->hdl, sd->ctrl_handler, NULL); + break; + case RTL2832_TUNER_R820T: + v4l2_ctrl_handler_init(&s->hdl, 2); + s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, + 0, 1, 1, 1); + s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH, + 0, 8000000, 100000, 0); + v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); + break; + case RTL2832_TUNER_FC0012: + case RTL2832_TUNER_FC0013: + v4l2_ctrl_handler_init(&s->hdl, 2); + s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, + 0, 1, 1, 1); + s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH, + 6000000, 8000000, 1000000, + 6000000); + v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); + break; + default: + v4l2_ctrl_handler_init(&s->hdl, 0); + dev_notice(&s->udev->dev, "%s: Unsupported tuner\n", + KBUILD_MODNAME); + goto err_free_controls; + } + + if (s->hdl.error) { + ret = s->hdl.error; + dev_err(&s->udev->dev, "Could not initialize controls\n"); + goto err_free_controls; + } + + /* Init video_device structure */ + s->vdev = rtl2832_sdr_template; + s->vdev.queue = &s->vb_queue; + s->vdev.queue->lock = &s->vb_queue_lock; + video_set_drvdata(&s->vdev, s); + + /* Register the v4l2_device structure */ + s->v4l2_dev.release = rtl2832_sdr_video_release; + ret = v4l2_device_register(&s->udev->dev, &s->v4l2_dev); + if (ret) { + dev_err(&s->udev->dev, + "Failed to register v4l2-device (%d)\n", ret); + goto err_free_controls; + } + + s->v4l2_dev.ctrl_handler = &s->hdl; + s->vdev.v4l2_dev = &s->v4l2_dev; + s->vdev.lock = &s->v4l2_lock; + s->vdev.vfl_dir = VFL_DIR_RX; + + ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); + if (ret) { + dev_err(&s->udev->dev, + "Failed to register as video device (%d)\n", + ret); + goto err_unregister_v4l2_dev; + } + dev_info(&s->udev->dev, "Registered as %s\n", + video_device_node_name(&s->vdev)); + + fe->sec_priv = s; + fe->ops.release_sec = rtl2832_sdr_release_sec; + + dev_info(&s->i2c->dev, "%s: Realtek RTL2832 SDR attached\n", + KBUILD_MODNAME); + return fe; + +err_unregister_v4l2_dev: + v4l2_device_unregister(&s->v4l2_dev); +err_free_controls: + v4l2_ctrl_handler_free(&s->hdl); +err_free_mem: + kfree(s); + return NULL; +} +EXPORT_SYMBOL(rtl2832_sdr_attach); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Realtek RTL2832 SDR driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.h b/drivers/media/dvb-frontends/rtl2832_sdr.h new file mode 100644 index 0000000..b865fad --- /dev/null +++ b/drivers/media/dvb-frontends/rtl2832_sdr.h @@ -0,0 +1,54 @@ +/* + * Realtek RTL2832U SDR driver + * + * Copyright (C) 2013 Antti Palosaari + * + * 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. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * GNU Radio plugin "gr-kernel" for device usage will be on: + * http://git.linuxtv.org/anttip/gr-kernel.git + * + * TODO: + * Help is very highly welcome for these + all the others you could imagine: + * - move controls to V4L2 API + * - use libv4l2 for stream format conversions + * - gr-kernel: switch to v4l2_mmap (current read eats a lot of cpu) + * - SDRSharp support + */ + +#ifndef RTL2832_SDR_H +#define RTL2832_SDR_H + +#include +#include + +/* for config struct */ +#include "rtl2832.h" + +#if IS_ENABLED(CONFIG_DVB_RTL2832_SDR) +extern struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, const struct rtl2832_config *cfg, + struct v4l2_subdev *sd); +#else +static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, const struct rtl2832_config *cfg, + struct v4l2_subdev *sd) +{ + dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* RTL2832_SDR_H */ diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 037e519..0ea144e 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -129,6 +129,7 @@ config DVB_USB_RTL28XXU depends on DVB_USB_V2 && I2C_MUX select DVB_RTL2830 select DVB_RTL2832 + select DVB_RTL2832_SDR if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 5942414..8abda14 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -40,8 +40,6 @@ source "drivers/staging/media/solo6x10/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" -source "drivers/staging/media/rtl2832u_sdr/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 730c265..74a9d5a 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -11,5 +11,4 @@ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_OMAP2) += omap24xx/ obj-$(CONFIG_VIDEO_TCM825X) += omap24xx/ -obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832u_sdr/ diff --git a/drivers/staging/media/rtl2832u_sdr/Kconfig b/drivers/staging/media/rtl2832u_sdr/Kconfig deleted file mode 100644 index 3ede5fe..0000000 --- a/drivers/staging/media/rtl2832u_sdr/Kconfig +++ /dev/null @@ -1,7 +0,0 @@ -config DVB_RTL2832_SDR - tristate "Realtek RTL2832 SDR" - depends on USB && DVB_CORE && I2C && VIDEO_V4L2 && DVB_USB_RTL28XXU - select DVB_RTL2832 - select VIDEOBUF2_VMALLOC - default m if !MEDIA_SUBDRV_AUTOSELECT - diff --git a/drivers/staging/media/rtl2832u_sdr/Makefile b/drivers/staging/media/rtl2832u_sdr/Makefile deleted file mode 100644 index 7e00a0d..0000000 --- a/drivers/staging/media/rtl2832u_sdr/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o - -ccflags-y += -Idrivers/media/dvb-core -ccflags-y += -Idrivers/media/dvb-frontends -ccflags-y += -Idrivers/media/tuners -ccflags-y += -Idrivers/media/usb/dvb-usb-v2 diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c deleted file mode 100644 index f81b1c8..0000000 --- a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c +++ /dev/null @@ -1,1528 +0,0 @@ -/* - * Realtek RTL2832U SDR driver - * - * Copyright (C) 2013 Antti Palosaari - * - * 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. - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * GNU Radio plugin "gr-kernel" for device usage will be on: - * http://git.linuxtv.org/anttip/gr-kernel.git - * - */ - -#include "dvb_frontend.h" -#include "rtl2832_sdr.h" -#include "dvb_usb.h" - -#include -#include -#include -#include -#include - -#include -#include - -#define MAX_BULK_BUFS (10) -#define BULK_BUFFER_SIZE (128 * 512) - -static const struct v4l2_frequency_band bands_adc[] = { - { - .tuner = 0, - .type = V4L2_TUNER_ADC, - .index = 0, - .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 300000, - .rangehigh = 300000, - }, - { - .tuner = 0, - .type = V4L2_TUNER_ADC, - .index = 1, - .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 900001, - .rangehigh = 2800000, - }, - { - .tuner = 0, - .type = V4L2_TUNER_ADC, - .index = 2, - .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 3200000, - .rangehigh = 3200000, - }, -}; - -static const struct v4l2_frequency_band bands_fm[] = { - { - .tuner = 1, - .type = V4L2_TUNER_RF, - .index = 0, - .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 50000000, - .rangehigh = 2000000000, - }, -}; - -/* stream formats */ -struct rtl2832_sdr_format { - char *name; - u32 pixelformat; -}; - -static struct rtl2832_sdr_format formats[] = { - { - .name = "IQ U8", - .pixelformat = V4L2_SDR_FMT_CU8, - }, { - .name = "IQ U16LE (emulated)", - .pixelformat = V4L2_SDR_FMT_CU16LE, - }, -}; - -static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); - -/* intermediate buffers with raw data from the USB device */ -struct rtl2832_sdr_frame_buf { - struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ - struct list_head list; -}; - -struct rtl2832_sdr_state { -#define POWER_ON (1 << 1) -#define URB_BUF (1 << 2) - unsigned long flags; - - const struct rtl2832_config *cfg; - struct dvb_frontend *fe; - struct dvb_usb_device *d; - struct i2c_adapter *i2c; - u8 bank; - - struct video_device vdev; - struct v4l2_device v4l2_dev; - - /* videobuf2 queue and queued buffers list */ - struct vb2_queue vb_queue; - struct list_head queued_bufs; - spinlock_t queued_bufs_lock; /* Protects queued_bufs */ - unsigned sequence; /* buffer sequence counter */ - - /* Note if taking both locks v4l2_lock must always be locked first! */ - struct mutex v4l2_lock; /* Protects everything else */ - struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ - - /* Pointer to our usb_device, will be NULL after unplug */ - struct usb_device *udev; /* Both mutexes most be hold when setting! */ - - unsigned int vb_full; /* vb is full and packets dropped */ - - struct urb *urb_list[MAX_BULK_BUFS]; - int buf_num; - unsigned long buf_size; - u8 *buf_list[MAX_BULK_BUFS]; - dma_addr_t dma_addr[MAX_BULK_BUFS]; - int urbs_initialized; - int urbs_submitted; - - unsigned int f_adc, f_tuner; - u32 pixelformat; - - /* Controls */ - struct v4l2_ctrl_handler hdl; - struct v4l2_ctrl *bandwidth_auto; - struct v4l2_ctrl *bandwidth; - - /* for sample rate calc */ - unsigned int sample; - unsigned int sample_measured; - unsigned long jiffies_next; -}; - -/* write multiple hardware registers */ -static int rtl2832_sdr_wr(struct rtl2832_sdr_state *s, u8 reg, const u8 *val, - int len) -{ - int ret; -#define MAX_WR_LEN 24 -#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1) - u8 buf[MAX_WR_XFER_LEN]; - struct i2c_msg msg[1] = { - { - .addr = s->cfg->i2c_addr, - .flags = 0, - .len = 1 + len, - .buf = buf, - } - }; - - if (WARN_ON(len > MAX_WR_LEN)) - return -EINVAL; - - buf[0] = reg; - memcpy(&buf[1], val, len); - - ret = i2c_transfer(s->i2c, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_err(&s->i2c->dev, - "%s: I2C wr failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - return ret; -} - -/* read multiple hardware registers */ -static int rtl2832_sdr_rd(struct rtl2832_sdr_state *s, u8 reg, u8 *val, int len) -{ - int ret; - struct i2c_msg msg[2] = { - { - .addr = s->cfg->i2c_addr, - .flags = 0, - .len = 1, - .buf = ®, - }, { - .addr = s->cfg->i2c_addr, - .flags = I2C_M_RD, - .len = len, - .buf = val, - } - }; - - ret = i2c_transfer(s->i2c, msg, 2); - if (ret == 2) { - ret = 0; - } else { - dev_err(&s->i2c->dev, - "%s: I2C rd failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - return ret; -} - -/* write multiple registers */ -static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_state *s, u16 reg, - const u8 *val, int len) -{ - int ret; - u8 reg2 = (reg >> 0) & 0xff; - u8 bank = (reg >> 8) & 0xff; - - /* switch bank if needed */ - if (bank != s->bank) { - ret = rtl2832_sdr_wr(s, 0x00, &bank, 1); - if (ret) - return ret; - - s->bank = bank; - } - - return rtl2832_sdr_wr(s, reg2, val, len); -} - -/* read multiple registers */ -static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_state *s, u16 reg, u8 *val, - int len) -{ - int ret; - u8 reg2 = (reg >> 0) & 0xff; - u8 bank = (reg >> 8) & 0xff; - - /* switch bank if needed */ - if (bank != s->bank) { - ret = rtl2832_sdr_wr(s, 0x00, &bank, 1); - if (ret) - return ret; - - s->bank = bank; - } - - return rtl2832_sdr_rd(s, reg2, val, len); -} - -/* write single register */ -static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_state *s, u16 reg, u8 val) -{ - return rtl2832_sdr_wr_regs(s, reg, &val, 1); -} - -#if 0 -/* read single register */ -static int rtl2832_sdr_rd_reg(struct rtl2832_sdr_state *s, u16 reg, u8 *val) -{ - return rtl2832_sdr_rd_regs(s, reg, val, 1); -} -#endif - -/* write single register with mask */ -static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_state *s, u16 reg, - u8 val, u8 mask) -{ - int ret; - u8 tmp; - - /* no need for read if whole reg is written */ - if (mask != 0xff) { - ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1); - if (ret) - return ret; - - val &= mask; - tmp &= ~mask; - val |= tmp; - } - - return rtl2832_sdr_wr_regs(s, reg, &val, 1); -} - -#if 0 -/* read single register with mask */ -static int rtl2832_sdr_rd_reg_mask(struct rtl2832_sdr_state *s, u16 reg, - u8 *val, u8 mask) -{ - int ret, i; - u8 tmp; - - ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1); - if (ret) - return ret; - - tmp &= mask; - - /* find position of the first bit */ - for (i = 0; i < 8; i++) { - if ((mask >> i) & 0x01) - break; - } - *val = tmp >> i; - - return 0; -} -#endif - -/* Private functions */ -static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf( - struct rtl2832_sdr_state *s) -{ - unsigned long flags = 0; - struct rtl2832_sdr_frame_buf *buf = NULL; - - spin_lock_irqsave(&s->queued_bufs_lock, flags); - if (list_empty(&s->queued_bufs)) - goto leave; - - buf = list_entry(s->queued_bufs.next, - struct rtl2832_sdr_frame_buf, list); - list_del(&buf->list); -leave: - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); - return buf; -} - -static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s, - void *dst, const u8 *src, unsigned int src_len) -{ - unsigned int dst_len; - - if (s->pixelformat == V4L2_SDR_FMT_CU8) { - /* native stream, no need to convert */ - memcpy(dst, src, src_len); - dst_len = src_len; - } else if (s->pixelformat == V4L2_SDR_FMT_CU16LE) { - /* convert u8 to u16 */ - unsigned int i; - u16 *u16dst = dst; - - for (i = 0; i < src_len; i++) - *u16dst++ = (src[i] << 8) | (src[i] >> 0); - dst_len = 2 * src_len; - } else { - dst_len = 0; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if (unlikely(time_is_before_jiffies(s->jiffies_next))) { -#define MSECS 10000UL - unsigned int samples = s->sample - s->sample_measured; - - s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); - s->sample_measured = s->sample; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, MSECS, - samples * 1000UL / MSECS); - } - - /* total number of I+Q pairs */ - s->sample += src_len / 2; - - return dst_len; -} - -/* - * This gets called for the bulk stream pipe. This is done in interrupt - * time, so it has to be fast, not crash, and not stall. Neat. - */ -static void rtl2832_sdr_urb_complete(struct urb *urb) -{ - struct rtl2832_sdr_state *s = urb->context; - struct rtl2832_sdr_frame_buf *fbuf; - - dev_dbg_ratelimited(&s->udev->dev, - "%s: status=%d length=%d/%d errors=%d\n", - __func__, urb->status, urb->actual_length, - urb->transfer_buffer_length, urb->error_count); - - switch (urb->status) { - case 0: /* success */ - case -ETIMEDOUT: /* NAK */ - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* error */ - dev_err_ratelimited(&s->udev->dev, "urb failed=%d\n", - urb->status); - break; - } - - if (likely(urb->actual_length > 0)) { - void *ptr; - unsigned int len; - /* get free framebuffer */ - fbuf = rtl2832_sdr_get_next_fill_buf(s); - if (unlikely(fbuf == NULL)) { - s->vb_full++; - dev_notice_ratelimited(&s->udev->dev, - "videobuf is full, %d packets dropped\n", - s->vb_full); - goto skip; - } - - /* fill framebuffer */ - ptr = vb2_plane_vaddr(&fbuf->vb, 0); - len = rtl2832_sdr_convert_stream(s, ptr, urb->transfer_buffer, - urb->actual_length); - vb2_set_plane_payload(&fbuf->vb, 0, len); - v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp); - fbuf->vb.v4l2_buf.sequence = s->sequence++; - vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); - } -skip: - usb_submit_urb(urb, GFP_ATOMIC); -} - -static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_state *s) -{ - int i; - - for (i = s->urbs_submitted - 1; i >= 0; i--) { - dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i); - /* stop the URB */ - usb_kill_urb(s->urb_list[i]); - } - s->urbs_submitted = 0; - - return 0; -} - -static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_state *s) -{ - int i, ret; - - for (i = 0; i < s->urbs_initialized; i++) { - dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i); - ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC); - if (ret) { - dev_err(&s->udev->dev, - "Could not submit urb no. %d - get them all back\n", - i); - rtl2832_sdr_kill_urbs(s); - return ret; - } - s->urbs_submitted++; - } - - return 0; -} - -static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_state *s) -{ - if (s->flags & USB_STATE_URB_BUF) { - while (s->buf_num) { - s->buf_num--; - dev_dbg(&s->udev->dev, "%s: free buf=%d\n", - __func__, s->buf_num); - usb_free_coherent(s->udev, s->buf_size, - s->buf_list[s->buf_num], - s->dma_addr[s->buf_num]); - } - } - s->flags &= ~USB_STATE_URB_BUF; - - return 0; -} - -static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_state *s) -{ - s->buf_num = 0; - s->buf_size = BULK_BUFFER_SIZE; - - dev_dbg(&s->udev->dev, - "%s: all in all I will use %u bytes for streaming\n", - __func__, MAX_BULK_BUFS * BULK_BUFFER_SIZE); - - for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) { - s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev, - BULK_BUFFER_SIZE, GFP_ATOMIC, - &s->dma_addr[s->buf_num]); - if (!s->buf_list[s->buf_num]) { - dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n", - __func__, s->buf_num); - rtl2832_sdr_free_stream_bufs(s); - return -ENOMEM; - } - - dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n", - __func__, s->buf_num, - s->buf_list[s->buf_num], - (long long)s->dma_addr[s->buf_num]); - s->flags |= USB_STATE_URB_BUF; - } - - return 0; -} - -static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_state *s) -{ - int i; - - rtl2832_sdr_kill_urbs(s); - - for (i = s->urbs_initialized - 1; i >= 0; i--) { - if (s->urb_list[i]) { - dev_dbg(&s->udev->dev, "%s: free urb=%d\n", - __func__, i); - /* free the URBs */ - usb_free_urb(s->urb_list[i]); - } - } - s->urbs_initialized = 0; - - return 0; -} - -static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s) -{ - int i, j; - - /* allocate the URBs */ - for (i = 0; i < MAX_BULK_BUFS; i++) { - dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i); - s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); - if (!s->urb_list[i]) { - dev_dbg(&s->udev->dev, "%s: failed\n", __func__); - for (j = 0; j < i; j++) - usb_free_urb(s->urb_list[j]); - return -ENOMEM; - } - usb_fill_bulk_urb(s->urb_list[i], - s->udev, - usb_rcvbulkpipe(s->udev, 0x81), - s->buf_list[i], - BULK_BUFFER_SIZE, - rtl2832_sdr_urb_complete, s); - - s->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - s->urb_list[i]->transfer_dma = s->dma_addr[i]; - s->urbs_initialized++; - } - - return 0; -} - -/* Must be called with vb_queue_lock hold */ -static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s) -{ - unsigned long flags = 0; - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - spin_lock_irqsave(&s->queued_bufs_lock, flags); - while (!list_empty(&s->queued_bufs)) { - struct rtl2832_sdr_frame_buf *buf; - - buf = list_entry(s->queued_bufs.next, - struct rtl2832_sdr_frame_buf, list); - list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - } - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); -} - -/* The user yanked out the cable... */ -static void rtl2832_sdr_release_sec(struct dvb_frontend *fe) -{ - struct rtl2832_sdr_state *s = fe->sec_priv; - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - mutex_lock(&s->vb_queue_lock); - mutex_lock(&s->v4l2_lock); - /* No need to keep the urbs around after disconnection */ - s->udev = NULL; - - v4l2_device_disconnect(&s->v4l2_dev); - video_unregister_device(&s->vdev); - mutex_unlock(&s->v4l2_lock); - mutex_unlock(&s->vb_queue_lock); - - v4l2_device_put(&s->v4l2_dev); - - fe->sec_priv = NULL; -} - -static int rtl2832_sdr_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct rtl2832_sdr_state *s = video_drvdata(file); - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); - strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); - usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE | V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -/* Videobuf2 operations */ -static int rtl2832_sdr_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) -{ - struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); - - dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); - - /* Need at least 8 buffers */ - if (vq->num_buffers + *nbuffers < 8) - *nbuffers = 8 - vq->num_buffers; - *nplanes = 1; - /* 2 = max 16-bit sample returned */ - sizes[0] = PAGE_ALIGN(BULK_BUFFER_SIZE * 2); - dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", - __func__, *nbuffers, sizes[0]); - return 0; -} - -static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb) -{ - struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue); - - /* Don't allow queing new buffers after device disconnection */ - if (!s->udev) - return -ENODEV; - - return 0; -} - -static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb) -{ - struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue); - struct rtl2832_sdr_frame_buf *buf = - container_of(vb, struct rtl2832_sdr_frame_buf, vb); - unsigned long flags = 0; - - /* Check the device has not disconnected between prep and queuing */ - if (!s->udev) { - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - return; - } - - spin_lock_irqsave(&s->queued_bufs_lock, flags); - list_add_tail(&buf->list, &s->queued_bufs); - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); -} - -static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s) -{ - struct dvb_frontend *fe = s->fe; - int ret; - unsigned int f_sr, f_if; - u8 buf[4], u8tmp1, u8tmp2; - u64 u64tmp; - u32 u32tmp; - - dev_dbg(&s->udev->dev, "%s: f_adc=%u\n", __func__, s->f_adc); - - if (!test_bit(POWER_ON, &s->flags)) - return 0; - - if (s->f_adc == 0) - return 0; - - f_sr = s->f_adc; - - ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x00\x00", 2); - if (ret) - goto err; - - ret = rtl2832_sdr_wr_regs(s, 0x115, "\x00\x00\x00\x00", 4); - if (ret) - goto err; - - /* get IF from tuner */ - if (fe->ops.tuner_ops.get_if_frequency) - ret = fe->ops.tuner_ops.get_if_frequency(fe, &f_if); - else - ret = -EINVAL; - - if (ret) - goto err; - - /* program IF */ - u64tmp = f_if % s->cfg->xtal; - u64tmp *= 0x400000; - u64tmp = div_u64(u64tmp, s->cfg->xtal); - u64tmp = -u64tmp; - u32tmp = u64tmp & 0x3fffff; - - dev_dbg(&s->udev->dev, "%s: f_if=%u if_ctl=%08x\n", - __func__, f_if, u32tmp); - - buf[0] = (u32tmp >> 16) & 0xff; - buf[1] = (u32tmp >> 8) & 0xff; - buf[2] = (u32tmp >> 0) & 0xff; - - ret = rtl2832_sdr_wr_regs(s, 0x119, buf, 3); - if (ret) - goto err; - - /* BB / IF mode */ - /* POR: 0x1b1=0x1f, 0x008=0x0d, 0x006=0x80 */ - if (f_if) { - u8tmp1 = 0x1a; /* disable Zero-IF */ - u8tmp2 = 0x8d; /* enable ADC I */ - } else { - u8tmp1 = 0x1b; /* enable Zero-IF, DC, IQ */ - u8tmp2 = 0xcd; /* enable ADC I, ADC Q */ - } - - ret = rtl2832_sdr_wr_reg(s, 0x1b1, u8tmp1); - if (ret) - goto err; - - ret = rtl2832_sdr_wr_reg(s, 0x008, u8tmp2); - if (ret) - goto err; - - ret = rtl2832_sdr_wr_reg(s, 0x006, 0x80); - if (ret) - goto err; - - /* program sampling rate (resampling down) */ - u32tmp = div_u64(s->cfg->xtal * 0x400000ULL, f_sr * 4U); - u32tmp <<= 2; - buf[0] = (u32tmp >> 24) & 0xff; - buf[1] = (u32tmp >> 16) & 0xff; - buf[2] = (u32tmp >> 8) & 0xff; - buf[3] = (u32tmp >> 0) & 0xff; - ret = rtl2832_sdr_wr_regs(s, 0x19f, buf, 4); - if (ret) - goto err; - - /* low-pass filter */ - ret = rtl2832_sdr_wr_regs(s, 0x11c, - "\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5", - 20); - if (ret) - goto err; - - ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2); - if (ret) - goto err; - - /* mode */ - ret = rtl2832_sdr_wr_regs(s, 0x019, "\x05", 1); - if (ret) - goto err; - - ret = rtl2832_sdr_wr_regs(s, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6); - if (ret) - goto err; - - /* FSM */ - ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\xf0\x0f", 3); - if (ret) - goto err; - - /* PID filter */ - ret = rtl2832_sdr_wr_regs(s, 0x061, "\x60", 1); - if (ret) - goto err; - - /* used RF tuner based settings */ - switch (s->cfg->tuner) { - case RTL2832_TUNER_E4000: - ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x30", 1); - ret = rtl2832_sdr_wr_regs(s, 0x104, "\xd0", 1); - ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x18", 1); - ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x011, "\xd4", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x14", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xec", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x83", 1); - ret = rtl2832_sdr_wr_regs(s, 0x010, "\x49", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x87", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x85", 1); - ret = rtl2832_sdr_wr_regs(s, 0x013, "\x02", 1); - break; - case RTL2832_TUNER_FC0012: - case RTL2832_TUNER_FC0013: - ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x2c", 1); - ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x16", 1); - ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x011, "\xe9\xbf", 2); - ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x11", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xef", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1); - break; - case RTL2832_TUNER_R820T: - ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x115, "\x01", 1); - ret = rtl2832_sdr_wr_regs(s, 0x103, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x24", 1); - ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x14", 1); - ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x011, "\xf4", 1); - break; - default: - dev_notice(&s->udev->dev, "Unsupported tuner\n"); - } - - /* software reset */ - ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x04, 0x04); - if (ret) - goto err; - - ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x00, 0x04); - if (ret) - goto err; -err: - return ret; -}; - -static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_state *s) -{ - int ret; - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - /* PID filter */ - ret = rtl2832_sdr_wr_regs(s, 0x061, "\xe0", 1); - if (ret) - goto err; - - /* mode */ - ret = rtl2832_sdr_wr_regs(s, 0x019, "\x20", 1); - if (ret) - goto err; - - ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2); - if (ret) - goto err; - - /* FSM */ - ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\x0f\xff", 3); - if (ret) - goto err; - - ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x40\x00", 2); - if (ret) - goto err; - - ret = rtl2832_sdr_wr_regs(s, 0x115, "\x06\x3f\xce\xcc", 4); - if (ret) - goto err; -err: - return; -}; - -static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s) -{ - struct dvb_frontend *fe = s->fe; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct v4l2_ctrl *bandwidth_auto; - struct v4l2_ctrl *bandwidth; - - /* - * tuner RF (Hz) - */ - if (s->f_tuner == 0) - return 0; - - /* - * bandwidth (Hz) - */ - bandwidth_auto = v4l2_ctrl_find(&s->hdl, - V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); - bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); - if (v4l2_ctrl_g_ctrl(bandwidth_auto)) { - c->bandwidth_hz = s->f_adc; - v4l2_ctrl_s_ctrl(bandwidth, s->f_adc); - } else { - c->bandwidth_hz = v4l2_ctrl_g_ctrl(bandwidth); - } - - c->frequency = s->f_tuner; - c->delivery_system = SYS_DVBT; - - dev_dbg(&s->udev->dev, "%s: frequency=%u bandwidth=%d\n", - __func__, c->frequency, c->bandwidth_hz); - - if (!test_bit(POWER_ON, &s->flags)) - return 0; - - if (fe->ops.tuner_ops.set_params) - fe->ops.tuner_ops.set_params(fe); - - return 0; -}; - -static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_state *s) -{ - struct dvb_frontend *fe = s->fe; - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - if (fe->ops.tuner_ops.init) - fe->ops.tuner_ops.init(fe); - - return 0; -}; - -static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_state *s) -{ - struct dvb_frontend *fe = s->fe; - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - if (fe->ops.tuner_ops.sleep) - fe->ops.tuner_ops.sleep(fe); - - return; -}; - -static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); - int ret; - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - if (!s->udev) - return -ENODEV; - - if (mutex_lock_interruptible(&s->v4l2_lock)) - return -ERESTARTSYS; - - if (s->d->props->power_ctrl) - s->d->props->power_ctrl(s->d, 1); - - set_bit(POWER_ON, &s->flags); - - ret = rtl2832_sdr_set_tuner(s); - if (ret) - goto err; - - ret = rtl2832_sdr_set_tuner_freq(s); - if (ret) - goto err; - - ret = rtl2832_sdr_set_adc(s); - if (ret) - goto err; - - ret = rtl2832_sdr_alloc_stream_bufs(s); - if (ret) - goto err; - - ret = rtl2832_sdr_alloc_urbs(s); - if (ret) - goto err; - - s->sequence = 0; - - ret = rtl2832_sdr_submit_urbs(s); - if (ret) - goto err; - -err: - mutex_unlock(&s->v4l2_lock); - - return ret; -} - -static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) -{ - struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - mutex_lock(&s->v4l2_lock); - - rtl2832_sdr_kill_urbs(s); - rtl2832_sdr_free_urbs(s); - rtl2832_sdr_free_stream_bufs(s); - rtl2832_sdr_cleanup_queued_bufs(s); - rtl2832_sdr_unset_adc(s); - rtl2832_sdr_unset_tuner(s); - - clear_bit(POWER_ON, &s->flags); - - if (s->d->props->power_ctrl) - s->d->props->power_ctrl(s->d, 0); - - mutex_unlock(&s->v4l2_lock); -} - -static struct vb2_ops rtl2832_sdr_vb2_ops = { - .queue_setup = rtl2832_sdr_queue_setup, - .buf_prepare = rtl2832_sdr_buf_prepare, - .buf_queue = rtl2832_sdr_buf_queue, - .start_streaming = rtl2832_sdr_start_streaming, - .stop_streaming = rtl2832_sdr_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int rtl2832_sdr_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - struct rtl2832_sdr_state *s = video_drvdata(file); - - dev_dbg(&s->udev->dev, "%s: index=%d type=%d\n", - __func__, v->index, v->type); - - if (v->index == 0) { - strlcpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name)); - v->type = V4L2_TUNER_ADC; - v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; - v->rangelow = 300000; - v->rangehigh = 3200000; - } else if (v->index == 1) { - strlcpy(v->name, "RF: ", sizeof(v->name)); - v->type = V4L2_TUNER_RF; - v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; - v->rangelow = 50000000; - v->rangehigh = 2000000000; - } else { - return -EINVAL; - } - - return 0; -} - -static int rtl2832_sdr_s_tuner(struct file *file, void *priv, - const struct v4l2_tuner *v) -{ - struct rtl2832_sdr_state *s = video_drvdata(file); - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - if (v->index > 1) - return -EINVAL; - return 0; -} - -static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv, - struct v4l2_frequency_band *band) -{ - struct rtl2832_sdr_state *s = video_drvdata(file); - - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", - __func__, band->tuner, band->type, band->index); - - if (band->tuner == 0) { - if (band->index >= ARRAY_SIZE(bands_adc)) - return -EINVAL; - - *band = bands_adc[band->index]; - } else if (band->tuner == 1) { - if (band->index >= ARRAY_SIZE(bands_fm)) - return -EINVAL; - - *band = bands_fm[band->index]; - } else { - return -EINVAL; - } - - return 0; -} - -static int rtl2832_sdr_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct rtl2832_sdr_state *s = video_drvdata(file); - int ret = 0; - - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", - __func__, f->tuner, f->type); - - if (f->tuner == 0) { - f->frequency = s->f_adc; - f->type = V4L2_TUNER_ADC; - } else if (f->tuner == 1) { - f->frequency = s->f_tuner; - f->type = V4L2_TUNER_RF; - } else { - return -EINVAL; - } - - return ret; -} - -static int rtl2832_sdr_s_frequency(struct file *file, void *priv, - const struct v4l2_frequency *f) -{ - struct rtl2832_sdr_state *s = video_drvdata(file); - int ret, band; - - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", - __func__, f->tuner, f->type, f->frequency); - - /* ADC band midpoints */ - #define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2) - #define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2) - - if (f->tuner == 0 && f->type == V4L2_TUNER_ADC) { - if (f->frequency < BAND_ADC_0) - band = 0; - else if (f->frequency < BAND_ADC_1) - band = 1; - else - band = 2; - - s->f_adc = clamp_t(unsigned int, f->frequency, - bands_adc[band].rangelow, - bands_adc[band].rangehigh); - - dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n", - __func__, s->f_adc); - ret = rtl2832_sdr_set_adc(s); - } else if (f->tuner == 1) { - s->f_tuner = clamp_t(unsigned int, f->frequency, - bands_fm[0].rangelow, - bands_fm[0].rangehigh); - dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n", - __func__, f->frequency); - - ret = rtl2832_sdr_set_tuner_freq(s); - } else { - ret = -EINVAL; - } - - return ret; -} - -static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct rtl2832_sdr_state *s = video_drvdata(file); - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - if (f->index >= NUM_FORMATS) - return -EINVAL; - - strlcpy(f->description, formats[f->index].name, sizeof(f->description)); - f->pixelformat = formats[f->index].pixelformat; - - return 0; -} - -static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct rtl2832_sdr_state *s = video_drvdata(file); - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - f->fmt.sdr.pixelformat = s->pixelformat; - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - - return 0; -} - -static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct rtl2832_sdr_state *s = video_drvdata(file); - struct vb2_queue *q = &s->vb_queue; - int i; - - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, - (char *)&f->fmt.sdr.pixelformat); - - if (vb2_is_busy(q)) - return -EBUSY; - - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { - s->pixelformat = f->fmt.sdr.pixelformat; - return 0; - } - } - - f->fmt.sdr.pixelformat = formats[0].pixelformat; - s->pixelformat = formats[0].pixelformat; - - return 0; -} - -static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct rtl2832_sdr_state *s = video_drvdata(file); - int i; - - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, - (char *)&f->fmt.sdr.pixelformat); - - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].pixelformat == f->fmt.sdr.pixelformat) - return 0; - } - - f->fmt.sdr.pixelformat = formats[0].pixelformat; - - return 0; -} - -static const struct v4l2_ioctl_ops rtl2832_sdr_ioctl_ops = { - .vidioc_querycap = rtl2832_sdr_querycap, - - .vidioc_enum_fmt_sdr_cap = rtl2832_sdr_enum_fmt_sdr_cap, - .vidioc_g_fmt_sdr_cap = rtl2832_sdr_g_fmt_sdr_cap, - .vidioc_s_fmt_sdr_cap = rtl2832_sdr_s_fmt_sdr_cap, - .vidioc_try_fmt_sdr_cap = rtl2832_sdr_try_fmt_sdr_cap, - - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_g_tuner = rtl2832_sdr_g_tuner, - .vidioc_s_tuner = rtl2832_sdr_s_tuner, - - .vidioc_enum_freq_bands = rtl2832_sdr_enum_freq_bands, - .vidioc_g_frequency = rtl2832_sdr_g_frequency, - .vidioc_s_frequency = rtl2832_sdr_s_frequency, - - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - .vidioc_log_status = v4l2_ctrl_log_status, -}; - -static const struct v4l2_file_operations rtl2832_sdr_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static struct video_device rtl2832_sdr_template = { - .name = "Realtek RTL2832 SDR", - .release = video_device_release_empty, - .fops = &rtl2832_sdr_fops, - .ioctl_ops = &rtl2832_sdr_ioctl_ops, -}; - -static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct rtl2832_sdr_state *s = - container_of(ctrl->handler, struct rtl2832_sdr_state, - hdl); - struct dvb_frontend *fe = s->fe; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret; - - dev_dbg(&s->udev->dev, - "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", - __func__, ctrl->id, ctrl->name, ctrl->val, - ctrl->minimum, ctrl->maximum, ctrl->step); - - switch (ctrl->id) { - case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: - case V4L2_CID_RF_TUNER_BANDWIDTH: - /* TODO: these controls should be moved to tuner drivers */ - if (s->bandwidth_auto->val) { - /* Round towards the closest legal value */ - s32 val = s->f_adc + s->bandwidth->step / 2; - u32 offset; - - val = clamp_t(s32, val, s->bandwidth->minimum, - s->bandwidth->maximum); - offset = val - s->bandwidth->minimum; - offset = s->bandwidth->step * - (offset / s->bandwidth->step); - s->bandwidth->val = s->bandwidth->minimum + offset; - } - - c->bandwidth_hz = s->bandwidth->val; - - if (!test_bit(POWER_ON, &s->flags)) - return 0; - - if (fe->ops.tuner_ops.set_params) - ret = fe->ops.tuner_ops.set_params(fe); - else - ret = 0; - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static const struct v4l2_ctrl_ops rtl2832_sdr_ctrl_ops = { - .s_ctrl = rtl2832_sdr_s_ctrl, -}; - -static void rtl2832_sdr_video_release(struct v4l2_device *v) -{ - struct rtl2832_sdr_state *s = - container_of(v, struct rtl2832_sdr_state, v4l2_dev); - - v4l2_ctrl_handler_free(&s->hdl); - v4l2_device_unregister(&s->v4l2_dev); - kfree(s); -} - -struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct rtl2832_config *cfg, - struct v4l2_subdev *sd) -{ - int ret; - struct rtl2832_sdr_state *s; - const struct v4l2_ctrl_ops *ops = &rtl2832_sdr_ctrl_ops; - struct dvb_usb_device *d = i2c_get_adapdata(i2c); - - s = kzalloc(sizeof(struct rtl2832_sdr_state), GFP_KERNEL); - if (s == NULL) { - dev_err(&d->udev->dev, - "Could not allocate memory for rtl2832_sdr_state\n"); - return NULL; - } - - /* setup the state */ - s->fe = fe; - s->d = d; - s->udev = d->udev; - s->i2c = i2c; - s->cfg = cfg; - s->f_adc = bands_adc[0].rangelow; - s->f_tuner = bands_fm[0].rangelow; - s->pixelformat = V4L2_SDR_FMT_CU8; - - mutex_init(&s->v4l2_lock); - mutex_init(&s->vb_queue_lock); - spin_lock_init(&s->queued_bufs_lock); - INIT_LIST_HEAD(&s->queued_bufs); - - /* Init videobuf2 queue structure */ - s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; - s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - s->vb_queue.drv_priv = s; - s->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf); - s->vb_queue.ops = &rtl2832_sdr_vb2_ops; - s->vb_queue.mem_ops = &vb2_vmalloc_memops; - s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - ret = vb2_queue_init(&s->vb_queue); - if (ret) { - dev_err(&s->udev->dev, "Could not initialize vb2 queue\n"); - goto err_free_mem; - } - - /* Register controls */ - switch (s->cfg->tuner) { - case RTL2832_TUNER_E4000: - v4l2_ctrl_handler_init(&s->hdl, 9); - if (sd) - v4l2_ctrl_add_handler(&s->hdl, sd->ctrl_handler, NULL); - break; - case RTL2832_TUNER_R820T: - v4l2_ctrl_handler_init(&s->hdl, 2); - s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, - V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, - 0, 1, 1, 1); - s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, - V4L2_CID_RF_TUNER_BANDWIDTH, - 0, 8000000, 100000, 0); - v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); - break; - case RTL2832_TUNER_FC0012: - case RTL2832_TUNER_FC0013: - v4l2_ctrl_handler_init(&s->hdl, 2); - s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, - V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, - 0, 1, 1, 1); - s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, - V4L2_CID_RF_TUNER_BANDWIDTH, - 6000000, 8000000, 1000000, - 6000000); - v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); - break; - default: - v4l2_ctrl_handler_init(&s->hdl, 0); - dev_notice(&s->udev->dev, "%s: Unsupported tuner\n", - KBUILD_MODNAME); - goto err_free_controls; - } - - if (s->hdl.error) { - ret = s->hdl.error; - dev_err(&s->udev->dev, "Could not initialize controls\n"); - goto err_free_controls; - } - - /* Init video_device structure */ - s->vdev = rtl2832_sdr_template; - s->vdev.queue = &s->vb_queue; - s->vdev.queue->lock = &s->vb_queue_lock; - video_set_drvdata(&s->vdev, s); - - /* Register the v4l2_device structure */ - s->v4l2_dev.release = rtl2832_sdr_video_release; - ret = v4l2_device_register(&s->udev->dev, &s->v4l2_dev); - if (ret) { - dev_err(&s->udev->dev, - "Failed to register v4l2-device (%d)\n", ret); - goto err_free_controls; - } - - s->v4l2_dev.ctrl_handler = &s->hdl; - s->vdev.v4l2_dev = &s->v4l2_dev; - s->vdev.lock = &s->v4l2_lock; - s->vdev.vfl_dir = VFL_DIR_RX; - - ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); - if (ret) { - dev_err(&s->udev->dev, - "Failed to register as video device (%d)\n", - ret); - goto err_unregister_v4l2_dev; - } - dev_info(&s->udev->dev, "Registered as %s\n", - video_device_node_name(&s->vdev)); - - fe->sec_priv = s; - fe->ops.release_sec = rtl2832_sdr_release_sec; - - dev_info(&s->i2c->dev, "%s: Realtek RTL2832 SDR attached\n", - KBUILD_MODNAME); - return fe; - -err_unregister_v4l2_dev: - v4l2_device_unregister(&s->v4l2_dev); -err_free_controls: - v4l2_ctrl_handler_free(&s->hdl); -err_free_mem: - kfree(s); - return NULL; -} -EXPORT_SYMBOL(rtl2832_sdr_attach); - -MODULE_AUTHOR("Antti Palosaari "); -MODULE_DESCRIPTION("Realtek RTL2832 SDR driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h deleted file mode 100644 index b865fad..0000000 --- a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Realtek RTL2832U SDR driver - * - * Copyright (C) 2013 Antti Palosaari - * - * 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. - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * GNU Radio plugin "gr-kernel" for device usage will be on: - * http://git.linuxtv.org/anttip/gr-kernel.git - * - * TODO: - * Help is very highly welcome for these + all the others you could imagine: - * - move controls to V4L2 API - * - use libv4l2 for stream format conversions - * - gr-kernel: switch to v4l2_mmap (current read eats a lot of cpu) - * - SDRSharp support - */ - -#ifndef RTL2832_SDR_H -#define RTL2832_SDR_H - -#include -#include - -/* for config struct */ -#include "rtl2832.h" - -#if IS_ENABLED(CONFIG_DVB_RTL2832_SDR) -extern struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct rtl2832_config *cfg, - struct v4l2_subdev *sd); -#else -static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct rtl2832_config *cfg, - struct v4l2_subdev *sd) -{ - dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif - -#endif /* RTL2832_SDR_H */ -- cgit v0.10.2 From 2970c0d572688188176d6dbe4eb873f34520a4dd Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 14 Jul 2014 21:11:12 -0300 Subject: [media] rtl2832_sdr: put complex U16 format behind module parameter Move emulated format behind module parameter as those are not supported. Format conversions will be on library eventually. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index f81b1c8..389f923 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -35,6 +35,10 @@ #include #include +static bool rtl2832_sdr_emulated_fmt; +module_param_named(emulated_formats, rtl2832_sdr_emulated_fmt, bool, 0644); +MODULE_PARM_DESC(emulated_formats, "enable emulated formats (disappears in future)"); + #define MAX_BULK_BUFS (10) #define BULK_BUFFER_SIZE (128 * 512) @@ -84,10 +88,10 @@ struct rtl2832_sdr_format { static struct rtl2832_sdr_format formats[] = { { - .name = "IQ U8", + .name = "Complex U8", .pixelformat = V4L2_SDR_FMT_CU8, }, { - .name = "IQ U16LE (emulated)", + .name = "Complex U16LE (emulated)", .pixelformat = V4L2_SDR_FMT_CU16LE, }, }; @@ -139,6 +143,7 @@ struct rtl2832_sdr_state { unsigned int f_adc, f_tuner; u32 pixelformat; + unsigned int num_formats; /* Controls */ struct v4l2_ctrl_handler hdl; @@ -1211,7 +1216,7 @@ static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv, dev_dbg(&s->udev->dev, "%s:\n", __func__); - if (f->index >= NUM_FORMATS) + if (f->index >= s->num_formats) return -EINVAL; strlcpy(f->description, formats[f->index].name, sizeof(f->description)); @@ -1247,7 +1252,7 @@ static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv, return -EBUSY; memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < NUM_FORMATS; i++) { + for (i = 0; i < s->num_formats; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { s->pixelformat = f->fmt.sdr.pixelformat; return 0; @@ -1270,7 +1275,7 @@ static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv, (char *)&f->fmt.sdr.pixelformat); memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < NUM_FORMATS; i++) { + for (i = 0; i < s->num_formats; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) return 0; } @@ -1414,6 +1419,9 @@ struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, s->f_adc = bands_adc[0].rangelow; s->f_tuner = bands_fm[0].rangelow; s->pixelformat = V4L2_SDR_FMT_CU8; + s->num_formats = NUM_FORMATS; + if (rtl2832_sdr_emulated_fmt == false) + s->num_formats -= 1; mutex_init(&s->v4l2_lock); mutex_init(&s->vb_queue_lock); -- cgit v0.10.2 From 624ce384d5b6a5c55e99fd5a9eea304e5bb13627 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 14 Jul 2014 21:16:50 -0300 Subject: [media] rtl2832_sdr: print notice to point SDR API is not 100% stable yet SDR API is very new and surprises may occur. Due to that print notice to remind possible users. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 389f923..8665ea3 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -1519,6 +1519,9 @@ struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, dev_info(&s->i2c->dev, "%s: Realtek RTL2832 SDR attached\n", KBUILD_MODNAME); + dev_notice(&s->udev->dev, + "%s: SDR API is still slightly experimental and functionality changes may follow\n", + KBUILD_MODNAME); return fe; err_unregister_v4l2_dev: -- cgit v0.10.2 From b4bb1c285f14e8ddafe4ed5fdec477e3a02de708 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 14 Jul 2014 21:19:29 -0300 Subject: [media] MAINTAINERS: update RTL2832_SDR location It is moved out of staging to media. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 7c6a7ad..030038a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7585,7 +7585,7 @@ W: http://palosaari.fi/linux/ Q: http://patchwork.linuxtv.org/project/linux-media/list/ T: git git://linuxtv.org/anttip/media_tree.git S: Maintained -F: drivers/staging/media/rtl2832u_sdr/rtl2832_sdr* +F: drivers/media/dvb-frontends/rtl2832_sdr* RTL8180 WIRELESS DRIVER M: "John W. Linville" -- cgit v0.10.2 From eb83eab7fec5a5137e95717188e57310a0ebc051 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 17 Jul 2014 21:15:11 -0300 Subject: [media] airspy: remove v4l2-compliance workaround v4l2-compliance is now happy with frequency ranges where both lower and upper limit is same. Cc: Hans Verkuil Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/airspy/airspy.c b/drivers/staging/media/airspy/airspy.c index 91857e7..255fe6a 100644 --- a/drivers/staging/media/airspy/airspy.c +++ b/drivers/staging/media/airspy/airspy.c @@ -62,7 +62,7 @@ static const struct v4l2_frequency_band bands[] = { .index = 0, .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, .rangelow = 20000000, - .rangehigh = 20000001, /* FIXME: make v4l2-compliance happy */ + .rangehigh = 20000000, }, }; -- cgit v0.10.2 From f3e775962ccbc62bd93f2200b82db88af05d0137 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 17 Jul 2014 21:42:24 -0300 Subject: [media] airspy: move out of staging into drivers/media/usb Move it out of staging into media like all the other SDR drivers too. There is no good reasons to keep these SDR drivers in staging. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index f8c1099..38b6fe1 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -54,6 +54,7 @@ endif if MEDIA_SDR_SUPPORT comment "Software defined radio USB devices" +source "drivers/media/usb/airspy/Kconfig" endif endif #MEDIA_USB_SUPPORT diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 7ac4b14..05d69c2 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -9,6 +9,7 @@ obj-y += zr364xx/ stkwebcam/ s2255/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_USB_PWC) += pwc/ +obj-$(CONFIG_USB_AIRSPY) += airspy/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ diff --git a/drivers/media/usb/airspy/Kconfig b/drivers/media/usb/airspy/Kconfig new file mode 100644 index 0000000..10b204c --- /dev/null +++ b/drivers/media/usb/airspy/Kconfig @@ -0,0 +1,10 @@ +config USB_AIRSPY + tristate "AirSpy" + depends on VIDEO_V4L2 + select VIDEOBUF2_VMALLOC + ---help--- + This is a video4linux2 driver for AirSpy SDR device. + + To compile this driver as a module, choose M here: the + module will be called airspy + diff --git a/drivers/media/usb/airspy/Makefile b/drivers/media/usb/airspy/Makefile new file mode 100644 index 0000000..8d8e61c --- /dev/null +++ b/drivers/media/usb/airspy/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_AIRSPY) += airspy.o diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c new file mode 100644 index 0000000..255fe6a --- /dev/null +++ b/drivers/media/usb/airspy/airspy.c @@ -0,0 +1,1118 @@ +/* + * AirSpy SDR driver + * + * Copyright (C) 2014 Antti Palosaari + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* AirSpy USB API commands (from AirSpy Library) */ +enum { + CMD_INVALID = 0x00, + CMD_RECEIVER_MODE = 0x01, + CMD_SI5351C_WRITE = 0x02, + CMD_SI5351C_READ = 0x03, + CMD_R820T_WRITE = 0x04, + CMD_R820T_READ = 0x05, + CMD_SPIFLASH_ERASE = 0x06, + CMD_SPIFLASH_WRITE = 0x07, + CMD_SPIFLASH_READ = 0x08, + CMD_BOARD_ID_READ = 0x09, + CMD_VERSION_STRING_READ = 0x0a, + CMD_BOARD_PARTID_SERIALNO_READ = 0x0b, + CMD_SET_SAMPLE_RATE = 0x0c, + CMD_SET_FREQ = 0x0d, + CMD_SET_LNA_GAIN = 0x0e, + CMD_SET_MIXER_GAIN = 0x0f, + CMD_SET_VGA_GAIN = 0x10, + CMD_SET_LNA_AGC = 0x11, + CMD_SET_MIXER_AGC = 0x12, + CMD_SET_PACKING = 0x13, +}; + +/* + * bEndpointAddress 0x81 EP 1 IN + * Transfer Type Bulk + * wMaxPacketSize 0x0200 1x 512 bytes + */ +#define MAX_BULK_BUFS (6) +#define BULK_BUFFER_SIZE (128 * 512) + +static const struct v4l2_frequency_band bands[] = { + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 20000000, + .rangehigh = 20000000, + }, +}; + +static const struct v4l2_frequency_band bands_rf[] = { + { + .tuner = 1, + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 24000000, + .rangehigh = 1750000000, + }, +}; + +/* stream formats */ +struct airspy_format { + char *name; + u32 pixelformat; +}; + +/* format descriptions for capture and preview */ +static struct airspy_format formats[] = { + { + .name = "Real U12LE", + .pixelformat = V4L2_SDR_FMT_RU12LE, + }, +}; + +static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); + +/* intermediate buffers with raw data from the USB device */ +struct airspy_frame_buf { + struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ + struct list_head list; +}; + +struct airspy { +#define POWER_ON (1 << 1) +#define URB_BUF (1 << 2) +#define USB_STATE_URB_BUF (1 << 3) + unsigned long flags; + + struct usb_device *udev; + struct video_device vdev; + struct v4l2_device v4l2_dev; + + /* videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + unsigned sequence; /* Buffer sequence counter */ + unsigned int vb_full; /* vb is full and packets dropped */ + + /* Note if taking both locks v4l2_lock must always be locked first! */ + struct mutex v4l2_lock; /* Protects everything else */ + struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ + + struct urb *urb_list[MAX_BULK_BUFS]; + int buf_num; + unsigned long buf_size; + u8 *buf_list[MAX_BULK_BUFS]; + dma_addr_t dma_addr[MAX_BULK_BUFS]; + int urbs_initialized; + int urbs_submitted; + + /* USB control message buffer */ + #define BUF_SIZE 24 + u8 buf[BUF_SIZE]; + + /* Current configuration */ + unsigned int f_adc; + unsigned int f_rf; + u32 pixelformat; + + /* Controls */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *lna_gain_auto; + struct v4l2_ctrl *lna_gain; + struct v4l2_ctrl *mixer_gain_auto; + struct v4l2_ctrl *mixer_gain; + struct v4l2_ctrl *if_gain; + + /* Sample rate calc */ + unsigned long jiffies_next; + unsigned int sample; + unsigned int sample_measured; +}; + +#define airspy_dbg_usb_control_msg(_udev, _r, _t, _v, _i, _b, _l) { \ + char *_direction; \ + if (_t & USB_DIR_IN) \ + _direction = "<<<"; \ + else \ + _direction = ">>>"; \ + dev_dbg(&_udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ + "%s %*ph\n", __func__, _t, _r, _v & 0xff, _v >> 8, \ + _i & 0xff, _i >> 8, _l & 0xff, _l >> 8, _direction, \ + _l, _b); \ +} + +/* execute firmware command */ +static int airspy_ctrl_msg(struct airspy *s, u8 request, u16 value, u16 index, + u8 *data, u16 size) +{ + int ret; + unsigned int pipe; + u8 requesttype; + + switch (request) { + case CMD_RECEIVER_MODE: + case CMD_SET_FREQ: + pipe = usb_sndctrlpipe(s->udev, 0); + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + break; + case CMD_BOARD_ID_READ: + case CMD_VERSION_STRING_READ: + case CMD_BOARD_PARTID_SERIALNO_READ: + case CMD_SET_LNA_GAIN: + case CMD_SET_MIXER_GAIN: + case CMD_SET_VGA_GAIN: + case CMD_SET_LNA_AGC: + case CMD_SET_MIXER_AGC: + pipe = usb_rcvctrlpipe(s->udev, 0); + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + break; + default: + dev_err(&s->udev->dev, "Unknown command %02x\n", request); + ret = -EINVAL; + goto err; + } + + /* write request */ + if (!(requesttype & USB_DIR_IN)) + memcpy(s->buf, data, size); + + ret = usb_control_msg(s->udev, pipe, request, requesttype, value, + index, s->buf, size, 1000); + airspy_dbg_usb_control_msg(s->udev, request, requesttype, value, + index, s->buf, size); + if (ret < 0) { + dev_err(&s->udev->dev, + "usb_control_msg() failed %d request %02x\n", + ret, request); + goto err; + } + + /* read request */ + if (requesttype & USB_DIR_IN) + memcpy(data, s->buf, size); + + return 0; +err: + return ret; +} + +/* Private functions */ +static struct airspy_frame_buf *airspy_get_next_fill_buf(struct airspy *s) +{ + unsigned long flags = 0; + struct airspy_frame_buf *buf = NULL; + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + if (list_empty(&s->queued_bufs)) + goto leave; + + buf = list_entry(s->queued_bufs.next, + struct airspy_frame_buf, list); + list_del(&buf->list); +leave: + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); + return buf; +} + +static unsigned int airspy_convert_stream(struct airspy *s, + void *dst, void *src, unsigned int src_len) +{ + unsigned int dst_len; + + if (s->pixelformat == V4L2_SDR_FMT_RU12LE) { + memcpy(dst, src, src_len); + dst_len = src_len; + } else { + dst_len = 0; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if (unlikely(time_is_before_jiffies(s->jiffies_next))) { + #define MSECS 10000UL + unsigned int samples = s->sample - s->sample_measured; + s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); + s->sample_measured = s->sample; + dev_dbg(&s->udev->dev, + "slen=%d samples=%u msecs=%lu sample rate=%lu\n", + src_len, samples, MSECS, + samples * 1000UL / MSECS); + } + + /* total number of samples */ + s->sample += src_len / 2; + + return dst_len; +} + +/* + * This gets called for the bulk stream pipe. This is done in interrupt + * time, so it has to be fast, not crash, and not stall. Neat. + */ +static void airspy_urb_complete(struct urb *urb) +{ + struct airspy *s = urb->context; + struct airspy_frame_buf *fbuf; + + dev_dbg_ratelimited(&s->udev->dev, + "%s: status=%d length=%d/%d errors=%d\n", + __func__, urb->status, urb->actual_length, + urb->transfer_buffer_length, urb->error_count); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dev_err_ratelimited(&s->udev->dev, "URB failed %d\n", + urb->status); + break; + } + + if (likely(urb->actual_length > 0)) { + void *ptr; + unsigned int len; + /* get free framebuffer */ + fbuf = airspy_get_next_fill_buf(s); + if (unlikely(fbuf == NULL)) { + s->vb_full++; + dev_notice_ratelimited(&s->udev->dev, + "videobuf is full, %d packets dropped\n", + s->vb_full); + goto skip; + } + + /* fill framebuffer */ + ptr = vb2_plane_vaddr(&fbuf->vb, 0); + len = airspy_convert_stream(s, ptr, urb->transfer_buffer, + urb->actual_length); + vb2_set_plane_payload(&fbuf->vb, 0, len); + v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp); + fbuf->vb.v4l2_buf.sequence = s->sequence++; + vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + } +skip: + usb_submit_urb(urb, GFP_ATOMIC); +} + +static int airspy_kill_urbs(struct airspy *s) +{ + int i; + + for (i = s->urbs_submitted - 1; i >= 0; i--) { + dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i); + /* stop the URB */ + usb_kill_urb(s->urb_list[i]); + } + s->urbs_submitted = 0; + + return 0; +} + +static int airspy_submit_urbs(struct airspy *s) +{ + int i, ret; + + for (i = 0; i < s->urbs_initialized; i++) { + dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i); + ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC); + if (ret) { + dev_err(&s->udev->dev, + "Could not submit URB no. %d - get them all back\n", + i); + airspy_kill_urbs(s); + return ret; + } + s->urbs_submitted++; + } + + return 0; +} + +static int airspy_free_stream_bufs(struct airspy *s) +{ + if (s->flags & USB_STATE_URB_BUF) { + while (s->buf_num) { + s->buf_num--; + dev_dbg(&s->udev->dev, "%s: free buf=%d\n", + __func__, s->buf_num); + usb_free_coherent(s->udev, s->buf_size, + s->buf_list[s->buf_num], + s->dma_addr[s->buf_num]); + } + } + s->flags &= ~USB_STATE_URB_BUF; + + return 0; +} + +static int airspy_alloc_stream_bufs(struct airspy *s) +{ + s->buf_num = 0; + s->buf_size = BULK_BUFFER_SIZE; + + dev_dbg(&s->udev->dev, + "%s: all in all I will use %u bytes for streaming\n", + __func__, MAX_BULK_BUFS * BULK_BUFFER_SIZE); + + for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) { + s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev, + BULK_BUFFER_SIZE, GFP_ATOMIC, + &s->dma_addr[s->buf_num]); + if (!s->buf_list[s->buf_num]) { + dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n", + __func__, s->buf_num); + airspy_free_stream_bufs(s); + return -ENOMEM; + } + + dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n", + __func__, s->buf_num, + s->buf_list[s->buf_num], + (long long)s->dma_addr[s->buf_num]); + s->flags |= USB_STATE_URB_BUF; + } + + return 0; +} + +static int airspy_free_urbs(struct airspy *s) +{ + int i; + + airspy_kill_urbs(s); + + for (i = s->urbs_initialized - 1; i >= 0; i--) { + if (s->urb_list[i]) { + dev_dbg(&s->udev->dev, "%s: free urb=%d\n", + __func__, i); + /* free the URBs */ + usb_free_urb(s->urb_list[i]); + } + } + s->urbs_initialized = 0; + + return 0; +} + +static int airspy_alloc_urbs(struct airspy *s) +{ + int i, j; + + /* allocate the URBs */ + for (i = 0; i < MAX_BULK_BUFS; i++) { + dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i); + s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + if (!s->urb_list[i]) { + dev_dbg(&s->udev->dev, "%s: failed\n", __func__); + for (j = 0; j < i; j++) + usb_free_urb(s->urb_list[j]); + return -ENOMEM; + } + usb_fill_bulk_urb(s->urb_list[i], + s->udev, + usb_rcvbulkpipe(s->udev, 0x81), + s->buf_list[i], + BULK_BUFFER_SIZE, + airspy_urb_complete, s); + + s->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + s->urb_list[i]->transfer_dma = s->dma_addr[i]; + s->urbs_initialized++; + } + + return 0; +} + +/* Must be called with vb_queue_lock hold */ +static void airspy_cleanup_queued_bufs(struct airspy *s) +{ + unsigned long flags = 0; + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + while (!list_empty(&s->queued_bufs)) { + struct airspy_frame_buf *buf; + buf = list_entry(s->queued_bufs.next, + struct airspy_frame_buf, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); +} + +/* The user yanked out the cable... */ +static void airspy_disconnect(struct usb_interface *intf) +{ + struct v4l2_device *v = usb_get_intfdata(intf); + struct airspy *s = container_of(v, struct airspy, v4l2_dev); + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + mutex_lock(&s->vb_queue_lock); + mutex_lock(&s->v4l2_lock); + /* No need to keep the urbs around after disconnection */ + s->udev = NULL; + v4l2_device_disconnect(&s->v4l2_dev); + video_unregister_device(&s->vdev); + mutex_unlock(&s->v4l2_lock); + mutex_unlock(&s->vb_queue_lock); + + v4l2_device_put(&s->v4l2_dev); +} + +/* Videobuf2 operations */ +static int airspy_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) +{ + struct airspy *s = vb2_get_drv_priv(vq); + + dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); + + /* Need at least 8 buffers */ + if (vq->num_buffers + *nbuffers < 8) + *nbuffers = 8 - vq->num_buffers; + *nplanes = 1; + sizes[0] = PAGE_ALIGN(BULK_BUFFER_SIZE); + + dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", + __func__, *nbuffers, sizes[0]); + return 0; +} + +static void airspy_buf_queue(struct vb2_buffer *vb) +{ + struct airspy *s = vb2_get_drv_priv(vb->vb2_queue); + struct airspy_frame_buf *buf = + container_of(vb, struct airspy_frame_buf, vb); + unsigned long flags = 0; + + /* Check the device has not disconnected between prep and queuing */ + if (unlikely(!s->udev)) { + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + return; + } + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + list_add_tail(&buf->list, &s->queued_bufs); + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); +} + +static int airspy_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct airspy *s = vb2_get_drv_priv(vq); + int ret; + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + if (!s->udev) + return -ENODEV; + + mutex_lock(&s->v4l2_lock); + + set_bit(POWER_ON, &s->flags); + + s->sequence = 0; + + ret = airspy_alloc_stream_bufs(s); + if (ret) + goto err; + + ret = airspy_alloc_urbs(s); + if (ret) + goto err; + + ret = airspy_submit_urbs(s); + if (ret) + goto err; + + /* start hardware streaming */ + ret = airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 1, 0, NULL, 0); + if (ret) + goto err; +err: + mutex_unlock(&s->v4l2_lock); + + return ret; +} + +static void airspy_stop_streaming(struct vb2_queue *vq) +{ + struct airspy *s = vb2_get_drv_priv(vq); + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + mutex_lock(&s->v4l2_lock); + + /* stop hardware streaming */ + airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 0, 0, NULL, 0); + + airspy_kill_urbs(s); + airspy_free_urbs(s); + airspy_free_stream_bufs(s); + + airspy_cleanup_queued_bufs(s); + + clear_bit(POWER_ON, &s->flags); + + mutex_unlock(&s->v4l2_lock); +} + +static struct vb2_ops airspy_vb2_ops = { + .queue_setup = airspy_queue_setup, + .buf_queue = airspy_buf_queue, + .start_streaming = airspy_start_streaming, + .stop_streaming = airspy_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int airspy_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct airspy *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); + usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int airspy_enum_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct airspy *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index); + + if (f->index >= NUM_FORMATS) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, sizeof(f->description)); + f->pixelformat = formats[f->index].pixelformat; + + return 0; +} + +static int airspy_g_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct airspy *s = video_drvdata(file); + + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + (char *)&s->pixelformat); + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + f->fmt.sdr.pixelformat = s->pixelformat; + + return 0; +} + +static int airspy_s_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct airspy *s = video_drvdata(file); + struct vb2_queue *q = &s->vb_queue; + int i; + + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + (char *)&f->fmt.sdr.pixelformat); + + if (vb2_is_busy(q)) + return -EBUSY; + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + s->pixelformat = f->fmt.sdr.pixelformat; + return 0; + } + } + + f->fmt.sdr.pixelformat = formats[0].pixelformat; + s->pixelformat = formats[0].pixelformat; + + return 0; +} + +static int airspy_try_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct airspy *s = video_drvdata(file); + int i; + + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + (char *)&f->fmt.sdr.pixelformat); + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) + return 0; + } + + f->fmt.sdr.pixelformat = formats[0].pixelformat; + + return 0; +} + +static int airspy_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *v) +{ + struct airspy *s = video_drvdata(file); + int ret; + + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); + + if (v->index == 0) + ret = 0; + else if (v->index == 1) + ret = 0; + else + ret = -EINVAL; + + return ret; +} + +static int airspy_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) +{ + struct airspy *s = video_drvdata(file); + int ret; + + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); + + if (v->index == 0) { + strlcpy(v->name, "AirSpy ADC", sizeof(v->name)); + v->type = V4L2_TUNER_ADC; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = bands[0].rangelow; + v->rangehigh = bands[0].rangehigh; + ret = 0; + } else if (v->index == 1) { + strlcpy(v->name, "AirSpy RF", sizeof(v->name)); + v->type = V4L2_TUNER_RF; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = bands_rf[0].rangelow; + v->rangehigh = bands_rf[0].rangehigh; + ret = 0; + } else { + ret = -EINVAL; + } + + return ret; +} + +static int airspy_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct airspy *s = video_drvdata(file); + int ret = 0; + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", + __func__, f->tuner, f->type); + + if (f->tuner == 0) { + f->type = V4L2_TUNER_ADC; + f->frequency = s->f_adc; + ret = 0; + } else if (f->tuner == 1) { + f->type = V4L2_TUNER_RF; + f->frequency = s->f_rf; + } else { + ret = -EINVAL; + } + + return ret; +} + +static int airspy_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct airspy *s = video_drvdata(file); + int ret; + u8 buf[4]; + + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", + __func__, f->tuner, f->type, f->frequency); + + if (f->tuner == 0) { + s->f_adc = clamp_t(unsigned int, f->frequency, + bands[0].rangelow, + bands[0].rangehigh); + dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n", + __func__, s->f_adc); + ret = 0; + } else if (f->tuner == 1) { + s->f_rf = clamp_t(unsigned int, f->frequency, + bands_rf[0].rangelow, + bands_rf[0].rangehigh); + dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n", + __func__, s->f_rf); + buf[0] = (s->f_rf >> 0) & 0xff; + buf[1] = (s->f_rf >> 8) & 0xff; + buf[2] = (s->f_rf >> 16) & 0xff; + buf[3] = (s->f_rf >> 24) & 0xff; + ret = airspy_ctrl_msg(s, CMD_SET_FREQ, 0, 0, buf, 4); + } else { + ret = -EINVAL; + } + + return ret; +} + +static int airspy_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + struct airspy *s = video_drvdata(file); + int ret; + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", + __func__, band->tuner, band->type, band->index); + + if (band->tuner == 0) { + if (band->index >= ARRAY_SIZE(bands)) { + ret = -EINVAL; + } else { + *band = bands[band->index]; + ret = 0; + } + } else if (band->tuner == 1) { + if (band->index >= ARRAY_SIZE(bands_rf)) { + ret = -EINVAL; + } else { + *band = bands_rf[band->index]; + ret = 0; + } + } else { + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ioctl_ops airspy_ioctl_ops = { + .vidioc_querycap = airspy_querycap, + + .vidioc_enum_fmt_sdr_cap = airspy_enum_fmt_sdr_cap, + .vidioc_g_fmt_sdr_cap = airspy_g_fmt_sdr_cap, + .vidioc_s_fmt_sdr_cap = airspy_s_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = airspy_try_fmt_sdr_cap, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_g_tuner = airspy_g_tuner, + .vidioc_s_tuner = airspy_s_tuner, + + .vidioc_g_frequency = airspy_g_frequency, + .vidioc_s_frequency = airspy_s_frequency, + .vidioc_enum_freq_bands = airspy_enum_freq_bands, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_log_status = v4l2_ctrl_log_status, +}; + +static const struct v4l2_file_operations airspy_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static struct video_device airspy_template = { + .name = "AirSpy SDR", + .release = video_device_release_empty, + .fops = &airspy_fops, + .ioctl_ops = &airspy_ioctl_ops, +}; + +static void airspy_video_release(struct v4l2_device *v) +{ + struct airspy *s = container_of(v, struct airspy, v4l2_dev); + + v4l2_ctrl_handler_free(&s->hdl); + v4l2_device_unregister(&s->v4l2_dev); + kfree(s); +} + +static int airspy_set_lna_gain(struct airspy *s) +{ + int ret; + u8 u8tmp; + + dev_dbg(&s->udev->dev, "%s: lna auto=%d->%d val=%d->%d\n", + __func__, s->lna_gain_auto->cur.val, + s->lna_gain_auto->val, s->lna_gain->cur.val, + s->lna_gain->val); + + ret = airspy_ctrl_msg(s, CMD_SET_LNA_AGC, 0, s->lna_gain_auto->val, + &u8tmp, 1); + if (ret) + goto err; + + if (s->lna_gain_auto->val == false) { + ret = airspy_ctrl_msg(s, CMD_SET_LNA_GAIN, 0, s->lna_gain->val, + &u8tmp, 1); + if (ret) + goto err; + } +err: + if (ret) + dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int airspy_set_mixer_gain(struct airspy *s) +{ + int ret; + u8 u8tmp; + + dev_dbg(&s->udev->dev, "%s: mixer auto=%d->%d val=%d->%d\n", + __func__, s->mixer_gain_auto->cur.val, + s->mixer_gain_auto->val, s->mixer_gain->cur.val, + s->mixer_gain->val); + + ret = airspy_ctrl_msg(s, CMD_SET_MIXER_AGC, 0, s->mixer_gain_auto->val, + &u8tmp, 1); + if (ret) + goto err; + + if (s->mixer_gain_auto->val == false) { + ret = airspy_ctrl_msg(s, CMD_SET_MIXER_GAIN, 0, + s->mixer_gain->val, &u8tmp, 1); + if (ret) + goto err; + } +err: + if (ret) + dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int airspy_set_if_gain(struct airspy *s) +{ + int ret; + u8 u8tmp; + + dev_dbg(&s->udev->dev, "%s: val=%d->%d\n", + __func__, s->if_gain->cur.val, s->if_gain->val); + + ret = airspy_ctrl_msg(s, CMD_SET_VGA_GAIN, 0, s->if_gain->val, + &u8tmp, 1); + if (ret) + goto err; +err: + if (ret) + dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int airspy_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct airspy *s = container_of(ctrl->handler, struct airspy, hdl); + int ret; + + switch (ctrl->id) { + case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: + case V4L2_CID_RF_TUNER_LNA_GAIN: + ret = airspy_set_lna_gain(s); + break; + case V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO: + case V4L2_CID_RF_TUNER_MIXER_GAIN: + ret = airspy_set_mixer_gain(s); + break; + case V4L2_CID_RF_TUNER_IF_GAIN: + ret = airspy_set_if_gain(s); + break; + default: + dev_dbg(&s->udev->dev, "%s: unknown ctrl: id=%d name=%s\n", + __func__, ctrl->id, ctrl->name); + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ctrl_ops airspy_ctrl_ops = { + .s_ctrl = airspy_s_ctrl, +}; + +static int airspy_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct airspy *s = NULL; + int ret; + u8 u8tmp, buf[BUF_SIZE]; + + s = kzalloc(sizeof(struct airspy), GFP_KERNEL); + if (s == NULL) { + dev_err(&udev->dev, + "Could not allocate memory for airspy state\n"); + return -ENOMEM; + } + + mutex_init(&s->v4l2_lock); + mutex_init(&s->vb_queue_lock); + spin_lock_init(&s->queued_bufs_lock); + INIT_LIST_HEAD(&s->queued_bufs); + s->udev = udev; + s->f_adc = bands[0].rangelow; + s->f_rf = bands_rf[0].rangelow; + s->pixelformat = V4L2_SDR_FMT_RU12LE; + + /* Detect device */ + ret = airspy_ctrl_msg(s, CMD_BOARD_ID_READ, 0, 0, &u8tmp, 1); + if (ret == 0) + ret = airspy_ctrl_msg(s, CMD_VERSION_STRING_READ, 0, 0, + buf, BUF_SIZE); + if (ret) { + dev_err(&s->udev->dev, "Could not detect board\n"); + goto err_free_mem; + } + + buf[BUF_SIZE - 1] = '\0'; + + dev_info(&s->udev->dev, "Board ID: %02x\n", u8tmp); + dev_info(&s->udev->dev, "Firmware version: %s\n", buf); + + /* Init videobuf2 queue structure */ + s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; + s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + s->vb_queue.drv_priv = s; + s->vb_queue.buf_struct_size = sizeof(struct airspy_frame_buf); + s->vb_queue.ops = &airspy_vb2_ops; + s->vb_queue.mem_ops = &vb2_vmalloc_memops; + s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&s->vb_queue); + if (ret) { + dev_err(&s->udev->dev, "Could not initialize vb2 queue\n"); + goto err_free_mem; + } + + /* Init video_device structure */ + s->vdev = airspy_template; + s->vdev.queue = &s->vb_queue; + s->vdev.queue->lock = &s->vb_queue_lock; + video_set_drvdata(&s->vdev, s); + + /* Register the v4l2_device structure */ + s->v4l2_dev.release = airspy_video_release; + ret = v4l2_device_register(&intf->dev, &s->v4l2_dev); + if (ret) { + dev_err(&s->udev->dev, + "Failed to register v4l2-device (%d)\n", ret); + goto err_free_mem; + } + + /* Register controls */ + v4l2_ctrl_handler_init(&s->hdl, 5); + s->lna_gain_auto = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, + V4L2_CID_RF_TUNER_LNA_GAIN_AUTO, 0, 1, 1, 0); + s->lna_gain = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, + V4L2_CID_RF_TUNER_LNA_GAIN, 0, 14, 1, 8); + v4l2_ctrl_auto_cluster(2, &s->lna_gain_auto, 0, false); + s->mixer_gain_auto = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, + V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO, 0, 1, 1, 0); + s->mixer_gain = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, + V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 15, 1, 8); + v4l2_ctrl_auto_cluster(2, &s->mixer_gain_auto, 0, false); + s->if_gain = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, + V4L2_CID_RF_TUNER_IF_GAIN, 0, 15, 1, 0); + if (s->hdl.error) { + ret = s->hdl.error; + dev_err(&s->udev->dev, "Could not initialize controls\n"); + goto err_free_controls; + } + + v4l2_ctrl_handler_setup(&s->hdl); + + s->v4l2_dev.ctrl_handler = &s->hdl; + s->vdev.v4l2_dev = &s->v4l2_dev; + s->vdev.lock = &s->v4l2_lock; + + ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); + if (ret) { + dev_err(&s->udev->dev, + "Failed to register as video device (%d)\n", + ret); + goto err_unregister_v4l2_dev; + } + dev_info(&s->udev->dev, "Registered as %s\n", + video_device_node_name(&s->vdev)); + + return 0; + +err_free_controls: + v4l2_ctrl_handler_free(&s->hdl); +err_unregister_v4l2_dev: + v4l2_device_unregister(&s->v4l2_dev); +err_free_mem: + kfree(s); + return ret; +} + +/* USB device ID list */ +static struct usb_device_id airspy_id_table[] = { + { USB_DEVICE(0x1d50, 0x60a1) }, /* AirSpy */ + { } +}; +MODULE_DEVICE_TABLE(usb, airspy_id_table); + +/* USB subsystem interface */ +static struct usb_driver airspy_driver = { + .name = KBUILD_MODNAME, + .probe = airspy_probe, + .disconnect = airspy_disconnect, + .id_table = airspy_id_table, +}; + +module_usb_driver(airspy_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("AirSpy SDR"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 8abda14..a326d5d 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -19,7 +19,6 @@ menuconfig STAGING_MEDIA if STAGING_MEDIA # Please keep them in alphabetic order -source "drivers/staging/media/airspy/Kconfig" source "drivers/staging/media/as102/Kconfig" source "drivers/staging/media/bcm2048/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 74a9d5a..e1e053a 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,4 +1,3 @@ -obj-$(CONFIG_USB_AIRSPY) += airspy/ obj-$(CONFIG_DVB_AS102) += as102/ obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ diff --git a/drivers/staging/media/airspy/Kconfig b/drivers/staging/media/airspy/Kconfig deleted file mode 100644 index 59a829b..0000000 --- a/drivers/staging/media/airspy/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -config USB_AIRSPY - tristate "AirSpy" - depends on USB && VIDEO_DEV && VIDEO_V4L2 && I2C - select VIDEOBUF2_VMALLOC - default m if !MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/staging/media/airspy/Makefile b/drivers/staging/media/airspy/Makefile deleted file mode 100644 index 8d8e61c..0000000 --- a/drivers/staging/media/airspy/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_USB_AIRSPY) += airspy.o diff --git a/drivers/staging/media/airspy/airspy.c b/drivers/staging/media/airspy/airspy.c deleted file mode 100644 index 255fe6a..0000000 --- a/drivers/staging/media/airspy/airspy.c +++ /dev/null @@ -1,1118 +0,0 @@ -/* - * AirSpy SDR driver - * - * Copyright (C) 2014 Antti Palosaari - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -/* AirSpy USB API commands (from AirSpy Library) */ -enum { - CMD_INVALID = 0x00, - CMD_RECEIVER_MODE = 0x01, - CMD_SI5351C_WRITE = 0x02, - CMD_SI5351C_READ = 0x03, - CMD_R820T_WRITE = 0x04, - CMD_R820T_READ = 0x05, - CMD_SPIFLASH_ERASE = 0x06, - CMD_SPIFLASH_WRITE = 0x07, - CMD_SPIFLASH_READ = 0x08, - CMD_BOARD_ID_READ = 0x09, - CMD_VERSION_STRING_READ = 0x0a, - CMD_BOARD_PARTID_SERIALNO_READ = 0x0b, - CMD_SET_SAMPLE_RATE = 0x0c, - CMD_SET_FREQ = 0x0d, - CMD_SET_LNA_GAIN = 0x0e, - CMD_SET_MIXER_GAIN = 0x0f, - CMD_SET_VGA_GAIN = 0x10, - CMD_SET_LNA_AGC = 0x11, - CMD_SET_MIXER_AGC = 0x12, - CMD_SET_PACKING = 0x13, -}; - -/* - * bEndpointAddress 0x81 EP 1 IN - * Transfer Type Bulk - * wMaxPacketSize 0x0200 1x 512 bytes - */ -#define MAX_BULK_BUFS (6) -#define BULK_BUFFER_SIZE (128 * 512) - -static const struct v4l2_frequency_band bands[] = { - { - .tuner = 0, - .type = V4L2_TUNER_ADC, - .index = 0, - .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 20000000, - .rangehigh = 20000000, - }, -}; - -static const struct v4l2_frequency_band bands_rf[] = { - { - .tuner = 1, - .type = V4L2_TUNER_RF, - .index = 0, - .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 24000000, - .rangehigh = 1750000000, - }, -}; - -/* stream formats */ -struct airspy_format { - char *name; - u32 pixelformat; -}; - -/* format descriptions for capture and preview */ -static struct airspy_format formats[] = { - { - .name = "Real U12LE", - .pixelformat = V4L2_SDR_FMT_RU12LE, - }, -}; - -static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); - -/* intermediate buffers with raw data from the USB device */ -struct airspy_frame_buf { - struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ - struct list_head list; -}; - -struct airspy { -#define POWER_ON (1 << 1) -#define URB_BUF (1 << 2) -#define USB_STATE_URB_BUF (1 << 3) - unsigned long flags; - - struct usb_device *udev; - struct video_device vdev; - struct v4l2_device v4l2_dev; - - /* videobuf2 queue and queued buffers list */ - struct vb2_queue vb_queue; - struct list_head queued_bufs; - spinlock_t queued_bufs_lock; /* Protects queued_bufs */ - unsigned sequence; /* Buffer sequence counter */ - unsigned int vb_full; /* vb is full and packets dropped */ - - /* Note if taking both locks v4l2_lock must always be locked first! */ - struct mutex v4l2_lock; /* Protects everything else */ - struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ - - struct urb *urb_list[MAX_BULK_BUFS]; - int buf_num; - unsigned long buf_size; - u8 *buf_list[MAX_BULK_BUFS]; - dma_addr_t dma_addr[MAX_BULK_BUFS]; - int urbs_initialized; - int urbs_submitted; - - /* USB control message buffer */ - #define BUF_SIZE 24 - u8 buf[BUF_SIZE]; - - /* Current configuration */ - unsigned int f_adc; - unsigned int f_rf; - u32 pixelformat; - - /* Controls */ - struct v4l2_ctrl_handler hdl; - struct v4l2_ctrl *lna_gain_auto; - struct v4l2_ctrl *lna_gain; - struct v4l2_ctrl *mixer_gain_auto; - struct v4l2_ctrl *mixer_gain; - struct v4l2_ctrl *if_gain; - - /* Sample rate calc */ - unsigned long jiffies_next; - unsigned int sample; - unsigned int sample_measured; -}; - -#define airspy_dbg_usb_control_msg(_udev, _r, _t, _v, _i, _b, _l) { \ - char *_direction; \ - if (_t & USB_DIR_IN) \ - _direction = "<<<"; \ - else \ - _direction = ">>>"; \ - dev_dbg(&_udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ - "%s %*ph\n", __func__, _t, _r, _v & 0xff, _v >> 8, \ - _i & 0xff, _i >> 8, _l & 0xff, _l >> 8, _direction, \ - _l, _b); \ -} - -/* execute firmware command */ -static int airspy_ctrl_msg(struct airspy *s, u8 request, u16 value, u16 index, - u8 *data, u16 size) -{ - int ret; - unsigned int pipe; - u8 requesttype; - - switch (request) { - case CMD_RECEIVER_MODE: - case CMD_SET_FREQ: - pipe = usb_sndctrlpipe(s->udev, 0); - requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); - break; - case CMD_BOARD_ID_READ: - case CMD_VERSION_STRING_READ: - case CMD_BOARD_PARTID_SERIALNO_READ: - case CMD_SET_LNA_GAIN: - case CMD_SET_MIXER_GAIN: - case CMD_SET_VGA_GAIN: - case CMD_SET_LNA_AGC: - case CMD_SET_MIXER_AGC: - pipe = usb_rcvctrlpipe(s->udev, 0); - requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); - break; - default: - dev_err(&s->udev->dev, "Unknown command %02x\n", request); - ret = -EINVAL; - goto err; - } - - /* write request */ - if (!(requesttype & USB_DIR_IN)) - memcpy(s->buf, data, size); - - ret = usb_control_msg(s->udev, pipe, request, requesttype, value, - index, s->buf, size, 1000); - airspy_dbg_usb_control_msg(s->udev, request, requesttype, value, - index, s->buf, size); - if (ret < 0) { - dev_err(&s->udev->dev, - "usb_control_msg() failed %d request %02x\n", - ret, request); - goto err; - } - - /* read request */ - if (requesttype & USB_DIR_IN) - memcpy(data, s->buf, size); - - return 0; -err: - return ret; -} - -/* Private functions */ -static struct airspy_frame_buf *airspy_get_next_fill_buf(struct airspy *s) -{ - unsigned long flags = 0; - struct airspy_frame_buf *buf = NULL; - - spin_lock_irqsave(&s->queued_bufs_lock, flags); - if (list_empty(&s->queued_bufs)) - goto leave; - - buf = list_entry(s->queued_bufs.next, - struct airspy_frame_buf, list); - list_del(&buf->list); -leave: - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); - return buf; -} - -static unsigned int airspy_convert_stream(struct airspy *s, - void *dst, void *src, unsigned int src_len) -{ - unsigned int dst_len; - - if (s->pixelformat == V4L2_SDR_FMT_RU12LE) { - memcpy(dst, src, src_len); - dst_len = src_len; - } else { - dst_len = 0; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if (unlikely(time_is_before_jiffies(s->jiffies_next))) { - #define MSECS 10000UL - unsigned int samples = s->sample - s->sample_measured; - s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); - s->sample_measured = s->sample; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sample rate=%lu\n", - src_len, samples, MSECS, - samples * 1000UL / MSECS); - } - - /* total number of samples */ - s->sample += src_len / 2; - - return dst_len; -} - -/* - * This gets called for the bulk stream pipe. This is done in interrupt - * time, so it has to be fast, not crash, and not stall. Neat. - */ -static void airspy_urb_complete(struct urb *urb) -{ - struct airspy *s = urb->context; - struct airspy_frame_buf *fbuf; - - dev_dbg_ratelimited(&s->udev->dev, - "%s: status=%d length=%d/%d errors=%d\n", - __func__, urb->status, urb->actual_length, - urb->transfer_buffer_length, urb->error_count); - - switch (urb->status) { - case 0: /* success */ - case -ETIMEDOUT: /* NAK */ - break; - case -ECONNRESET: /* kill */ - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* error */ - dev_err_ratelimited(&s->udev->dev, "URB failed %d\n", - urb->status); - break; - } - - if (likely(urb->actual_length > 0)) { - void *ptr; - unsigned int len; - /* get free framebuffer */ - fbuf = airspy_get_next_fill_buf(s); - if (unlikely(fbuf == NULL)) { - s->vb_full++; - dev_notice_ratelimited(&s->udev->dev, - "videobuf is full, %d packets dropped\n", - s->vb_full); - goto skip; - } - - /* fill framebuffer */ - ptr = vb2_plane_vaddr(&fbuf->vb, 0); - len = airspy_convert_stream(s, ptr, urb->transfer_buffer, - urb->actual_length); - vb2_set_plane_payload(&fbuf->vb, 0, len); - v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp); - fbuf->vb.v4l2_buf.sequence = s->sequence++; - vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); - } -skip: - usb_submit_urb(urb, GFP_ATOMIC); -} - -static int airspy_kill_urbs(struct airspy *s) -{ - int i; - - for (i = s->urbs_submitted - 1; i >= 0; i--) { - dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i); - /* stop the URB */ - usb_kill_urb(s->urb_list[i]); - } - s->urbs_submitted = 0; - - return 0; -} - -static int airspy_submit_urbs(struct airspy *s) -{ - int i, ret; - - for (i = 0; i < s->urbs_initialized; i++) { - dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i); - ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC); - if (ret) { - dev_err(&s->udev->dev, - "Could not submit URB no. %d - get them all back\n", - i); - airspy_kill_urbs(s); - return ret; - } - s->urbs_submitted++; - } - - return 0; -} - -static int airspy_free_stream_bufs(struct airspy *s) -{ - if (s->flags & USB_STATE_URB_BUF) { - while (s->buf_num) { - s->buf_num--; - dev_dbg(&s->udev->dev, "%s: free buf=%d\n", - __func__, s->buf_num); - usb_free_coherent(s->udev, s->buf_size, - s->buf_list[s->buf_num], - s->dma_addr[s->buf_num]); - } - } - s->flags &= ~USB_STATE_URB_BUF; - - return 0; -} - -static int airspy_alloc_stream_bufs(struct airspy *s) -{ - s->buf_num = 0; - s->buf_size = BULK_BUFFER_SIZE; - - dev_dbg(&s->udev->dev, - "%s: all in all I will use %u bytes for streaming\n", - __func__, MAX_BULK_BUFS * BULK_BUFFER_SIZE); - - for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) { - s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev, - BULK_BUFFER_SIZE, GFP_ATOMIC, - &s->dma_addr[s->buf_num]); - if (!s->buf_list[s->buf_num]) { - dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n", - __func__, s->buf_num); - airspy_free_stream_bufs(s); - return -ENOMEM; - } - - dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n", - __func__, s->buf_num, - s->buf_list[s->buf_num], - (long long)s->dma_addr[s->buf_num]); - s->flags |= USB_STATE_URB_BUF; - } - - return 0; -} - -static int airspy_free_urbs(struct airspy *s) -{ - int i; - - airspy_kill_urbs(s); - - for (i = s->urbs_initialized - 1; i >= 0; i--) { - if (s->urb_list[i]) { - dev_dbg(&s->udev->dev, "%s: free urb=%d\n", - __func__, i); - /* free the URBs */ - usb_free_urb(s->urb_list[i]); - } - } - s->urbs_initialized = 0; - - return 0; -} - -static int airspy_alloc_urbs(struct airspy *s) -{ - int i, j; - - /* allocate the URBs */ - for (i = 0; i < MAX_BULK_BUFS; i++) { - dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i); - s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); - if (!s->urb_list[i]) { - dev_dbg(&s->udev->dev, "%s: failed\n", __func__); - for (j = 0; j < i; j++) - usb_free_urb(s->urb_list[j]); - return -ENOMEM; - } - usb_fill_bulk_urb(s->urb_list[i], - s->udev, - usb_rcvbulkpipe(s->udev, 0x81), - s->buf_list[i], - BULK_BUFFER_SIZE, - airspy_urb_complete, s); - - s->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - s->urb_list[i]->transfer_dma = s->dma_addr[i]; - s->urbs_initialized++; - } - - return 0; -} - -/* Must be called with vb_queue_lock hold */ -static void airspy_cleanup_queued_bufs(struct airspy *s) -{ - unsigned long flags = 0; - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - spin_lock_irqsave(&s->queued_bufs_lock, flags); - while (!list_empty(&s->queued_bufs)) { - struct airspy_frame_buf *buf; - buf = list_entry(s->queued_bufs.next, - struct airspy_frame_buf, list); - list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - } - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); -} - -/* The user yanked out the cable... */ -static void airspy_disconnect(struct usb_interface *intf) -{ - struct v4l2_device *v = usb_get_intfdata(intf); - struct airspy *s = container_of(v, struct airspy, v4l2_dev); - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - mutex_lock(&s->vb_queue_lock); - mutex_lock(&s->v4l2_lock); - /* No need to keep the urbs around after disconnection */ - s->udev = NULL; - v4l2_device_disconnect(&s->v4l2_dev); - video_unregister_device(&s->vdev); - mutex_unlock(&s->v4l2_lock); - mutex_unlock(&s->vb_queue_lock); - - v4l2_device_put(&s->v4l2_dev); -} - -/* Videobuf2 operations */ -static int airspy_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) -{ - struct airspy *s = vb2_get_drv_priv(vq); - - dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); - - /* Need at least 8 buffers */ - if (vq->num_buffers + *nbuffers < 8) - *nbuffers = 8 - vq->num_buffers; - *nplanes = 1; - sizes[0] = PAGE_ALIGN(BULK_BUFFER_SIZE); - - dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", - __func__, *nbuffers, sizes[0]); - return 0; -} - -static void airspy_buf_queue(struct vb2_buffer *vb) -{ - struct airspy *s = vb2_get_drv_priv(vb->vb2_queue); - struct airspy_frame_buf *buf = - container_of(vb, struct airspy_frame_buf, vb); - unsigned long flags = 0; - - /* Check the device has not disconnected between prep and queuing */ - if (unlikely(!s->udev)) { - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - return; - } - - spin_lock_irqsave(&s->queued_bufs_lock, flags); - list_add_tail(&buf->list, &s->queued_bufs); - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); -} - -static int airspy_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct airspy *s = vb2_get_drv_priv(vq); - int ret; - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - if (!s->udev) - return -ENODEV; - - mutex_lock(&s->v4l2_lock); - - set_bit(POWER_ON, &s->flags); - - s->sequence = 0; - - ret = airspy_alloc_stream_bufs(s); - if (ret) - goto err; - - ret = airspy_alloc_urbs(s); - if (ret) - goto err; - - ret = airspy_submit_urbs(s); - if (ret) - goto err; - - /* start hardware streaming */ - ret = airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 1, 0, NULL, 0); - if (ret) - goto err; -err: - mutex_unlock(&s->v4l2_lock); - - return ret; -} - -static void airspy_stop_streaming(struct vb2_queue *vq) -{ - struct airspy *s = vb2_get_drv_priv(vq); - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - mutex_lock(&s->v4l2_lock); - - /* stop hardware streaming */ - airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 0, 0, NULL, 0); - - airspy_kill_urbs(s); - airspy_free_urbs(s); - airspy_free_stream_bufs(s); - - airspy_cleanup_queued_bufs(s); - - clear_bit(POWER_ON, &s->flags); - - mutex_unlock(&s->v4l2_lock); -} - -static struct vb2_ops airspy_vb2_ops = { - .queue_setup = airspy_queue_setup, - .buf_queue = airspy_buf_queue, - .start_streaming = airspy_start_streaming, - .stop_streaming = airspy_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int airspy_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct airspy *s = video_drvdata(file); - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); - strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); - usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE | V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int airspy_enum_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct airspy *s = video_drvdata(file); - - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index); - - if (f->index >= NUM_FORMATS) - return -EINVAL; - - strlcpy(f->description, formats[f->index].name, sizeof(f->description)); - f->pixelformat = formats[f->index].pixelformat; - - return 0; -} - -static int airspy_g_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct airspy *s = video_drvdata(file); - - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, - (char *)&s->pixelformat); - - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - f->fmt.sdr.pixelformat = s->pixelformat; - - return 0; -} - -static int airspy_s_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct airspy *s = video_drvdata(file); - struct vb2_queue *q = &s->vb_queue; - int i; - - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, - (char *)&f->fmt.sdr.pixelformat); - - if (vb2_is_busy(q)) - return -EBUSY; - - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { - s->pixelformat = f->fmt.sdr.pixelformat; - return 0; - } - } - - f->fmt.sdr.pixelformat = formats[0].pixelformat; - s->pixelformat = formats[0].pixelformat; - - return 0; -} - -static int airspy_try_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct airspy *s = video_drvdata(file); - int i; - - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, - (char *)&f->fmt.sdr.pixelformat); - - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].pixelformat == f->fmt.sdr.pixelformat) - return 0; - } - - f->fmt.sdr.pixelformat = formats[0].pixelformat; - - return 0; -} - -static int airspy_s_tuner(struct file *file, void *priv, - const struct v4l2_tuner *v) -{ - struct airspy *s = video_drvdata(file); - int ret; - - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); - - if (v->index == 0) - ret = 0; - else if (v->index == 1) - ret = 0; - else - ret = -EINVAL; - - return ret; -} - -static int airspy_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) -{ - struct airspy *s = video_drvdata(file); - int ret; - - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); - - if (v->index == 0) { - strlcpy(v->name, "AirSpy ADC", sizeof(v->name)); - v->type = V4L2_TUNER_ADC; - v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; - v->rangelow = bands[0].rangelow; - v->rangehigh = bands[0].rangehigh; - ret = 0; - } else if (v->index == 1) { - strlcpy(v->name, "AirSpy RF", sizeof(v->name)); - v->type = V4L2_TUNER_RF; - v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; - v->rangelow = bands_rf[0].rangelow; - v->rangehigh = bands_rf[0].rangehigh; - ret = 0; - } else { - ret = -EINVAL; - } - - return ret; -} - -static int airspy_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct airspy *s = video_drvdata(file); - int ret = 0; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", - __func__, f->tuner, f->type); - - if (f->tuner == 0) { - f->type = V4L2_TUNER_ADC; - f->frequency = s->f_adc; - ret = 0; - } else if (f->tuner == 1) { - f->type = V4L2_TUNER_RF; - f->frequency = s->f_rf; - } else { - ret = -EINVAL; - } - - return ret; -} - -static int airspy_s_frequency(struct file *file, void *priv, - const struct v4l2_frequency *f) -{ - struct airspy *s = video_drvdata(file); - int ret; - u8 buf[4]; - - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", - __func__, f->tuner, f->type, f->frequency); - - if (f->tuner == 0) { - s->f_adc = clamp_t(unsigned int, f->frequency, - bands[0].rangelow, - bands[0].rangehigh); - dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n", - __func__, s->f_adc); - ret = 0; - } else if (f->tuner == 1) { - s->f_rf = clamp_t(unsigned int, f->frequency, - bands_rf[0].rangelow, - bands_rf[0].rangehigh); - dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n", - __func__, s->f_rf); - buf[0] = (s->f_rf >> 0) & 0xff; - buf[1] = (s->f_rf >> 8) & 0xff; - buf[2] = (s->f_rf >> 16) & 0xff; - buf[3] = (s->f_rf >> 24) & 0xff; - ret = airspy_ctrl_msg(s, CMD_SET_FREQ, 0, 0, buf, 4); - } else { - ret = -EINVAL; - } - - return ret; -} - -static int airspy_enum_freq_bands(struct file *file, void *priv, - struct v4l2_frequency_band *band) -{ - struct airspy *s = video_drvdata(file); - int ret; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", - __func__, band->tuner, band->type, band->index); - - if (band->tuner == 0) { - if (band->index >= ARRAY_SIZE(bands)) { - ret = -EINVAL; - } else { - *band = bands[band->index]; - ret = 0; - } - } else if (band->tuner == 1) { - if (band->index >= ARRAY_SIZE(bands_rf)) { - ret = -EINVAL; - } else { - *band = bands_rf[band->index]; - ret = 0; - } - } else { - ret = -EINVAL; - } - - return ret; -} - -static const struct v4l2_ioctl_ops airspy_ioctl_ops = { - .vidioc_querycap = airspy_querycap, - - .vidioc_enum_fmt_sdr_cap = airspy_enum_fmt_sdr_cap, - .vidioc_g_fmt_sdr_cap = airspy_g_fmt_sdr_cap, - .vidioc_s_fmt_sdr_cap = airspy_s_fmt_sdr_cap, - .vidioc_try_fmt_sdr_cap = airspy_try_fmt_sdr_cap, - - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_g_tuner = airspy_g_tuner, - .vidioc_s_tuner = airspy_s_tuner, - - .vidioc_g_frequency = airspy_g_frequency, - .vidioc_s_frequency = airspy_s_frequency, - .vidioc_enum_freq_bands = airspy_enum_freq_bands, - - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - .vidioc_log_status = v4l2_ctrl_log_status, -}; - -static const struct v4l2_file_operations airspy_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static struct video_device airspy_template = { - .name = "AirSpy SDR", - .release = video_device_release_empty, - .fops = &airspy_fops, - .ioctl_ops = &airspy_ioctl_ops, -}; - -static void airspy_video_release(struct v4l2_device *v) -{ - struct airspy *s = container_of(v, struct airspy, v4l2_dev); - - v4l2_ctrl_handler_free(&s->hdl); - v4l2_device_unregister(&s->v4l2_dev); - kfree(s); -} - -static int airspy_set_lna_gain(struct airspy *s) -{ - int ret; - u8 u8tmp; - - dev_dbg(&s->udev->dev, "%s: lna auto=%d->%d val=%d->%d\n", - __func__, s->lna_gain_auto->cur.val, - s->lna_gain_auto->val, s->lna_gain->cur.val, - s->lna_gain->val); - - ret = airspy_ctrl_msg(s, CMD_SET_LNA_AGC, 0, s->lna_gain_auto->val, - &u8tmp, 1); - if (ret) - goto err; - - if (s->lna_gain_auto->val == false) { - ret = airspy_ctrl_msg(s, CMD_SET_LNA_GAIN, 0, s->lna_gain->val, - &u8tmp, 1); - if (ret) - goto err; - } -err: - if (ret) - dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); - - return ret; -} - -static int airspy_set_mixer_gain(struct airspy *s) -{ - int ret; - u8 u8tmp; - - dev_dbg(&s->udev->dev, "%s: mixer auto=%d->%d val=%d->%d\n", - __func__, s->mixer_gain_auto->cur.val, - s->mixer_gain_auto->val, s->mixer_gain->cur.val, - s->mixer_gain->val); - - ret = airspy_ctrl_msg(s, CMD_SET_MIXER_AGC, 0, s->mixer_gain_auto->val, - &u8tmp, 1); - if (ret) - goto err; - - if (s->mixer_gain_auto->val == false) { - ret = airspy_ctrl_msg(s, CMD_SET_MIXER_GAIN, 0, - s->mixer_gain->val, &u8tmp, 1); - if (ret) - goto err; - } -err: - if (ret) - dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); - - return ret; -} - -static int airspy_set_if_gain(struct airspy *s) -{ - int ret; - u8 u8tmp; - - dev_dbg(&s->udev->dev, "%s: val=%d->%d\n", - __func__, s->if_gain->cur.val, s->if_gain->val); - - ret = airspy_ctrl_msg(s, CMD_SET_VGA_GAIN, 0, s->if_gain->val, - &u8tmp, 1); - if (ret) - goto err; -err: - if (ret) - dev_dbg(&s->udev->dev, "%s: failed=%d\n", __func__, ret); - - return ret; -} - -static int airspy_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct airspy *s = container_of(ctrl->handler, struct airspy, hdl); - int ret; - - switch (ctrl->id) { - case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: - case V4L2_CID_RF_TUNER_LNA_GAIN: - ret = airspy_set_lna_gain(s); - break; - case V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO: - case V4L2_CID_RF_TUNER_MIXER_GAIN: - ret = airspy_set_mixer_gain(s); - break; - case V4L2_CID_RF_TUNER_IF_GAIN: - ret = airspy_set_if_gain(s); - break; - default: - dev_dbg(&s->udev->dev, "%s: unknown ctrl: id=%d name=%s\n", - __func__, ctrl->id, ctrl->name); - ret = -EINVAL; - } - - return ret; -} - -static const struct v4l2_ctrl_ops airspy_ctrl_ops = { - .s_ctrl = airspy_s_ctrl, -}; - -static int airspy_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct airspy *s = NULL; - int ret; - u8 u8tmp, buf[BUF_SIZE]; - - s = kzalloc(sizeof(struct airspy), GFP_KERNEL); - if (s == NULL) { - dev_err(&udev->dev, - "Could not allocate memory for airspy state\n"); - return -ENOMEM; - } - - mutex_init(&s->v4l2_lock); - mutex_init(&s->vb_queue_lock); - spin_lock_init(&s->queued_bufs_lock); - INIT_LIST_HEAD(&s->queued_bufs); - s->udev = udev; - s->f_adc = bands[0].rangelow; - s->f_rf = bands_rf[0].rangelow; - s->pixelformat = V4L2_SDR_FMT_RU12LE; - - /* Detect device */ - ret = airspy_ctrl_msg(s, CMD_BOARD_ID_READ, 0, 0, &u8tmp, 1); - if (ret == 0) - ret = airspy_ctrl_msg(s, CMD_VERSION_STRING_READ, 0, 0, - buf, BUF_SIZE); - if (ret) { - dev_err(&s->udev->dev, "Could not detect board\n"); - goto err_free_mem; - } - - buf[BUF_SIZE - 1] = '\0'; - - dev_info(&s->udev->dev, "Board ID: %02x\n", u8tmp); - dev_info(&s->udev->dev, "Firmware version: %s\n", buf); - - /* Init videobuf2 queue structure */ - s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; - s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - s->vb_queue.drv_priv = s; - s->vb_queue.buf_struct_size = sizeof(struct airspy_frame_buf); - s->vb_queue.ops = &airspy_vb2_ops; - s->vb_queue.mem_ops = &vb2_vmalloc_memops; - s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - ret = vb2_queue_init(&s->vb_queue); - if (ret) { - dev_err(&s->udev->dev, "Could not initialize vb2 queue\n"); - goto err_free_mem; - } - - /* Init video_device structure */ - s->vdev = airspy_template; - s->vdev.queue = &s->vb_queue; - s->vdev.queue->lock = &s->vb_queue_lock; - video_set_drvdata(&s->vdev, s); - - /* Register the v4l2_device structure */ - s->v4l2_dev.release = airspy_video_release; - ret = v4l2_device_register(&intf->dev, &s->v4l2_dev); - if (ret) { - dev_err(&s->udev->dev, - "Failed to register v4l2-device (%d)\n", ret); - goto err_free_mem; - } - - /* Register controls */ - v4l2_ctrl_handler_init(&s->hdl, 5); - s->lna_gain_auto = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, - V4L2_CID_RF_TUNER_LNA_GAIN_AUTO, 0, 1, 1, 0); - s->lna_gain = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, - V4L2_CID_RF_TUNER_LNA_GAIN, 0, 14, 1, 8); - v4l2_ctrl_auto_cluster(2, &s->lna_gain_auto, 0, false); - s->mixer_gain_auto = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, - V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO, 0, 1, 1, 0); - s->mixer_gain = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, - V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 15, 1, 8); - v4l2_ctrl_auto_cluster(2, &s->mixer_gain_auto, 0, false); - s->if_gain = v4l2_ctrl_new_std(&s->hdl, &airspy_ctrl_ops, - V4L2_CID_RF_TUNER_IF_GAIN, 0, 15, 1, 0); - if (s->hdl.error) { - ret = s->hdl.error; - dev_err(&s->udev->dev, "Could not initialize controls\n"); - goto err_free_controls; - } - - v4l2_ctrl_handler_setup(&s->hdl); - - s->v4l2_dev.ctrl_handler = &s->hdl; - s->vdev.v4l2_dev = &s->v4l2_dev; - s->vdev.lock = &s->v4l2_lock; - - ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); - if (ret) { - dev_err(&s->udev->dev, - "Failed to register as video device (%d)\n", - ret); - goto err_unregister_v4l2_dev; - } - dev_info(&s->udev->dev, "Registered as %s\n", - video_device_node_name(&s->vdev)); - - return 0; - -err_free_controls: - v4l2_ctrl_handler_free(&s->hdl); -err_unregister_v4l2_dev: - v4l2_device_unregister(&s->v4l2_dev); -err_free_mem: - kfree(s); - return ret; -} - -/* USB device ID list */ -static struct usb_device_id airspy_id_table[] = { - { USB_DEVICE(0x1d50, 0x60a1) }, /* AirSpy */ - { } -}; -MODULE_DEVICE_TABLE(usb, airspy_id_table); - -/* USB subsystem interface */ -static struct usb_driver airspy_driver = { - .name = KBUILD_MODNAME, - .probe = airspy_probe, - .disconnect = airspy_disconnect, - .id_table = airspy_id_table, -}; - -module_usb_driver(airspy_driver); - -MODULE_AUTHOR("Antti Palosaari "); -MODULE_DESCRIPTION("AirSpy SDR"); -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From ac4f90ca1b3f6e22fa3f1140e2a73988902e4840 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 17 Jul 2014 21:47:48 -0300 Subject: [media] airspy: print notice to point SDR API is not 100% stable yet Print notice on driver load: "SDR API is still slightly experimental and functionality changes may follow". It is just remind possible used SDR API is very new and surprises may occur. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 255fe6a..72d9f2c 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -1084,7 +1084,9 @@ static int airspy_probe(struct usb_interface *intf, } dev_info(&s->udev->dev, "Registered as %s\n", video_device_node_name(&s->vdev)); - + dev_notice(&s->udev->dev, + "%s: SDR API is still slightly experimental and functionality changes may follow\n", + KBUILD_MODNAME); return 0; err_free_controls: -- cgit v0.10.2 From 469d4ec8776091a2846ba89803cf954cf8b3ce65 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 17 Jul 2014 21:57:14 -0300 Subject: [media] MAINTAINERS: add airspy driver Video4Linux2 driver for AirSpy SDR device. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 030038a..df6aa2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -515,6 +515,16 @@ S: Supported F: fs/aio.c F: include/linux/*aio*.h +AIRSPY MEDIA DRIVER +M: Antti Palosaari +L: linux-media@vger.kernel.org +W: http://linuxtv.org/ +W: http://palosaari.fi/linux/ +Q: http://patchwork.linuxtv.org/project/linux-media/list/ +T: git git://linuxtv.org/anttip/media_tree.git +S: Maintained +F: drivers/media/usb/airspy/ + ALCATEL SPEEDTOUCH USB DRIVER M: Duncan Sands L: linux-usb@vger.kernel.org -- cgit v0.10.2 From 32bf88182d8babfa205e2bd5cbaa992cd1c75511 Mon Sep 17 00:00:00 2001 From: Luis Alves Date: Thu, 17 Jul 2014 14:31:28 -0300 Subject: [media] si2168: Set symbol rate for DVB-C This patch adds symbol rate setting to the driver. Signed-off-by: Luis Alves Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 1b56b8e..5075189 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -278,6 +278,18 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; + /* set DVB-C symbol rate */ + if (c->delivery_system == SYS_DVBC_ANNEX_A) { + memcpy(cmd.args, "\x14\x00\x02\x11", 4); + cmd.args[4] = (c->symbol_rate / 1000) & 0xff; + cmd.args[5] = ((c->symbol_rate / 1000) >> 8) & 0xff; + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + } + memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6); cmd.wlen = 6; cmd.rlen = 4; -- cgit v0.10.2 From 43911776faf04599f48732403959d510bde03815 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Thu, 17 Jul 2014 15:43:27 -0300 Subject: [media] si2168: improve scanning performance Improve scanning performance by setting property 0301 with a value from Windows driver. Signed-off-by: Olli Salonen Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 5075189..303a441 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -325,6 +325,13 @@ static int si2168_set_frontend(struct dvb_frontend *fe) if (ret) goto err; + memcpy(cmd.args, "\x14\x00\x01\x03\x0c\x00", 6); + cmd.wlen = 6; + cmd.rlen = 4; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + memcpy(cmd.args, "\x85", 1); cmd.wlen = 1; cmd.rlen = 1; -- cgit v0.10.2 From 4d6efc7a96c126f2b21ffd4edad9b11144a0336a Mon Sep 17 00:00:00 2001 From: Luis Alves Date: Thu, 17 Jul 2014 16:38:08 -0300 Subject: [media] si2168: Fix i2c_add_mux_adapter return value In case of failure the return value was always 0. Return proper error code (ENODEV) instead. Signed-off-by: Luis Alves Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 303a441..3e3fd2a 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -640,8 +640,10 @@ static int si2168_probe(struct i2c_client *client, /* create mux i2c adapter for tuner */ s->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, s, 0, 0, 0, si2168_select, si2168_deselect); - if (s->adapter == NULL) + if (s->adapter == NULL) { + ret = -ENODEV; goto err; + } /* create dvb_frontend */ memcpy(&s->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); -- cgit v0.10.2 From 2da2e18513b4f2de00c5f6ff4925bb011d6a8127 Mon Sep 17 00:00:00 2001 From: Luis Alves Date: Thu, 17 Jul 2014 17:56:44 -0300 Subject: [media] si2168: Remove testing for demod presence on probe Testing demod presence on probe fails if the demod was sleep mode. Signed-off-by: Luis Alves Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 3e3fd2a..84db613 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -616,7 +616,6 @@ static int si2168_probe(struct i2c_client *client, struct si2168_config *config = client->dev.platform_data; struct si2168 *s; int ret; - struct si2168_cmd cmd; dev_dbg(&client->dev, "%s:\n", __func__); @@ -630,13 +629,6 @@ static int si2168_probe(struct i2c_client *client, s->client = client; mutex_init(&s->i2c_mutex); - /* check if the demod is there */ - cmd.wlen = 0; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - /* create mux i2c adapter for tuner */ s->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, s, 0, 0, 0, si2168_select, si2168_deselect); -- cgit v0.10.2 From 635a90cf9385721dcb9c7f5c59a2873ef0279c8b Mon Sep 17 00:00:00 2001 From: Luis Alves Date: Thu, 17 Jul 2014 19:43:37 -0300 Subject: [media] si2168: Support Si2168-A20 firmware downloading This adds support for the Si2168-A20 firmware download. Extracting the firmware: wget http://www.tbsdtv.com/download/document/tbs6281/tbs6281-t2-t-driver_v1.0.0.6.zip unzip tbs6281-t2-t-driver_v1.0.0.6.zip dd if=tbs-6281_x64/tbs6281_64.sys of=dvb-demod-si2168-a20-01.fw count=28656 bs=1 skip=1625088 md5sum: 32e06713b33915f674bfb2c209beaea5 /lib/firmware/dvb-demod-si2168-a20-01.fw Signed-off-by: Luis Alves Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 84db613..97c4679 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -398,10 +398,14 @@ static int si2168_init(struct dvb_frontend *fe) chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | cmd.args[4] << 0; + #define SI2168_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0) #define SI2168_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0) #define SI2168_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0) switch (chip_id) { + case SI2168_A20: + fw_file = SI2168_A20_FIRMWARE; + break; case SI2168_A30: fw_file = SI2168_A30_FIRMWARE; break; @@ -693,5 +697,6 @@ module_i2c_driver(si2168_driver); MODULE_AUTHOR("Antti Palosaari "); MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(SI2168_A20_FIRMWARE); MODULE_FIRMWARE(SI2168_A30_FIRMWARE); MODULE_FIRMWARE(SI2168_B40_FIRMWARE); diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index d606915..c1f7a4c 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -22,6 +22,7 @@ #include #include +#define SI2168_A20_FIRMWARE "dvb-demod-si2168-a20-01.fw" #define SI2168_A30_FIRMWARE "dvb-demod-si2168-a30-01.fw" #define SI2168_B40_FIRMWARE "dvb-demod-si2168-b40-01.fw" #define SI2168_B40_FIRMWARE_FALLBACK "dvb-demod-si2168-01.fw" -- cgit v0.10.2 From 6cc8a35dcf6bceb4c6db38ae7862d826b3afb6a2 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Fri, 18 Jul 2014 02:41:12 -0300 Subject: [media] si2157: Use name si2157_ops instead of si2157_tuner_ops The struct prototype is defined at the beginning of the code as "si2157_ops" but the real struct is called "si2157_tuner_ops". This is causing the name to be empty on this info msg: si2157 16-0060: si2157: found a '' in cold state [crope@iki.fi: commit msg from Luis email reply] Signed-off-by: Olli Salonen Cc: Luis Alves Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 329004f..4730f69 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -277,7 +277,7 @@ err: return ret; } -static const struct dvb_tuner_ops si2157_tuner_ops = { +static const struct dvb_tuner_ops si2157_ops = { .info = { .name = "Silicon Labs Si2157/Si2158", .frequency_min = 110000000, @@ -317,7 +317,7 @@ static int si2157_probe(struct i2c_client *client, goto err; fe->tuner_priv = s; - memcpy(&fe->ops.tuner_ops, &si2157_tuner_ops, + memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops)); i2c_set_clientdata(client, s); -- cgit v0.10.2 From 9f7ca3d46b3d6dbde4edf9fa7ca95596e2e35085 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 15 Jul 2014 16:34:34 -0300 Subject: [media] cxusb: Prepare for si2157 driver getting more parameters Modify all users of si2157_config to correctly initialize all not listed values to 0. Signed-off-by: Matthias Schwarzott Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index d60e36d..b7461ac 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -1386,6 +1386,7 @@ static int cxusb_tt_ct2_4400_attach(struct dvb_usb_adapter *adap) st->i2c_client_demod = client_demod; /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = adap->fe_adap[0].fe; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); -- cgit v0.10.2 From c3b513bd695542dcf6d2da49bdd78ef111db24cc Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 15 Jul 2014 16:34:35 -0300 Subject: [media] em28xx-dvb: Prepare for si2157 driver getting more parameters Modify all users of si2157_config to correctly initialize all not listed values to 0. Signed-off-by: Matthias Schwarzott Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index a121ed9..96a0bdb 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1545,6 +1545,7 @@ static int em28xx_dvb_init(struct em28xx *dev) dvb->i2c_client_demod = client; /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = dvb->fe[0]; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); -- cgit v0.10.2 From 05024efe1264c3379135d7223f2c84c8f7ef2172 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 15 Jul 2014 16:34:36 -0300 Subject: [media] si2157: Add support for spectral inversion This is needed for PCTV 522e support. Signed-off-by: Matthias Schwarzott Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 4730f69..f619983 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -253,6 +253,8 @@ static int si2157_set_params(struct dvb_frontend *fe) memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6); cmd.args[4] = delivery_system | bandwidth; + if (s->inversion) + cmd.args[5] = 0x01; cmd.wlen = 6; cmd.rlen = 1; ret = si2157_cmd_execute(s, &cmd); @@ -307,6 +309,7 @@ static int si2157_probe(struct i2c_client *client, s->client = client; s->fe = cfg->fe; + s->inversion = cfg->inversion; mutex_init(&s->i2c_mutex); /* check if the tuner is there */ diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h index 4465c46..6da4d5d 100644 --- a/drivers/media/tuners/si2157.h +++ b/drivers/media/tuners/si2157.h @@ -29,6 +29,11 @@ struct si2157_config { * frontend */ struct dvb_frontend *fe; + + /* + * Spectral Inversion + */ + bool inversion; }; #endif diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index db79f3c..3ddab5e 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -26,6 +26,7 @@ struct si2157 { struct i2c_client *client; struct dvb_frontend *fe; bool active; + bool inversion; }; /* firmare command struct */ -- cgit v0.10.2 From 1140540da0e4b00978607244b58f06a528b819c7 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 15 Jul 2014 04:58:40 -0300 Subject: [media] si2157: Add get_if_frequency callback This is needed for PCTV 522e support. Signed-off-by: Matthias Schwarzott Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index f619983..6c53edb 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -279,6 +279,12 @@ err: return ret; } +static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 5000000; /* default value of property 0x0706 */ + return 0; +} + static const struct dvb_tuner_ops si2157_ops = { .info = { .name = "Silicon Labs Si2157/Si2158", @@ -289,6 +295,7 @@ static const struct dvb_tuner_ops si2157_ops = { .init = si2157_init, .sleep = si2157_sleep, .set_params = si2157_set_params, + .get_if_frequency = si2157_get_if_frequency, }; static int si2157_probe(struct i2c_client *client, -- cgit v0.10.2 From aaa968b636550042023bffa075adcdb01e6d257c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 Jul 2014 15:28:24 -0300 Subject: [media] v4l: videodev2: add buffer size to SDR format Add buffer size field to struct v4l2_sdr_format. It is used for negotiate streaming buffer size between application and driver. Signed-off-by: Antti Palosaari Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 6da302d..8fc4822 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1783,10 +1783,12 @@ struct v4l2_pix_format_mplane { /** * struct v4l2_sdr_format - SDR format definition * @pixelformat: little endian four character code (fourcc) + * @buffersize: maximum size in bytes required for data */ struct v4l2_sdr_format { __u32 pixelformat; - __u8 reserved[28]; + __u32 buffersize; + __u8 reserved[24]; } __attribute__ ((packed)); /** -- cgit v0.10.2 From d57fe404019b3c720e188156080c614290e1b78c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 Jul 2014 16:22:29 -0300 Subject: [media] rtl2832_sdr: fill FMT buffer size Fill FMT buffer size field in order to inform app which will be used streaming buffer size. Currently driver doesn't allow buffer size value proposed by application. Signed-off-by: Antti Palosaari Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 8665ea3..f58bd74 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -84,15 +84,18 @@ static const struct v4l2_frequency_band bands_fm[] = { struct rtl2832_sdr_format { char *name; u32 pixelformat; + u32 buffersize; }; static struct rtl2832_sdr_format formats[] = { { .name = "Complex U8", - .pixelformat = V4L2_SDR_FMT_CU8, + .pixelformat = V4L2_SDR_FMT_CU8, + .buffersize = BULK_BUFFER_SIZE, }, { .name = "Complex U16LE (emulated)", .pixelformat = V4L2_SDR_FMT_CU16LE, + .buffersize = BULK_BUFFER_SIZE * 2, }, }; @@ -143,6 +146,7 @@ struct rtl2832_sdr_state { unsigned int f_adc, f_tuner; u32 pixelformat; + u32 buffersize; unsigned int num_formats; /* Controls */ @@ -633,8 +637,7 @@ static int rtl2832_sdr_queue_setup(struct vb2_queue *vq, if (vq->num_buffers + *nbuffers < 8) *nbuffers = 8 - vq->num_buffers; *nplanes = 1; - /* 2 = max 16-bit sample returned */ - sizes[0] = PAGE_ALIGN(BULK_BUFFER_SIZE * 2); + sizes[0] = PAGE_ALIGN(s->buffersize); dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", __func__, *nbuffers, sizes[0]); return 0; @@ -1233,6 +1236,8 @@ static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv, dev_dbg(&s->udev->dev, "%s:\n", __func__); f->fmt.sdr.pixelformat = s->pixelformat; + f->fmt.sdr.buffersize = s->buffersize; + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); return 0; @@ -1254,13 +1259,17 @@ static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv, memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); for (i = 0; i < s->num_formats; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { - s->pixelformat = f->fmt.sdr.pixelformat; + s->pixelformat = formats[i].pixelformat; + s->buffersize = formats[i].buffersize; + f->fmt.sdr.buffersize = formats[i].buffersize; return 0; } } - f->fmt.sdr.pixelformat = formats[0].pixelformat; s->pixelformat = formats[0].pixelformat; + s->buffersize = formats[0].buffersize; + f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; return 0; } @@ -1276,11 +1285,14 @@ static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv, memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); for (i = 0; i < s->num_formats; i++) { - if (formats[i].pixelformat == f->fmt.sdr.pixelformat) + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + f->fmt.sdr.buffersize = formats[i].buffersize; return 0; + } } f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; return 0; } @@ -1418,7 +1430,8 @@ struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, s->cfg = cfg; s->f_adc = bands_adc[0].rangelow; s->f_tuner = bands_fm[0].rangelow; - s->pixelformat = V4L2_SDR_FMT_CU8; + s->pixelformat = formats[0].pixelformat; + s->buffersize = formats[0].buffersize; s->num_formats = NUM_FORMATS; if (rtl2832_sdr_emulated_fmt == false) s->num_formats -= 1; -- cgit v0.10.2 From fea9c63a10142e6cd2bf48dca2cd39d79a300f2c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 Jul 2014 20:21:16 -0300 Subject: [media] DocBook media: v4l2_sdr_format buffersize field New field buffersize was added to inform application maximum buffer size used. Add it to documentation too. Cc: Hans Verkuil Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/dev-sdr.xml b/Documentation/DocBook/media/v4l/dev-sdr.xml index dc14804..f890356 100644 --- a/Documentation/DocBook/media/v4l/dev-sdr.xml +++ b/Documentation/DocBook/media/v4l/dev-sdr.xml @@ -72,9 +72,12 @@ To use the format ioctls applications set the V4L2_BUF_TYPE_SDR_CAPTURE and use the &v4l2-sdr-format; sdr member of the fmt union as needed per the desired operation. -Currently only the pixelformat field of -&v4l2-sdr-format; is used. The content of that field is the V4L2 fourcc code -of the data format. +Currently there is two fields, pixelformat and +buffersize, of struct &v4l2-sdr-format; which are +used. Content of the pixelformat is V4L2 FourCC +code of the data format. The buffersize field is +maximum buffer size in bytes required for data transfer, set by the driver in +order to inform application. @@ -92,8 +95,15 @@ V4L2 defines SDR formats in . + __u32 + buffersize + +Maximum size in bytes required for data. Value is set by the driver. + + + __u8 - reserved[28] + reserved[24] This array is reserved for future extensions. Drivers and applications must set it to zero. -- cgit v0.10.2 From 1b303e1a58599e42f858805285f03ccb5a4e18d2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 Jul 2014 21:22:08 -0300 Subject: [media] airspy: fill FMT buffer size Fill FMT buffer size field in order to inform app which will be used streaming buffer size. Currently driver doesn't allow buffer size value proposed by application. Cc: Hans Verkuil Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 72d9f2c..cb0e515 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -81,13 +81,15 @@ static const struct v4l2_frequency_band bands_rf[] = { struct airspy_format { char *name; u32 pixelformat; + u32 buffersize; }; /* format descriptions for capture and preview */ static struct airspy_format formats[] = { { .name = "Real U12LE", - .pixelformat = V4L2_SDR_FMT_RU12LE, + .pixelformat = V4L2_SDR_FMT_RU12LE, + .buffersize = BULK_BUFFER_SIZE, }, }; @@ -136,6 +138,7 @@ struct airspy { unsigned int f_adc; unsigned int f_rf; u32 pixelformat; + u32 buffersize; /* Controls */ struct v4l2_ctrl_handler hdl; @@ -500,7 +503,7 @@ static int airspy_queue_setup(struct vb2_queue *vq, if (vq->num_buffers + *nbuffers < 8) *nbuffers = 8 - vq->num_buffers; *nplanes = 1; - sizes[0] = PAGE_ALIGN(BULK_BUFFER_SIZE); + sizes[0] = PAGE_ALIGN(s->buffersize); dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", __func__, *nbuffers, sizes[0]); @@ -635,8 +638,9 @@ static int airspy_g_fmt_sdr_cap(struct file *file, void *priv, dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, (char *)&s->pixelformat); - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); f->fmt.sdr.pixelformat = s->pixelformat; + f->fmt.sdr.buffersize = s->buffersize; + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); return 0; } @@ -657,13 +661,17 @@ static int airspy_s_fmt_sdr_cap(struct file *file, void *priv, memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); for (i = 0; i < NUM_FORMATS; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { - s->pixelformat = f->fmt.sdr.pixelformat; + s->pixelformat = formats[i].pixelformat; + s->buffersize = formats[i].buffersize; + f->fmt.sdr.buffersize = formats[i].buffersize; return 0; } } - f->fmt.sdr.pixelformat = formats[0].pixelformat; s->pixelformat = formats[0].pixelformat; + s->buffersize = formats[0].buffersize; + f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; return 0; } @@ -679,11 +687,14 @@ static int airspy_try_fmt_sdr_cap(struct file *file, void *priv, memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].pixelformat == f->fmt.sdr.pixelformat) + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + f->fmt.sdr.buffersize = formats[i].buffersize; return 0; + } } f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; return 0; } @@ -1003,7 +1014,8 @@ static int airspy_probe(struct usb_interface *intf, s->udev = udev; s->f_adc = bands[0].rangelow; s->f_rf = bands_rf[0].rangelow; - s->pixelformat = V4L2_SDR_FMT_RU12LE; + s->pixelformat = formats[0].pixelformat; + s->buffersize = formats[0].buffersize; /* Detect device */ ret = airspy_ctrl_msg(s, CMD_BOARD_ID_READ, 0, 0, &u8tmp, 1); -- cgit v0.10.2 From fd8b5f502929925468561279d5697a4d7d8b7671 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 13 Jul 2014 04:12:05 +0300 Subject: msi2500: move msi3101 out of staging and rename Move msi3101 out of staging and rename to msi2500. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 38b6fe1..fa67519 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -54,6 +54,7 @@ endif if MEDIA_SDR_SUPPORT comment "Software defined radio USB devices" +source "drivers/media/usb/msi2500/Kconfig" source "drivers/media/usb/airspy/Kconfig" endif diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 05d69c2..712a6b1 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -9,6 +9,7 @@ obj-y += zr364xx/ stkwebcam/ s2255/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_USB_PWC) += pwc/ +obj-$(CONFIG_USB_MSI2500) += msi2500/ obj-$(CONFIG_USB_AIRSPY) += airspy/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_AU0828) += au0828/ diff --git a/drivers/media/usb/msi2500/Kconfig b/drivers/media/usb/msi2500/Kconfig new file mode 100644 index 0000000..9eff8a7 --- /dev/null +++ b/drivers/media/usb/msi2500/Kconfig @@ -0,0 +1,5 @@ +config USB_MSI2500 + tristate "Mirics MSi2500" + depends on VIDEO_V4L2 && SPI + select VIDEOBUF2_VMALLOC + select MEDIA_TUNER_MSI001 diff --git a/drivers/media/usb/msi2500/Makefile b/drivers/media/usb/msi2500/Makefile new file mode 100644 index 0000000..b3bc2e5 --- /dev/null +++ b/drivers/media/usb/msi2500/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_MSI2500) += msi2500.o diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c new file mode 100644 index 0000000..53aca38 --- /dev/null +++ b/drivers/media/usb/msi2500/msi2500.c @@ -0,0 +1,1517 @@ +/* + * Mirics MSi3101 SDR Dongle driver + * + * Copyright (C) 2013 Antti Palosaari + * + * 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. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * That driver is somehow based of pwc driver: + * (C) 1999-2004 Nemosoft Unv. + * (C) 2004-2006 Luc Saillard (luc@saillard.org) + * (C) 2011 Hans de Goede + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * iConfiguration 0 + * bInterfaceNumber 0 + * bAlternateSetting 1 + * bNumEndpoints 1 + * bEndpointAddress 0x81 EP 1 IN + * bmAttributes 1 + * Transfer Type Isochronous + * wMaxPacketSize 0x1400 3x 1024 bytes + * bInterval 1 + */ +#define MAX_ISO_BUFS (8) +#define ISO_FRAMES_PER_DESC (8) +#define ISO_MAX_FRAME_SIZE (3 * 1024) +#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) +#define MAX_ISOC_ERRORS 20 + +/* TODO: These should be moved to V4L2 API */ +#define V4L2_PIX_FMT_SDR_S8 v4l2_fourcc('D', 'S', '0', '8') /* signed 8-bit */ +#define V4L2_PIX_FMT_SDR_S12 v4l2_fourcc('D', 'S', '1', '2') /* signed 12-bit */ +#define V4L2_PIX_FMT_SDR_S14 v4l2_fourcc('D', 'S', '1', '4') /* signed 14-bit */ +#define V4L2_PIX_FMT_SDR_MSI2500_384 v4l2_fourcc('M', '3', '8', '4') /* Mirics MSi2500 format 384 */ + +static const struct v4l2_frequency_band bands[] = { + { + .tuner = 0, + .type = V4L2_TUNER_ADC, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 1200000, + .rangehigh = 15000000, + }, +}; + +/* stream formats */ +struct msi3101_format { + char *name; + u32 pixelformat; +}; + +/* format descriptions for capture and preview */ +static struct msi3101_format formats[] = { + { + .name = "IQ U8", + .pixelformat = V4L2_SDR_FMT_CU8, + }, { + .name = "IQ U16LE", + .pixelformat = V4L2_SDR_FMT_CU16LE, +#if 0 + }, { + .name = "8-bit signed", + .pixelformat = V4L2_PIX_FMT_SDR_S8, + }, { + .name = "10+2-bit signed", + .pixelformat = V4L2_PIX_FMT_SDR_MSI2500_384, + }, { + .name = "12-bit signed", + .pixelformat = V4L2_PIX_FMT_SDR_S12, + }, { + .name = "14-bit signed", + .pixelformat = V4L2_PIX_FMT_SDR_S14, +#endif + }, +}; + +static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); + +/* intermediate buffers with raw data from the USB device */ +struct msi3101_frame_buf { + struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ + struct list_head list; +}; + +struct msi3101_state { + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct v4l2_subdev *v4l2_subdev; + struct spi_master *master; + + /* videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + + /* Note if taking both locks v4l2_lock must always be locked first! */ + struct mutex v4l2_lock; /* Protects everything else */ + struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ + + /* Pointer to our usb_device, will be NULL after unplug */ + struct usb_device *udev; /* Both mutexes most be hold when setting! */ + + unsigned int f_adc; + u32 pixelformat; + + unsigned int isoc_errors; /* number of contiguous ISOC errors */ + unsigned int vb_full; /* vb is full and packets dropped */ + + struct urb *urbs[MAX_ISO_BUFS]; + int (*convert_stream)(struct msi3101_state *s, u8 *dst, u8 *src, + unsigned int src_len); + + /* Controls */ + struct v4l2_ctrl_handler hdl; + + u32 next_sample; /* for track lost packets */ + u32 sample; /* for sample rate calc */ + unsigned long jiffies_next; + unsigned int sample_ctrl_bit[4]; +}; + +/* Private functions */ +static struct msi3101_frame_buf *msi3101_get_next_fill_buf( + struct msi3101_state *s) +{ + unsigned long flags = 0; + struct msi3101_frame_buf *buf = NULL; + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + if (list_empty(&s->queued_bufs)) + goto leave; + + buf = list_entry(s->queued_bufs.next, struct msi3101_frame_buf, list); + list_del(&buf->list); +leave: + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); + return buf; +} + +/* + * +=========================================================================== + * | 00-1023 | USB packet type '504' + * +=========================================================================== + * | 00- 03 | sequence number of first sample in that USB packet + * +--------------------------------------------------------------------------- + * | 04- 15 | garbage + * +--------------------------------------------------------------------------- + * | 16-1023 | samples + * +--------------------------------------------------------------------------- + * signed 8-bit sample + * 504 * 2 = 1008 samples + */ +static int msi3101_convert_stream_504(struct msi3101_state *s, u8 *dst, + u8 *src, unsigned int src_len) +{ + int i, i_max, dst_len = 0; + u32 sample_num[3]; + + /* There could be 1-3 1024 bytes URB frames */ + i_max = src_len / 1024; + + for (i = 0; i < i_max; i++) { + sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; + if (i == 0 && s->next_sample != sample_num[0]) { + dev_dbg_ratelimited(&s->udev->dev, + "%d samples lost, %d %08x:%08x\n", + sample_num[0] - s->next_sample, + src_len, s->next_sample, sample_num[0]); + } + + /* + * Dump all unknown 'garbage' data - maybe we will discover + * someday if there is something rational... + */ + dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); + + /* 504 x I+Q samples */ + src += 16; + memcpy(dst, src, 1008); + src += 1008; + dst += 1008; + dst_len += 1008; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { + unsigned long jiffies_now = jiffies; + unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); + unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies_now; + s->sample = sample_num[i_max - 1]; + dev_dbg(&s->udev->dev, + "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", + src_len, samples, msecs, + samples * 1000UL / msecs); + } + + /* next sample (sample = sample + i * 504) */ + s->next_sample = sample_num[i_max - 1] + 504; + + return dst_len; +} + +static int msi3101_convert_stream_504_u8(struct msi3101_state *s, u8 *dst, + u8 *src, unsigned int src_len) +{ + int i, j, i_max, dst_len = 0; + u32 sample_num[3]; + s8 *s8src; + u8 *u8dst; + + /* There could be 1-3 1024 bytes URB frames */ + i_max = src_len / 1024; + u8dst = (u8 *) dst; + + for (i = 0; i < i_max; i++) { + sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; + if (i == 0 && s->next_sample != sample_num[0]) { + dev_dbg_ratelimited(&s->udev->dev, + "%d samples lost, %d %08x:%08x\n", + sample_num[0] - s->next_sample, + src_len, s->next_sample, sample_num[0]); + } + + /* + * Dump all unknown 'garbage' data - maybe we will discover + * someday if there is something rational... + */ + dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); + + /* 504 x I+Q samples */ + src += 16; + + s8src = (s8 *) src; + for (j = 0; j < 1008; j++) + *u8dst++ = *s8src++ + 128; + + src += 1008; + dst += 1008; + dst_len += 1008; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if (unlikely(time_is_before_jiffies(s->jiffies_next))) { +#define MSECS 10000UL + unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); + s->sample = sample_num[i_max - 1]; + dev_dbg(&s->udev->dev, + "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", + src_len, samples, MSECS, + samples * 1000UL / MSECS); + } + + /* next sample (sample = sample + i * 504) */ + s->next_sample = sample_num[i_max - 1] + 504; + + return dst_len; +} + +/* + * +=========================================================================== + * | 00-1023 | USB packet type '384' + * +=========================================================================== + * | 00- 03 | sequence number of first sample in that USB packet + * +--------------------------------------------------------------------------- + * | 04- 15 | garbage + * +--------------------------------------------------------------------------- + * | 16- 175 | samples + * +--------------------------------------------------------------------------- + * | 176- 179 | control bits for previous samples + * +--------------------------------------------------------------------------- + * | 180- 339 | samples + * +--------------------------------------------------------------------------- + * | 340- 343 | control bits for previous samples + * +--------------------------------------------------------------------------- + * | 344- 503 | samples + * +--------------------------------------------------------------------------- + * | 504- 507 | control bits for previous samples + * +--------------------------------------------------------------------------- + * | 508- 667 | samples + * +--------------------------------------------------------------------------- + * | 668- 671 | control bits for previous samples + * +--------------------------------------------------------------------------- + * | 672- 831 | samples + * +--------------------------------------------------------------------------- + * | 832- 835 | control bits for previous samples + * +--------------------------------------------------------------------------- + * | 836- 995 | samples + * +--------------------------------------------------------------------------- + * | 996- 999 | control bits for previous samples + * +--------------------------------------------------------------------------- + * | 1000-1023 | garbage + * +--------------------------------------------------------------------------- + * + * Bytes 4 - 7 could have some meaning? + * + * Control bits for previous samples is 32-bit field, containing 16 x 2-bit + * numbers. This results one 2-bit number for 8 samples. It is likely used for + * for bit shifting sample by given bits, increasing actual sampling resolution. + * Number 2 (0b10) was never seen. + * + * 6 * 16 * 2 * 4 = 768 samples. 768 * 4 = 3072 bytes + */ +static int msi3101_convert_stream_384(struct msi3101_state *s, u8 *dst, + u8 *src, unsigned int src_len) +{ + int i, i_max, dst_len = 0; + u32 sample_num[3]; + + /* There could be 1-3 1024 bytes URB frames */ + i_max = src_len / 1024; + for (i = 0; i < i_max; i++) { + sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; + if (i == 0 && s->next_sample != sample_num[0]) { + dev_dbg_ratelimited(&s->udev->dev, + "%d samples lost, %d %08x:%08x\n", + sample_num[0] - s->next_sample, + src_len, s->next_sample, sample_num[0]); + } + + /* + * Dump all unknown 'garbage' data - maybe we will discover + * someday if there is something rational... + */ + dev_dbg_ratelimited(&s->udev->dev, + "%*ph %*ph\n", 12, &src[4], 24, &src[1000]); + + /* 384 x I+Q samples */ + src += 16; + memcpy(dst, src, 984); + src += 984 + 24; + dst += 984; + dst_len += 984; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { + unsigned long jiffies_now = jiffies; + unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); + unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies_now; + s->sample = sample_num[i_max - 1]; + dev_dbg(&s->udev->dev, + "slen=%d samples=%u msecs=%lu sampling rate=%lu bits=%d.%d.%d.%d\n", + src_len, samples, msecs, + samples * 1000UL / msecs, + s->sample_ctrl_bit[0], s->sample_ctrl_bit[1], + s->sample_ctrl_bit[2], s->sample_ctrl_bit[3]); + } + + /* next sample (sample = sample + i * 384) */ + s->next_sample = sample_num[i_max - 1] + 384; + + return dst_len; +} + +/* + * +=========================================================================== + * | 00-1023 | USB packet type '336' + * +=========================================================================== + * | 00- 03 | sequence number of first sample in that USB packet + * +--------------------------------------------------------------------------- + * | 04- 15 | garbage + * +--------------------------------------------------------------------------- + * | 16-1023 | samples + * +--------------------------------------------------------------------------- + * signed 12-bit sample + */ +static int msi3101_convert_stream_336(struct msi3101_state *s, u8 *dst, + u8 *src, unsigned int src_len) +{ + int i, i_max, dst_len = 0; + u32 sample_num[3]; + + /* There could be 1-3 1024 bytes URB frames */ + i_max = src_len / 1024; + + for (i = 0; i < i_max; i++) { + sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; + if (i == 0 && s->next_sample != sample_num[0]) { + dev_dbg_ratelimited(&s->udev->dev, + "%d samples lost, %d %08x:%08x\n", + sample_num[0] - s->next_sample, + src_len, s->next_sample, sample_num[0]); + } + + /* + * Dump all unknown 'garbage' data - maybe we will discover + * someday if there is something rational... + */ + dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); + + /* 336 x I+Q samples */ + src += 16; + memcpy(dst, src, 1008); + src += 1008; + dst += 1008; + dst_len += 1008; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { + unsigned long jiffies_now = jiffies; + unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); + unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies_now; + s->sample = sample_num[i_max - 1]; + dev_dbg(&s->udev->dev, + "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", + src_len, samples, msecs, + samples * 1000UL / msecs); + } + + /* next sample (sample = sample + i * 336) */ + s->next_sample = sample_num[i_max - 1] + 336; + + return dst_len; +} + +/* + * +=========================================================================== + * | 00-1023 | USB packet type '252' + * +=========================================================================== + * | 00- 03 | sequence number of first sample in that USB packet + * +--------------------------------------------------------------------------- + * | 04- 15 | garbage + * +--------------------------------------------------------------------------- + * | 16-1023 | samples + * +--------------------------------------------------------------------------- + * signed 14-bit sample + */ +static int msi3101_convert_stream_252(struct msi3101_state *s, u8 *dst, + u8 *src, unsigned int src_len) +{ + int i, i_max, dst_len = 0; + u32 sample_num[3]; + + /* There could be 1-3 1024 bytes URB frames */ + i_max = src_len / 1024; + + for (i = 0; i < i_max; i++) { + sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; + if (i == 0 && s->next_sample != sample_num[0]) { + dev_dbg_ratelimited(&s->udev->dev, + "%d samples lost, %d %08x:%08x\n", + sample_num[0] - s->next_sample, + src_len, s->next_sample, sample_num[0]); + } + + /* + * Dump all unknown 'garbage' data - maybe we will discover + * someday if there is something rational... + */ + dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); + + /* 252 x I+Q samples */ + src += 16; + memcpy(dst, src, 1008); + src += 1008; + dst += 1008; + dst_len += 1008; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { + unsigned long jiffies_now = jiffies; + unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); + unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies_now; + s->sample = sample_num[i_max - 1]; + dev_dbg(&s->udev->dev, + "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", + src_len, samples, msecs, + samples * 1000UL / msecs); + } + + /* next sample (sample = sample + i * 252) */ + s->next_sample = sample_num[i_max - 1] + 252; + + return dst_len; +} + +static int msi3101_convert_stream_252_u16(struct msi3101_state *s, u8 *dst, + u8 *src, unsigned int src_len) +{ + int i, j, i_max, dst_len = 0; + u32 sample_num[3]; + u16 *u16dst = (u16 *) dst; + struct {signed int x:14;} se; + + /* There could be 1-3 1024 bytes URB frames */ + i_max = src_len / 1024; + + for (i = 0; i < i_max; i++) { + sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; + if (i == 0 && s->next_sample != sample_num[0]) { + dev_dbg_ratelimited(&s->udev->dev, + "%d samples lost, %d %08x:%08x\n", + sample_num[0] - s->next_sample, + src_len, s->next_sample, sample_num[0]); + } + + /* + * Dump all unknown 'garbage' data - maybe we will discover + * someday if there is something rational... + */ + dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); + + /* 252 x I+Q samples */ + src += 16; + + for (j = 0; j < 1008; j += 4) { + unsigned int usample[2]; + int ssample[2]; + + usample[0] = src[j + 0] >> 0 | src[j + 1] << 8; + usample[1] = src[j + 2] >> 0 | src[j + 3] << 8; + + /* sign extension from 14-bit to signed int */ + ssample[0] = se.x = usample[0]; + ssample[1] = se.x = usample[1]; + + /* from signed to unsigned */ + usample[0] = ssample[0] + 8192; + usample[1] = ssample[1] + 8192; + + /* from 14-bit to 16-bit */ + *u16dst++ = (usample[0] << 2) | (usample[0] >> 12); + *u16dst++ = (usample[1] << 2) | (usample[1] >> 12); + } + + src += 1008; + dst += 1008; + dst_len += 1008; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if (unlikely(time_is_before_jiffies(s->jiffies_next))) { +#define MSECS 10000UL + unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); + s->sample = sample_num[i_max - 1]; + dev_dbg(&s->udev->dev, + "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", + src_len, samples, MSECS, + samples * 1000UL / MSECS); + } + + /* next sample (sample = sample + i * 252) */ + s->next_sample = sample_num[i_max - 1] + 252; + + return dst_len; +} + +/* + * This gets called for the Isochronous pipe (stream). This is done in interrupt + * time, so it has to be fast, not crash, and not stall. Neat. + */ +static void msi3101_isoc_handler(struct urb *urb) +{ + struct msi3101_state *s = (struct msi3101_state *)urb->context; + int i, flen, fstatus; + unsigned char *iso_buf = NULL; + struct msi3101_frame_buf *fbuf; + + if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) { + dev_dbg(&s->udev->dev, "URB (%p) unlinked %ssynchronuously\n", + urb, urb->status == -ENOENT ? "" : "a"); + return; + } + + if (unlikely(urb->status != 0)) { + dev_dbg(&s->udev->dev, + "msi3101_isoc_handler() called with status %d\n", + urb->status); + /* Give up after a number of contiguous errors */ + if (++s->isoc_errors > MAX_ISOC_ERRORS) + dev_dbg(&s->udev->dev, + "Too many ISOC errors, bailing out\n"); + goto handler_end; + } else { + /* Reset ISOC error counter. We did get here, after all. */ + s->isoc_errors = 0; + } + + /* Compact data */ + for (i = 0; i < urb->number_of_packets; i++) { + void *ptr; + + /* Check frame error */ + fstatus = urb->iso_frame_desc[i].status; + if (unlikely(fstatus)) { + dev_dbg_ratelimited(&s->udev->dev, + "frame=%d/%d has error %d skipping\n", + i, urb->number_of_packets, fstatus); + continue; + } + + /* Check if that frame contains data */ + flen = urb->iso_frame_desc[i].actual_length; + if (unlikely(flen == 0)) + continue; + + iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* Get free framebuffer */ + fbuf = msi3101_get_next_fill_buf(s); + if (unlikely(fbuf == NULL)) { + s->vb_full++; + dev_dbg_ratelimited(&s->udev->dev, + "videobuf is full, %d packets dropped\n", + s->vb_full); + continue; + } + + /* fill framebuffer */ + ptr = vb2_plane_vaddr(&fbuf->vb, 0); + flen = s->convert_stream(s, ptr, iso_buf, flen); + vb2_set_plane_payload(&fbuf->vb, 0, flen); + vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + } + +handler_end: + i = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(i != 0)) + dev_dbg(&s->udev->dev, + "Error (%d) re-submitting urb in msi3101_isoc_handler\n", + i); +} + +static void msi3101_iso_stop(struct msi3101_state *s) +{ + int i; + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + /* Unlinking ISOC buffers one by one */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (s->urbs[i]) { + dev_dbg(&s->udev->dev, "Unlinking URB %p\n", + s->urbs[i]); + usb_kill_urb(s->urbs[i]); + } + } +} + +static void msi3101_iso_free(struct msi3101_state *s) +{ + int i; + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + /* Freeing ISOC buffers one by one */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (s->urbs[i]) { + dev_dbg(&s->udev->dev, "Freeing URB\n"); + if (s->urbs[i]->transfer_buffer) { + usb_free_coherent(s->udev, + s->urbs[i]->transfer_buffer_length, + s->urbs[i]->transfer_buffer, + s->urbs[i]->transfer_dma); + } + usb_free_urb(s->urbs[i]); + s->urbs[i] = NULL; + } + } +} + +/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ +static void msi3101_isoc_cleanup(struct msi3101_state *s) +{ + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + msi3101_iso_stop(s); + msi3101_iso_free(s); +} + +/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ +static int msi3101_isoc_init(struct msi3101_state *s) +{ + struct usb_device *udev; + struct urb *urb; + int i, j, ret; + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + s->isoc_errors = 0; + udev = s->udev; + + ret = usb_set_interface(s->udev, 0, 1); + if (ret) + return ret; + + /* Allocate and init Isochronuous urbs */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); + if (urb == NULL) { + dev_err(&s->udev->dev, + "Failed to allocate urb %d\n", i); + msi3101_isoc_cleanup(s); + return -ENOMEM; + } + s->urbs[i] = urb; + dev_dbg(&s->udev->dev, "Allocated URB at 0x%p\n", urb); + + urb->interval = 1; + urb->dev = udev; + urb->pipe = usb_rcvisocpipe(udev, 0x81); + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->transfer_buffer = usb_alloc_coherent(udev, ISO_BUFFER_SIZE, + GFP_KERNEL, &urb->transfer_dma); + if (urb->transfer_buffer == NULL) { + dev_err(&s->udev->dev, + "Failed to allocate urb buffer %d\n", + i); + msi3101_isoc_cleanup(s); + return -ENOMEM; + } + urb->transfer_buffer_length = ISO_BUFFER_SIZE; + urb->complete = msi3101_isoc_handler; + urb->context = s; + urb->start_frame = 0; + urb->number_of_packets = ISO_FRAMES_PER_DESC; + for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; + urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE; + } + } + + /* link */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + ret = usb_submit_urb(s->urbs[i], GFP_KERNEL); + if (ret) { + dev_err(&s->udev->dev, + "isoc_init() submit_urb %d failed with error %d\n", + i, ret); + msi3101_isoc_cleanup(s); + return ret; + } + dev_dbg(&s->udev->dev, "URB 0x%p submitted.\n", s->urbs[i]); + } + + /* All is done... */ + return 0; +} + +/* Must be called with vb_queue_lock hold */ +static void msi3101_cleanup_queued_bufs(struct msi3101_state *s) +{ + unsigned long flags = 0; + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + while (!list_empty(&s->queued_bufs)) { + struct msi3101_frame_buf *buf; + + buf = list_entry(s->queued_bufs.next, struct msi3101_frame_buf, + list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); +} + +/* The user yanked out the cable... */ +static void msi3101_disconnect(struct usb_interface *intf) +{ + struct v4l2_device *v = usb_get_intfdata(intf); + struct msi3101_state *s = + container_of(v, struct msi3101_state, v4l2_dev); + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + mutex_lock(&s->vb_queue_lock); + mutex_lock(&s->v4l2_lock); + /* No need to keep the urbs around after disconnection */ + s->udev = NULL; + v4l2_device_disconnect(&s->v4l2_dev); + video_unregister_device(&s->vdev); + spi_unregister_master(s->master); + mutex_unlock(&s->v4l2_lock); + mutex_unlock(&s->vb_queue_lock); + + v4l2_device_put(&s->v4l2_dev); +} + +static int msi3101_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct msi3101_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); + usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +/* Videobuf2 operations */ +static int msi3101_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) +{ + struct msi3101_state *s = vb2_get_drv_priv(vq); + dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); + + /* Absolute min and max number of buffers available for mmap() */ + *nbuffers = clamp_t(unsigned int, *nbuffers, 8, 32); + *nplanes = 1; + /* + * 3, wMaxPacketSize 3x 1024 bytes + * 504, max IQ sample pairs per 1024 frame + * 2, two samples, I and Q + * 2, 16-bit is enough for single sample + */ + sizes[0] = PAGE_ALIGN(3 * 504 * 2 * 2); + dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", + __func__, *nbuffers, sizes[0]); + return 0; +} + +static void msi3101_buf_queue(struct vb2_buffer *vb) +{ + struct msi3101_state *s = vb2_get_drv_priv(vb->vb2_queue); + struct msi3101_frame_buf *buf = + container_of(vb, struct msi3101_frame_buf, vb); + unsigned long flags = 0; + + /* Check the device has not disconnected between prep and queuing */ + if (unlikely(!s->udev)) { + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + return; + } + + spin_lock_irqsave(&s->queued_bufs_lock, flags); + list_add_tail(&buf->list, &s->queued_bufs); + spin_unlock_irqrestore(&s->queued_bufs_lock, flags); +} + +#define CMD_WREG 0x41 +#define CMD_START_STREAMING 0x43 +#define CMD_STOP_STREAMING 0x45 +#define CMD_READ_UNKNOW 0x48 + +#define msi3101_dbg_usb_control_msg(udev, r, t, v, _i, b, l) { \ + char *direction; \ + if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ + direction = ">>>"; \ + else \ + direction = "<<<"; \ + dev_dbg(&udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ + "%s %*ph\n", __func__, t, r, v & 0xff, v >> 8, \ + _i & 0xff, _i >> 8, l & 0xff, l >> 8, direction, l, b); \ +} + +static int msi3101_ctrl_msg(struct msi3101_state *s, u8 cmd, u32 data) +{ + int ret; + u8 request = cmd; + u8 requesttype = USB_DIR_OUT | USB_TYPE_VENDOR; + u16 value = (data >> 0) & 0xffff; + u16 index = (data >> 16) & 0xffff; + + msi3101_dbg_usb_control_msg(s->udev, + request, requesttype, value, index, NULL, 0); + + ret = usb_control_msg(s->udev, usb_sndctrlpipe(s->udev, 0), + request, requesttype, value, index, NULL, 0, 2000); + + if (ret) + dev_err(&s->udev->dev, "%s: failed %d, cmd %02x, data %04x\n", + __func__, ret, cmd, data); + + return ret; +}; + +#define F_REF 24000000 +#define DIV_R_IN 2 +static int msi3101_set_usb_adc(struct msi3101_state *s) +{ + int ret, div_n, div_m, div_r_out, f_sr, f_vco, fract; + u32 reg3, reg4, reg7; + struct v4l2_ctrl *bandwidth_auto; + struct v4l2_ctrl *bandwidth; + + f_sr = s->f_adc; + + /* set tuner, subdev, filters according to sampling rate */ + bandwidth_auto = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); + if (v4l2_ctrl_g_ctrl(bandwidth_auto)) { + bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); + v4l2_ctrl_s_ctrl(bandwidth, s->f_adc); + } + + /* select stream format */ + switch (s->pixelformat) { + case V4L2_SDR_FMT_CU8: + s->convert_stream = msi3101_convert_stream_504_u8; + reg7 = 0x000c9407; + break; + case V4L2_SDR_FMT_CU16LE: + s->convert_stream = msi3101_convert_stream_252_u16; + reg7 = 0x00009407; + break; + case V4L2_PIX_FMT_SDR_S8: + s->convert_stream = msi3101_convert_stream_504; + reg7 = 0x000c9407; + break; + case V4L2_PIX_FMT_SDR_MSI2500_384: + s->convert_stream = msi3101_convert_stream_384; + reg7 = 0x0000a507; + break; + case V4L2_PIX_FMT_SDR_S12: + s->convert_stream = msi3101_convert_stream_336; + reg7 = 0x00008507; + break; + case V4L2_PIX_FMT_SDR_S14: + s->convert_stream = msi3101_convert_stream_252; + reg7 = 0x00009407; + break; + default: + s->convert_stream = msi3101_convert_stream_504_u8; + reg7 = 0x000c9407; + break; + } + + /* + * Synthesizer config is just a educated guess... + * + * [7:0] 0x03, register address + * [8] 1, power control + * [9] ?, power control + * [12:10] output divider + * [13] 0 ? + * [14] 0 ? + * [15] fractional MSB, bit 20 + * [16:19] N + * [23:20] ? + * [24:31] 0x01 + * + * output divider + * val div + * 0 - (invalid) + * 1 4 + * 2 6 + * 3 8 + * 4 10 + * 5 12 + * 6 14 + * 7 16 + * + * VCO 202000000 - 720000000++ + */ + reg3 = 0x01000303; + reg4 = 0x00000004; + + /* XXX: Filters? AGC? */ + if (f_sr < 6000000) + reg3 |= 0x1 << 20; + else if (f_sr < 7000000) + reg3 |= 0x5 << 20; + else if (f_sr < 8500000) + reg3 |= 0x9 << 20; + else + reg3 |= 0xd << 20; + + for (div_r_out = 4; div_r_out < 16; div_r_out += 2) { + f_vco = f_sr * div_r_out * 12; + dev_dbg(&s->udev->dev, "%s: div_r_out=%d f_vco=%d\n", + __func__, div_r_out, f_vco); + if (f_vco >= 202000000) + break; + } + + div_n = f_vco / (F_REF * DIV_R_IN); + div_m = f_vco % (F_REF * DIV_R_IN); + fract = 0x200000ul * div_m / (F_REF * DIV_R_IN); + + reg3 |= div_n << 16; + reg3 |= (div_r_out / 2 - 1) << 10; + reg3 |= ((fract >> 20) & 0x000001) << 15; /* [20] */ + reg4 |= ((fract >> 0) & 0x0fffff) << 8; /* [19:0] */ + + dev_dbg(&s->udev->dev, + "%s: f_sr=%d f_vco=%d div_n=%d div_m=%d div_r_out=%d reg3=%08x reg4=%08x\n", + __func__, f_sr, f_vco, div_n, div_m, div_r_out, reg3, reg4); + + ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00608008); + if (ret) + goto err; + + ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00000c05); + if (ret) + goto err; + + ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00020000); + if (ret) + goto err; + + ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00480102); + if (ret) + goto err; + + ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00f38008); + if (ret) + goto err; + + ret = msi3101_ctrl_msg(s, CMD_WREG, reg7); + if (ret) + goto err; + + ret = msi3101_ctrl_msg(s, CMD_WREG, reg4); + if (ret) + goto err; + + ret = msi3101_ctrl_msg(s, CMD_WREG, reg3); + if (ret) + goto err; +err: + return ret; +}; + +static int msi3101_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct msi3101_state *s = vb2_get_drv_priv(vq); + int ret; + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + if (!s->udev) + return -ENODEV; + + if (mutex_lock_interruptible(&s->v4l2_lock)) + return -ERESTARTSYS; + + /* wake-up tuner */ + v4l2_subdev_call(s->v4l2_subdev, core, s_power, 1); + + ret = msi3101_set_usb_adc(s); + + ret = msi3101_isoc_init(s); + if (ret) + msi3101_cleanup_queued_bufs(s); + + ret = msi3101_ctrl_msg(s, CMD_START_STREAMING, 0); + + mutex_unlock(&s->v4l2_lock); + + return ret; +} + +static void msi3101_stop_streaming(struct vb2_queue *vq) +{ + struct msi3101_state *s = vb2_get_drv_priv(vq); + + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + mutex_lock(&s->v4l2_lock); + + if (s->udev) + msi3101_isoc_cleanup(s); + + msi3101_cleanup_queued_bufs(s); + + /* according to tests, at least 700us delay is required */ + msleep(20); + if (!msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0)) { + /* sleep USB IF / ADC */ + msi3101_ctrl_msg(s, CMD_WREG, 0x01000003); + } + + /* sleep tuner */ + v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0); + + mutex_unlock(&s->v4l2_lock); +} + +static struct vb2_ops msi3101_vb2_ops = { + .queue_setup = msi3101_queue_setup, + .buf_queue = msi3101_buf_queue, + .start_streaming = msi3101_start_streaming, + .stop_streaming = msi3101_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int msi3101_enum_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct msi3101_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index); + + if (f->index >= NUM_FORMATS) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, sizeof(f->description)); + f->pixelformat = formats[f->index].pixelformat; + + return 0; +} + +static int msi3101_g_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct msi3101_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + (char *)&s->pixelformat); + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + f->fmt.sdr.pixelformat = s->pixelformat; + + return 0; +} + +static int msi3101_s_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct msi3101_state *s = video_drvdata(file); + struct vb2_queue *q = &s->vb_queue; + int i; + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + (char *)&f->fmt.sdr.pixelformat); + + if (vb2_is_busy(q)) + return -EBUSY; + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + s->pixelformat = f->fmt.sdr.pixelformat; + return 0; + } + } + + f->fmt.sdr.pixelformat = formats[0].pixelformat; + s->pixelformat = formats[0].pixelformat; + + return 0; +} + +static int msi3101_try_fmt_sdr_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct msi3101_state *s = video_drvdata(file); + int i; + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, + (char *)&f->fmt.sdr.pixelformat); + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) + return 0; + } + + f->fmt.sdr.pixelformat = formats[0].pixelformat; + + return 0; +} + +static int msi3101_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *v) +{ + struct msi3101_state *s = video_drvdata(file); + int ret; + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); + + if (v->index == 0) + ret = 0; + else if (v->index == 1) + ret = v4l2_subdev_call(s->v4l2_subdev, tuner, s_tuner, v); + else + ret = -EINVAL; + + return ret; +} + +static int msi3101_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) +{ + struct msi3101_state *s = video_drvdata(file); + int ret; + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); + + if (v->index == 0) { + strlcpy(v->name, "Mirics MSi2500", sizeof(v->name)); + v->type = V4L2_TUNER_ADC; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = 1200000; + v->rangehigh = 15000000; + ret = 0; + } else if (v->index == 1) { + ret = v4l2_subdev_call(s->v4l2_subdev, tuner, g_tuner, v); + } else { + ret = -EINVAL; + } + + return ret; +} + +static int msi3101_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct msi3101_state *s = video_drvdata(file); + int ret = 0; + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", + __func__, f->tuner, f->type); + + if (f->tuner == 0) { + f->frequency = s->f_adc; + ret = 0; + } else if (f->tuner == 1) { + f->type = V4L2_TUNER_RF; + ret = v4l2_subdev_call(s->v4l2_subdev, tuner, g_frequency, f); + } else { + ret = -EINVAL; + } + + return ret; +} + +static int msi3101_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct msi3101_state *s = video_drvdata(file); + int ret; + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", + __func__, f->tuner, f->type, f->frequency); + + if (f->tuner == 0) { + s->f_adc = clamp_t(unsigned int, f->frequency, + bands[0].rangelow, + bands[0].rangehigh); + dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n", + __func__, s->f_adc); + ret = msi3101_set_usb_adc(s); + } else if (f->tuner == 1) { + ret = v4l2_subdev_call(s->v4l2_subdev, tuner, s_frequency, f); + } else { + ret = -EINVAL; + } + + return ret; +} + +static int msi3101_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + struct msi3101_state *s = video_drvdata(file); + int ret; + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", + __func__, band->tuner, band->type, band->index); + + if (band->tuner == 0) { + if (band->index >= ARRAY_SIZE(bands)) { + ret = -EINVAL; + } else { + *band = bands[band->index]; + ret = 0; + } + } else if (band->tuner == 1) { + ret = v4l2_subdev_call(s->v4l2_subdev, tuner, + enum_freq_bands, band); + } else { + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ioctl_ops msi3101_ioctl_ops = { + .vidioc_querycap = msi3101_querycap, + + .vidioc_enum_fmt_sdr_cap = msi3101_enum_fmt_sdr_cap, + .vidioc_g_fmt_sdr_cap = msi3101_g_fmt_sdr_cap, + .vidioc_s_fmt_sdr_cap = msi3101_s_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = msi3101_try_fmt_sdr_cap, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_g_tuner = msi3101_g_tuner, + .vidioc_s_tuner = msi3101_s_tuner, + + .vidioc_g_frequency = msi3101_g_frequency, + .vidioc_s_frequency = msi3101_s_frequency, + .vidioc_enum_freq_bands = msi3101_enum_freq_bands, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_log_status = v4l2_ctrl_log_status, +}; + +static const struct v4l2_file_operations msi3101_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static struct video_device msi3101_template = { + .name = "Mirics MSi3101 SDR Dongle", + .release = video_device_release_empty, + .fops = &msi3101_fops, + .ioctl_ops = &msi3101_ioctl_ops, +}; + +static void msi3101_video_release(struct v4l2_device *v) +{ + struct msi3101_state *s = + container_of(v, struct msi3101_state, v4l2_dev); + + v4l2_ctrl_handler_free(&s->hdl); + v4l2_device_unregister(&s->v4l2_dev); + kfree(s); +} + +static int msi3101_transfer_one_message(struct spi_master *master, + struct spi_message *m) +{ + struct msi3101_state *s = spi_master_get_devdata(master); + struct spi_transfer *t; + int ret = 0; + u32 data; + + list_for_each_entry(t, &m->transfers, transfer_list) { + dev_dbg(&s->udev->dev, "%s: msg=%*ph\n", + __func__, t->len, t->tx_buf); + data = 0x09; /* reg 9 is SPI adapter */ + data |= ((u8 *)t->tx_buf)[0] << 8; + data |= ((u8 *)t->tx_buf)[1] << 16; + data |= ((u8 *)t->tx_buf)[2] << 24; + ret = msi3101_ctrl_msg(s, CMD_WREG, data); + } + + m->status = ret; + spi_finalize_current_message(master); + return ret; +} + +static int msi3101_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct msi3101_state *s = NULL; + struct v4l2_subdev *sd; + struct spi_master *master; + int ret; + static struct spi_board_info board_info = { + .modalias = "msi001", + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 12000000, + }; + + s = kzalloc(sizeof(struct msi3101_state), GFP_KERNEL); + if (s == NULL) { + pr_err("Could not allocate memory for msi3101_state\n"); + return -ENOMEM; + } + + mutex_init(&s->v4l2_lock); + mutex_init(&s->vb_queue_lock); + spin_lock_init(&s->queued_bufs_lock); + INIT_LIST_HEAD(&s->queued_bufs); + s->udev = udev; + s->f_adc = bands[0].rangelow; + s->pixelformat = V4L2_SDR_FMT_CU8; + + /* Init videobuf2 queue structure */ + s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; + s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + s->vb_queue.drv_priv = s; + s->vb_queue.buf_struct_size = sizeof(struct msi3101_frame_buf); + s->vb_queue.ops = &msi3101_vb2_ops; + s->vb_queue.mem_ops = &vb2_vmalloc_memops; + s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&s->vb_queue); + if (ret) { + dev_err(&s->udev->dev, "Could not initialize vb2 queue\n"); + goto err_free_mem; + } + + /* Init video_device structure */ + s->vdev = msi3101_template; + s->vdev.queue = &s->vb_queue; + s->vdev.queue->lock = &s->vb_queue_lock; + video_set_drvdata(&s->vdev, s); + + /* Register the v4l2_device structure */ + s->v4l2_dev.release = msi3101_video_release; + ret = v4l2_device_register(&intf->dev, &s->v4l2_dev); + if (ret) { + dev_err(&s->udev->dev, + "Failed to register v4l2-device (%d)\n", ret); + goto err_free_mem; + } + + /* SPI master adapter */ + master = spi_alloc_master(&s->udev->dev, 0); + if (master == NULL) { + ret = -ENOMEM; + goto err_unregister_v4l2_dev; + } + + s->master = master; + master->bus_num = 0; + master->num_chipselect = 1; + master->transfer_one_message = msi3101_transfer_one_message; + spi_master_set_devdata(master, s); + ret = spi_register_master(master); + if (ret) { + spi_master_put(master); + goto err_unregister_v4l2_dev; + } + + /* load v4l2 subdevice */ + sd = v4l2_spi_new_subdev(&s->v4l2_dev, master, &board_info); + s->v4l2_subdev = sd; + if (sd == NULL) { + dev_err(&s->udev->dev, "cannot get v4l2 subdevice\n"); + ret = -ENODEV; + goto err_unregister_master; + } + + /* Register controls */ + v4l2_ctrl_handler_init(&s->hdl, 0); + if (s->hdl.error) { + ret = s->hdl.error; + dev_err(&s->udev->dev, "Could not initialize controls\n"); + goto err_free_controls; + } + + /* currently all controls are from subdev */ + v4l2_ctrl_add_handler(&s->hdl, sd->ctrl_handler, NULL); + + s->v4l2_dev.ctrl_handler = &s->hdl; + s->vdev.v4l2_dev = &s->v4l2_dev; + s->vdev.lock = &s->v4l2_lock; + + ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); + if (ret) { + dev_err(&s->udev->dev, + "Failed to register as video device (%d)\n", + ret); + goto err_unregister_v4l2_dev; + } + dev_info(&s->udev->dev, "Registered as %s\n", + video_device_node_name(&s->vdev)); + + return 0; + +err_free_controls: + v4l2_ctrl_handler_free(&s->hdl); +err_unregister_master: + spi_unregister_master(s->master); +err_unregister_v4l2_dev: + v4l2_device_unregister(&s->v4l2_dev); +err_free_mem: + kfree(s); + return ret; +} + +/* USB device ID list */ +static struct usb_device_id msi3101_id_table[] = { + { USB_DEVICE(0x1df7, 0x2500) }, /* Mirics MSi3101 SDR Dongle */ + { USB_DEVICE(0x2040, 0xd300) }, /* Hauppauge WinTV 133559 LF */ + { } +}; +MODULE_DEVICE_TABLE(usb, msi3101_id_table); + +/* USB subsystem interface */ +static struct usb_driver msi3101_driver = { + .name = KBUILD_MODNAME, + .probe = msi3101_probe, + .disconnect = msi3101_disconnect, + .id_table = msi3101_id_table, +}; + +module_usb_driver(msi3101_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Mirics MSi3101 SDR Dongle"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index a326d5d..e91c538 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -31,8 +31,6 @@ source "drivers/staging/media/dt3155v4l/Kconfig" source "drivers/staging/media/go7007/Kconfig" -source "drivers/staging/media/msi3101/Kconfig" - source "drivers/staging/media/omap24xx/Kconfig" source "drivers/staging/media/solo6x10/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index e1e053a..4f6c0ee 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -5,7 +5,6 @@ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_SOLO6X10) += solo6x10/ obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ -obj-$(CONFIG_USB_MSI3101) += msi3101/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_OMAP2) += omap24xx/ diff --git a/drivers/staging/media/msi3101/Kconfig b/drivers/staging/media/msi3101/Kconfig deleted file mode 100644 index 399dbbb..0000000 --- a/drivers/staging/media/msi3101/Kconfig +++ /dev/null @@ -1,7 +0,0 @@ -config USB_MSI3101 - tristate "Mirics MSi3101 SDR Dongle" - depends on USB && VIDEO_DEV && VIDEO_V4L2 && SPI - select VIDEOBUF2_CORE - select VIDEOBUF2_VMALLOC - select MEDIA_TUNER_MSI001 - diff --git a/drivers/staging/media/msi3101/Makefile b/drivers/staging/media/msi3101/Makefile deleted file mode 100644 index 3730654..0000000 --- a/drivers/staging/media/msi3101/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_USB_MSI3101) += sdr-msi3101.o diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c deleted file mode 100644 index 53aca38..0000000 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ /dev/null @@ -1,1517 +0,0 @@ -/* - * Mirics MSi3101 SDR Dongle driver - * - * Copyright (C) 2013 Antti Palosaari - * - * 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. - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * That driver is somehow based of pwc driver: - * (C) 1999-2004 Nemosoft Unv. - * (C) 2004-2006 Luc Saillard (luc@saillard.org) - * (C) 2011 Hans de Goede - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * iConfiguration 0 - * bInterfaceNumber 0 - * bAlternateSetting 1 - * bNumEndpoints 1 - * bEndpointAddress 0x81 EP 1 IN - * bmAttributes 1 - * Transfer Type Isochronous - * wMaxPacketSize 0x1400 3x 1024 bytes - * bInterval 1 - */ -#define MAX_ISO_BUFS (8) -#define ISO_FRAMES_PER_DESC (8) -#define ISO_MAX_FRAME_SIZE (3 * 1024) -#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) -#define MAX_ISOC_ERRORS 20 - -/* TODO: These should be moved to V4L2 API */ -#define V4L2_PIX_FMT_SDR_S8 v4l2_fourcc('D', 'S', '0', '8') /* signed 8-bit */ -#define V4L2_PIX_FMT_SDR_S12 v4l2_fourcc('D', 'S', '1', '2') /* signed 12-bit */ -#define V4L2_PIX_FMT_SDR_S14 v4l2_fourcc('D', 'S', '1', '4') /* signed 14-bit */ -#define V4L2_PIX_FMT_SDR_MSI2500_384 v4l2_fourcc('M', '3', '8', '4') /* Mirics MSi2500 format 384 */ - -static const struct v4l2_frequency_band bands[] = { - { - .tuner = 0, - .type = V4L2_TUNER_ADC, - .index = 0, - .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 1200000, - .rangehigh = 15000000, - }, -}; - -/* stream formats */ -struct msi3101_format { - char *name; - u32 pixelformat; -}; - -/* format descriptions for capture and preview */ -static struct msi3101_format formats[] = { - { - .name = "IQ U8", - .pixelformat = V4L2_SDR_FMT_CU8, - }, { - .name = "IQ U16LE", - .pixelformat = V4L2_SDR_FMT_CU16LE, -#if 0 - }, { - .name = "8-bit signed", - .pixelformat = V4L2_PIX_FMT_SDR_S8, - }, { - .name = "10+2-bit signed", - .pixelformat = V4L2_PIX_FMT_SDR_MSI2500_384, - }, { - .name = "12-bit signed", - .pixelformat = V4L2_PIX_FMT_SDR_S12, - }, { - .name = "14-bit signed", - .pixelformat = V4L2_PIX_FMT_SDR_S14, -#endif - }, -}; - -static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); - -/* intermediate buffers with raw data from the USB device */ -struct msi3101_frame_buf { - struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ - struct list_head list; -}; - -struct msi3101_state { - struct video_device vdev; - struct v4l2_device v4l2_dev; - struct v4l2_subdev *v4l2_subdev; - struct spi_master *master; - - /* videobuf2 queue and queued buffers list */ - struct vb2_queue vb_queue; - struct list_head queued_bufs; - spinlock_t queued_bufs_lock; /* Protects queued_bufs */ - - /* Note if taking both locks v4l2_lock must always be locked first! */ - struct mutex v4l2_lock; /* Protects everything else */ - struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ - - /* Pointer to our usb_device, will be NULL after unplug */ - struct usb_device *udev; /* Both mutexes most be hold when setting! */ - - unsigned int f_adc; - u32 pixelformat; - - unsigned int isoc_errors; /* number of contiguous ISOC errors */ - unsigned int vb_full; /* vb is full and packets dropped */ - - struct urb *urbs[MAX_ISO_BUFS]; - int (*convert_stream)(struct msi3101_state *s, u8 *dst, u8 *src, - unsigned int src_len); - - /* Controls */ - struct v4l2_ctrl_handler hdl; - - u32 next_sample; /* for track lost packets */ - u32 sample; /* for sample rate calc */ - unsigned long jiffies_next; - unsigned int sample_ctrl_bit[4]; -}; - -/* Private functions */ -static struct msi3101_frame_buf *msi3101_get_next_fill_buf( - struct msi3101_state *s) -{ - unsigned long flags = 0; - struct msi3101_frame_buf *buf = NULL; - - spin_lock_irqsave(&s->queued_bufs_lock, flags); - if (list_empty(&s->queued_bufs)) - goto leave; - - buf = list_entry(s->queued_bufs.next, struct msi3101_frame_buf, list); - list_del(&buf->list); -leave: - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); - return buf; -} - -/* - * +=========================================================================== - * | 00-1023 | USB packet type '504' - * +=========================================================================== - * | 00- 03 | sequence number of first sample in that USB packet - * +--------------------------------------------------------------------------- - * | 04- 15 | garbage - * +--------------------------------------------------------------------------- - * | 16-1023 | samples - * +--------------------------------------------------------------------------- - * signed 8-bit sample - * 504 * 2 = 1008 samples - */ -static int msi3101_convert_stream_504(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) -{ - int i, i_max, dst_len = 0; - u32 sample_num[3]; - - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; - - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { - dev_dbg_ratelimited(&s->udev->dev, - "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); - } - - /* - * Dump all unknown 'garbage' data - maybe we will discover - * someday if there is something rational... - */ - dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); - - /* 504 x I+Q samples */ - src += 16; - memcpy(dst, src, 1008); - src += 1008; - dst += 1008; - dst_len += 1008; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { - unsigned long jiffies_now = jiffies; - unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); - unsigned int samples = sample_num[i_max - 1] - s->sample; - s->jiffies_next = jiffies_now; - s->sample = sample_num[i_max - 1]; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, msecs, - samples * 1000UL / msecs); - } - - /* next sample (sample = sample + i * 504) */ - s->next_sample = sample_num[i_max - 1] + 504; - - return dst_len; -} - -static int msi3101_convert_stream_504_u8(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) -{ - int i, j, i_max, dst_len = 0; - u32 sample_num[3]; - s8 *s8src; - u8 *u8dst; - - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; - u8dst = (u8 *) dst; - - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { - dev_dbg_ratelimited(&s->udev->dev, - "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); - } - - /* - * Dump all unknown 'garbage' data - maybe we will discover - * someday if there is something rational... - */ - dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); - - /* 504 x I+Q samples */ - src += 16; - - s8src = (s8 *) src; - for (j = 0; j < 1008; j++) - *u8dst++ = *s8src++ + 128; - - src += 1008; - dst += 1008; - dst_len += 1008; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if (unlikely(time_is_before_jiffies(s->jiffies_next))) { -#define MSECS 10000UL - unsigned int samples = sample_num[i_max - 1] - s->sample; - s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); - s->sample = sample_num[i_max - 1]; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, MSECS, - samples * 1000UL / MSECS); - } - - /* next sample (sample = sample + i * 504) */ - s->next_sample = sample_num[i_max - 1] + 504; - - return dst_len; -} - -/* - * +=========================================================================== - * | 00-1023 | USB packet type '384' - * +=========================================================================== - * | 00- 03 | sequence number of first sample in that USB packet - * +--------------------------------------------------------------------------- - * | 04- 15 | garbage - * +--------------------------------------------------------------------------- - * | 16- 175 | samples - * +--------------------------------------------------------------------------- - * | 176- 179 | control bits for previous samples - * +--------------------------------------------------------------------------- - * | 180- 339 | samples - * +--------------------------------------------------------------------------- - * | 340- 343 | control bits for previous samples - * +--------------------------------------------------------------------------- - * | 344- 503 | samples - * +--------------------------------------------------------------------------- - * | 504- 507 | control bits for previous samples - * +--------------------------------------------------------------------------- - * | 508- 667 | samples - * +--------------------------------------------------------------------------- - * | 668- 671 | control bits for previous samples - * +--------------------------------------------------------------------------- - * | 672- 831 | samples - * +--------------------------------------------------------------------------- - * | 832- 835 | control bits for previous samples - * +--------------------------------------------------------------------------- - * | 836- 995 | samples - * +--------------------------------------------------------------------------- - * | 996- 999 | control bits for previous samples - * +--------------------------------------------------------------------------- - * | 1000-1023 | garbage - * +--------------------------------------------------------------------------- - * - * Bytes 4 - 7 could have some meaning? - * - * Control bits for previous samples is 32-bit field, containing 16 x 2-bit - * numbers. This results one 2-bit number for 8 samples. It is likely used for - * for bit shifting sample by given bits, increasing actual sampling resolution. - * Number 2 (0b10) was never seen. - * - * 6 * 16 * 2 * 4 = 768 samples. 768 * 4 = 3072 bytes - */ -static int msi3101_convert_stream_384(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) -{ - int i, i_max, dst_len = 0; - u32 sample_num[3]; - - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { - dev_dbg_ratelimited(&s->udev->dev, - "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); - } - - /* - * Dump all unknown 'garbage' data - maybe we will discover - * someday if there is something rational... - */ - dev_dbg_ratelimited(&s->udev->dev, - "%*ph %*ph\n", 12, &src[4], 24, &src[1000]); - - /* 384 x I+Q samples */ - src += 16; - memcpy(dst, src, 984); - src += 984 + 24; - dst += 984; - dst_len += 984; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { - unsigned long jiffies_now = jiffies; - unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); - unsigned int samples = sample_num[i_max - 1] - s->sample; - s->jiffies_next = jiffies_now; - s->sample = sample_num[i_max - 1]; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu bits=%d.%d.%d.%d\n", - src_len, samples, msecs, - samples * 1000UL / msecs, - s->sample_ctrl_bit[0], s->sample_ctrl_bit[1], - s->sample_ctrl_bit[2], s->sample_ctrl_bit[3]); - } - - /* next sample (sample = sample + i * 384) */ - s->next_sample = sample_num[i_max - 1] + 384; - - return dst_len; -} - -/* - * +=========================================================================== - * | 00-1023 | USB packet type '336' - * +=========================================================================== - * | 00- 03 | sequence number of first sample in that USB packet - * +--------------------------------------------------------------------------- - * | 04- 15 | garbage - * +--------------------------------------------------------------------------- - * | 16-1023 | samples - * +--------------------------------------------------------------------------- - * signed 12-bit sample - */ -static int msi3101_convert_stream_336(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) -{ - int i, i_max, dst_len = 0; - u32 sample_num[3]; - - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; - - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { - dev_dbg_ratelimited(&s->udev->dev, - "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); - } - - /* - * Dump all unknown 'garbage' data - maybe we will discover - * someday if there is something rational... - */ - dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); - - /* 336 x I+Q samples */ - src += 16; - memcpy(dst, src, 1008); - src += 1008; - dst += 1008; - dst_len += 1008; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { - unsigned long jiffies_now = jiffies; - unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); - unsigned int samples = sample_num[i_max - 1] - s->sample; - s->jiffies_next = jiffies_now; - s->sample = sample_num[i_max - 1]; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, msecs, - samples * 1000UL / msecs); - } - - /* next sample (sample = sample + i * 336) */ - s->next_sample = sample_num[i_max - 1] + 336; - - return dst_len; -} - -/* - * +=========================================================================== - * | 00-1023 | USB packet type '252' - * +=========================================================================== - * | 00- 03 | sequence number of first sample in that USB packet - * +--------------------------------------------------------------------------- - * | 04- 15 | garbage - * +--------------------------------------------------------------------------- - * | 16-1023 | samples - * +--------------------------------------------------------------------------- - * signed 14-bit sample - */ -static int msi3101_convert_stream_252(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) -{ - int i, i_max, dst_len = 0; - u32 sample_num[3]; - - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; - - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { - dev_dbg_ratelimited(&s->udev->dev, - "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); - } - - /* - * Dump all unknown 'garbage' data - maybe we will discover - * someday if there is something rational... - */ - dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); - - /* 252 x I+Q samples */ - src += 16; - memcpy(dst, src, 1008); - src += 1008; - dst += 1008; - dst_len += 1008; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { - unsigned long jiffies_now = jiffies; - unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); - unsigned int samples = sample_num[i_max - 1] - s->sample; - s->jiffies_next = jiffies_now; - s->sample = sample_num[i_max - 1]; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, msecs, - samples * 1000UL / msecs); - } - - /* next sample (sample = sample + i * 252) */ - s->next_sample = sample_num[i_max - 1] + 252; - - return dst_len; -} - -static int msi3101_convert_stream_252_u16(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) -{ - int i, j, i_max, dst_len = 0; - u32 sample_num[3]; - u16 *u16dst = (u16 *) dst; - struct {signed int x:14;} se; - - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; - - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { - dev_dbg_ratelimited(&s->udev->dev, - "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); - } - - /* - * Dump all unknown 'garbage' data - maybe we will discover - * someday if there is something rational... - */ - dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); - - /* 252 x I+Q samples */ - src += 16; - - for (j = 0; j < 1008; j += 4) { - unsigned int usample[2]; - int ssample[2]; - - usample[0] = src[j + 0] >> 0 | src[j + 1] << 8; - usample[1] = src[j + 2] >> 0 | src[j + 3] << 8; - - /* sign extension from 14-bit to signed int */ - ssample[0] = se.x = usample[0]; - ssample[1] = se.x = usample[1]; - - /* from signed to unsigned */ - usample[0] = ssample[0] + 8192; - usample[1] = ssample[1] + 8192; - - /* from 14-bit to 16-bit */ - *u16dst++ = (usample[0] << 2) | (usample[0] >> 12); - *u16dst++ = (usample[1] << 2) | (usample[1] >> 12); - } - - src += 1008; - dst += 1008; - dst_len += 1008; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if (unlikely(time_is_before_jiffies(s->jiffies_next))) { -#define MSECS 10000UL - unsigned int samples = sample_num[i_max - 1] - s->sample; - s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); - s->sample = sample_num[i_max - 1]; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, MSECS, - samples * 1000UL / MSECS); - } - - /* next sample (sample = sample + i * 252) */ - s->next_sample = sample_num[i_max - 1] + 252; - - return dst_len; -} - -/* - * This gets called for the Isochronous pipe (stream). This is done in interrupt - * time, so it has to be fast, not crash, and not stall. Neat. - */ -static void msi3101_isoc_handler(struct urb *urb) -{ - struct msi3101_state *s = (struct msi3101_state *)urb->context; - int i, flen, fstatus; - unsigned char *iso_buf = NULL; - struct msi3101_frame_buf *fbuf; - - if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET || - urb->status == -ESHUTDOWN)) { - dev_dbg(&s->udev->dev, "URB (%p) unlinked %ssynchronuously\n", - urb, urb->status == -ENOENT ? "" : "a"); - return; - } - - if (unlikely(urb->status != 0)) { - dev_dbg(&s->udev->dev, - "msi3101_isoc_handler() called with status %d\n", - urb->status); - /* Give up after a number of contiguous errors */ - if (++s->isoc_errors > MAX_ISOC_ERRORS) - dev_dbg(&s->udev->dev, - "Too many ISOC errors, bailing out\n"); - goto handler_end; - } else { - /* Reset ISOC error counter. We did get here, after all. */ - s->isoc_errors = 0; - } - - /* Compact data */ - for (i = 0; i < urb->number_of_packets; i++) { - void *ptr; - - /* Check frame error */ - fstatus = urb->iso_frame_desc[i].status; - if (unlikely(fstatus)) { - dev_dbg_ratelimited(&s->udev->dev, - "frame=%d/%d has error %d skipping\n", - i, urb->number_of_packets, fstatus); - continue; - } - - /* Check if that frame contains data */ - flen = urb->iso_frame_desc[i].actual_length; - if (unlikely(flen == 0)) - continue; - - iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - /* Get free framebuffer */ - fbuf = msi3101_get_next_fill_buf(s); - if (unlikely(fbuf == NULL)) { - s->vb_full++; - dev_dbg_ratelimited(&s->udev->dev, - "videobuf is full, %d packets dropped\n", - s->vb_full); - continue; - } - - /* fill framebuffer */ - ptr = vb2_plane_vaddr(&fbuf->vb, 0); - flen = s->convert_stream(s, ptr, iso_buf, flen); - vb2_set_plane_payload(&fbuf->vb, 0, flen); - vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); - } - -handler_end: - i = usb_submit_urb(urb, GFP_ATOMIC); - if (unlikely(i != 0)) - dev_dbg(&s->udev->dev, - "Error (%d) re-submitting urb in msi3101_isoc_handler\n", - i); -} - -static void msi3101_iso_stop(struct msi3101_state *s) -{ - int i; - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - /* Unlinking ISOC buffers one by one */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (s->urbs[i]) { - dev_dbg(&s->udev->dev, "Unlinking URB %p\n", - s->urbs[i]); - usb_kill_urb(s->urbs[i]); - } - } -} - -static void msi3101_iso_free(struct msi3101_state *s) -{ - int i; - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - /* Freeing ISOC buffers one by one */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (s->urbs[i]) { - dev_dbg(&s->udev->dev, "Freeing URB\n"); - if (s->urbs[i]->transfer_buffer) { - usb_free_coherent(s->udev, - s->urbs[i]->transfer_buffer_length, - s->urbs[i]->transfer_buffer, - s->urbs[i]->transfer_dma); - } - usb_free_urb(s->urbs[i]); - s->urbs[i] = NULL; - } - } -} - -/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ -static void msi3101_isoc_cleanup(struct msi3101_state *s) -{ - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - msi3101_iso_stop(s); - msi3101_iso_free(s); -} - -/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ -static int msi3101_isoc_init(struct msi3101_state *s) -{ - struct usb_device *udev; - struct urb *urb; - int i, j, ret; - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - s->isoc_errors = 0; - udev = s->udev; - - ret = usb_set_interface(s->udev, 0, 1); - if (ret) - return ret; - - /* Allocate and init Isochronuous urbs */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); - if (urb == NULL) { - dev_err(&s->udev->dev, - "Failed to allocate urb %d\n", i); - msi3101_isoc_cleanup(s); - return -ENOMEM; - } - s->urbs[i] = urb; - dev_dbg(&s->udev->dev, "Allocated URB at 0x%p\n", urb); - - urb->interval = 1; - urb->dev = udev; - urb->pipe = usb_rcvisocpipe(udev, 0x81); - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->transfer_buffer = usb_alloc_coherent(udev, ISO_BUFFER_SIZE, - GFP_KERNEL, &urb->transfer_dma); - if (urb->transfer_buffer == NULL) { - dev_err(&s->udev->dev, - "Failed to allocate urb buffer %d\n", - i); - msi3101_isoc_cleanup(s); - return -ENOMEM; - } - urb->transfer_buffer_length = ISO_BUFFER_SIZE; - urb->complete = msi3101_isoc_handler; - urb->context = s; - urb->start_frame = 0; - urb->number_of_packets = ISO_FRAMES_PER_DESC; - for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { - urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; - urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE; - } - } - - /* link */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - ret = usb_submit_urb(s->urbs[i], GFP_KERNEL); - if (ret) { - dev_err(&s->udev->dev, - "isoc_init() submit_urb %d failed with error %d\n", - i, ret); - msi3101_isoc_cleanup(s); - return ret; - } - dev_dbg(&s->udev->dev, "URB 0x%p submitted.\n", s->urbs[i]); - } - - /* All is done... */ - return 0; -} - -/* Must be called with vb_queue_lock hold */ -static void msi3101_cleanup_queued_bufs(struct msi3101_state *s) -{ - unsigned long flags = 0; - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - spin_lock_irqsave(&s->queued_bufs_lock, flags); - while (!list_empty(&s->queued_bufs)) { - struct msi3101_frame_buf *buf; - - buf = list_entry(s->queued_bufs.next, struct msi3101_frame_buf, - list); - list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - } - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); -} - -/* The user yanked out the cable... */ -static void msi3101_disconnect(struct usb_interface *intf) -{ - struct v4l2_device *v = usb_get_intfdata(intf); - struct msi3101_state *s = - container_of(v, struct msi3101_state, v4l2_dev); - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - mutex_lock(&s->vb_queue_lock); - mutex_lock(&s->v4l2_lock); - /* No need to keep the urbs around after disconnection */ - s->udev = NULL; - v4l2_device_disconnect(&s->v4l2_dev); - video_unregister_device(&s->vdev); - spi_unregister_master(s->master); - mutex_unlock(&s->v4l2_lock); - mutex_unlock(&s->vb_queue_lock); - - v4l2_device_put(&s->v4l2_dev); -} - -static int msi3101_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct msi3101_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); - strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); - usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE | V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -/* Videobuf2 operations */ -static int msi3101_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) -{ - struct msi3101_state *s = vb2_get_drv_priv(vq); - dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); - - /* Absolute min and max number of buffers available for mmap() */ - *nbuffers = clamp_t(unsigned int, *nbuffers, 8, 32); - *nplanes = 1; - /* - * 3, wMaxPacketSize 3x 1024 bytes - * 504, max IQ sample pairs per 1024 frame - * 2, two samples, I and Q - * 2, 16-bit is enough for single sample - */ - sizes[0] = PAGE_ALIGN(3 * 504 * 2 * 2); - dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", - __func__, *nbuffers, sizes[0]); - return 0; -} - -static void msi3101_buf_queue(struct vb2_buffer *vb) -{ - struct msi3101_state *s = vb2_get_drv_priv(vb->vb2_queue); - struct msi3101_frame_buf *buf = - container_of(vb, struct msi3101_frame_buf, vb); - unsigned long flags = 0; - - /* Check the device has not disconnected between prep and queuing */ - if (unlikely(!s->udev)) { - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - return; - } - - spin_lock_irqsave(&s->queued_bufs_lock, flags); - list_add_tail(&buf->list, &s->queued_bufs); - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); -} - -#define CMD_WREG 0x41 -#define CMD_START_STREAMING 0x43 -#define CMD_STOP_STREAMING 0x45 -#define CMD_READ_UNKNOW 0x48 - -#define msi3101_dbg_usb_control_msg(udev, r, t, v, _i, b, l) { \ - char *direction; \ - if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ - direction = ">>>"; \ - else \ - direction = "<<<"; \ - dev_dbg(&udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ - "%s %*ph\n", __func__, t, r, v & 0xff, v >> 8, \ - _i & 0xff, _i >> 8, l & 0xff, l >> 8, direction, l, b); \ -} - -static int msi3101_ctrl_msg(struct msi3101_state *s, u8 cmd, u32 data) -{ - int ret; - u8 request = cmd; - u8 requesttype = USB_DIR_OUT | USB_TYPE_VENDOR; - u16 value = (data >> 0) & 0xffff; - u16 index = (data >> 16) & 0xffff; - - msi3101_dbg_usb_control_msg(s->udev, - request, requesttype, value, index, NULL, 0); - - ret = usb_control_msg(s->udev, usb_sndctrlpipe(s->udev, 0), - request, requesttype, value, index, NULL, 0, 2000); - - if (ret) - dev_err(&s->udev->dev, "%s: failed %d, cmd %02x, data %04x\n", - __func__, ret, cmd, data); - - return ret; -}; - -#define F_REF 24000000 -#define DIV_R_IN 2 -static int msi3101_set_usb_adc(struct msi3101_state *s) -{ - int ret, div_n, div_m, div_r_out, f_sr, f_vco, fract; - u32 reg3, reg4, reg7; - struct v4l2_ctrl *bandwidth_auto; - struct v4l2_ctrl *bandwidth; - - f_sr = s->f_adc; - - /* set tuner, subdev, filters according to sampling rate */ - bandwidth_auto = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); - if (v4l2_ctrl_g_ctrl(bandwidth_auto)) { - bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); - v4l2_ctrl_s_ctrl(bandwidth, s->f_adc); - } - - /* select stream format */ - switch (s->pixelformat) { - case V4L2_SDR_FMT_CU8: - s->convert_stream = msi3101_convert_stream_504_u8; - reg7 = 0x000c9407; - break; - case V4L2_SDR_FMT_CU16LE: - s->convert_stream = msi3101_convert_stream_252_u16; - reg7 = 0x00009407; - break; - case V4L2_PIX_FMT_SDR_S8: - s->convert_stream = msi3101_convert_stream_504; - reg7 = 0x000c9407; - break; - case V4L2_PIX_FMT_SDR_MSI2500_384: - s->convert_stream = msi3101_convert_stream_384; - reg7 = 0x0000a507; - break; - case V4L2_PIX_FMT_SDR_S12: - s->convert_stream = msi3101_convert_stream_336; - reg7 = 0x00008507; - break; - case V4L2_PIX_FMT_SDR_S14: - s->convert_stream = msi3101_convert_stream_252; - reg7 = 0x00009407; - break; - default: - s->convert_stream = msi3101_convert_stream_504_u8; - reg7 = 0x000c9407; - break; - } - - /* - * Synthesizer config is just a educated guess... - * - * [7:0] 0x03, register address - * [8] 1, power control - * [9] ?, power control - * [12:10] output divider - * [13] 0 ? - * [14] 0 ? - * [15] fractional MSB, bit 20 - * [16:19] N - * [23:20] ? - * [24:31] 0x01 - * - * output divider - * val div - * 0 - (invalid) - * 1 4 - * 2 6 - * 3 8 - * 4 10 - * 5 12 - * 6 14 - * 7 16 - * - * VCO 202000000 - 720000000++ - */ - reg3 = 0x01000303; - reg4 = 0x00000004; - - /* XXX: Filters? AGC? */ - if (f_sr < 6000000) - reg3 |= 0x1 << 20; - else if (f_sr < 7000000) - reg3 |= 0x5 << 20; - else if (f_sr < 8500000) - reg3 |= 0x9 << 20; - else - reg3 |= 0xd << 20; - - for (div_r_out = 4; div_r_out < 16; div_r_out += 2) { - f_vco = f_sr * div_r_out * 12; - dev_dbg(&s->udev->dev, "%s: div_r_out=%d f_vco=%d\n", - __func__, div_r_out, f_vco); - if (f_vco >= 202000000) - break; - } - - div_n = f_vco / (F_REF * DIV_R_IN); - div_m = f_vco % (F_REF * DIV_R_IN); - fract = 0x200000ul * div_m / (F_REF * DIV_R_IN); - - reg3 |= div_n << 16; - reg3 |= (div_r_out / 2 - 1) << 10; - reg3 |= ((fract >> 20) & 0x000001) << 15; /* [20] */ - reg4 |= ((fract >> 0) & 0x0fffff) << 8; /* [19:0] */ - - dev_dbg(&s->udev->dev, - "%s: f_sr=%d f_vco=%d div_n=%d div_m=%d div_r_out=%d reg3=%08x reg4=%08x\n", - __func__, f_sr, f_vco, div_n, div_m, div_r_out, reg3, reg4); - - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00608008); - if (ret) - goto err; - - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00000c05); - if (ret) - goto err; - - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00020000); - if (ret) - goto err; - - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00480102); - if (ret) - goto err; - - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00f38008); - if (ret) - goto err; - - ret = msi3101_ctrl_msg(s, CMD_WREG, reg7); - if (ret) - goto err; - - ret = msi3101_ctrl_msg(s, CMD_WREG, reg4); - if (ret) - goto err; - - ret = msi3101_ctrl_msg(s, CMD_WREG, reg3); - if (ret) - goto err; -err: - return ret; -}; - -static int msi3101_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct msi3101_state *s = vb2_get_drv_priv(vq); - int ret; - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - if (!s->udev) - return -ENODEV; - - if (mutex_lock_interruptible(&s->v4l2_lock)) - return -ERESTARTSYS; - - /* wake-up tuner */ - v4l2_subdev_call(s->v4l2_subdev, core, s_power, 1); - - ret = msi3101_set_usb_adc(s); - - ret = msi3101_isoc_init(s); - if (ret) - msi3101_cleanup_queued_bufs(s); - - ret = msi3101_ctrl_msg(s, CMD_START_STREAMING, 0); - - mutex_unlock(&s->v4l2_lock); - - return ret; -} - -static void msi3101_stop_streaming(struct vb2_queue *vq) -{ - struct msi3101_state *s = vb2_get_drv_priv(vq); - - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - mutex_lock(&s->v4l2_lock); - - if (s->udev) - msi3101_isoc_cleanup(s); - - msi3101_cleanup_queued_bufs(s); - - /* according to tests, at least 700us delay is required */ - msleep(20); - if (!msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0)) { - /* sleep USB IF / ADC */ - msi3101_ctrl_msg(s, CMD_WREG, 0x01000003); - } - - /* sleep tuner */ - v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0); - - mutex_unlock(&s->v4l2_lock); -} - -static struct vb2_ops msi3101_vb2_ops = { - .queue_setup = msi3101_queue_setup, - .buf_queue = msi3101_buf_queue, - .start_streaming = msi3101_start_streaming, - .stop_streaming = msi3101_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int msi3101_enum_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct msi3101_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index); - - if (f->index >= NUM_FORMATS) - return -EINVAL; - - strlcpy(f->description, formats[f->index].name, sizeof(f->description)); - f->pixelformat = formats[f->index].pixelformat; - - return 0; -} - -static int msi3101_g_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct msi3101_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, - (char *)&s->pixelformat); - - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - f->fmt.sdr.pixelformat = s->pixelformat; - - return 0; -} - -static int msi3101_s_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct msi3101_state *s = video_drvdata(file); - struct vb2_queue *q = &s->vb_queue; - int i; - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, - (char *)&f->fmt.sdr.pixelformat); - - if (vb2_is_busy(q)) - return -EBUSY; - - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { - s->pixelformat = f->fmt.sdr.pixelformat; - return 0; - } - } - - f->fmt.sdr.pixelformat = formats[0].pixelformat; - s->pixelformat = formats[0].pixelformat; - - return 0; -} - -static int msi3101_try_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct msi3101_state *s = video_drvdata(file); - int i; - dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, - (char *)&f->fmt.sdr.pixelformat); - - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].pixelformat == f->fmt.sdr.pixelformat) - return 0; - } - - f->fmt.sdr.pixelformat = formats[0].pixelformat; - - return 0; -} - -static int msi3101_s_tuner(struct file *file, void *priv, - const struct v4l2_tuner *v) -{ - struct msi3101_state *s = video_drvdata(file); - int ret; - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); - - if (v->index == 0) - ret = 0; - else if (v->index == 1) - ret = v4l2_subdev_call(s->v4l2_subdev, tuner, s_tuner, v); - else - ret = -EINVAL; - - return ret; -} - -static int msi3101_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) -{ - struct msi3101_state *s = video_drvdata(file); - int ret; - dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); - - if (v->index == 0) { - strlcpy(v->name, "Mirics MSi2500", sizeof(v->name)); - v->type = V4L2_TUNER_ADC; - v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; - v->rangelow = 1200000; - v->rangehigh = 15000000; - ret = 0; - } else if (v->index == 1) { - ret = v4l2_subdev_call(s->v4l2_subdev, tuner, g_tuner, v); - } else { - ret = -EINVAL; - } - - return ret; -} - -static int msi3101_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct msi3101_state *s = video_drvdata(file); - int ret = 0; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", - __func__, f->tuner, f->type); - - if (f->tuner == 0) { - f->frequency = s->f_adc; - ret = 0; - } else if (f->tuner == 1) { - f->type = V4L2_TUNER_RF; - ret = v4l2_subdev_call(s->v4l2_subdev, tuner, g_frequency, f); - } else { - ret = -EINVAL; - } - - return ret; -} - -static int msi3101_s_frequency(struct file *file, void *priv, - const struct v4l2_frequency *f) -{ - struct msi3101_state *s = video_drvdata(file); - int ret; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", - __func__, f->tuner, f->type, f->frequency); - - if (f->tuner == 0) { - s->f_adc = clamp_t(unsigned int, f->frequency, - bands[0].rangelow, - bands[0].rangehigh); - dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n", - __func__, s->f_adc); - ret = msi3101_set_usb_adc(s); - } else if (f->tuner == 1) { - ret = v4l2_subdev_call(s->v4l2_subdev, tuner, s_frequency, f); - } else { - ret = -EINVAL; - } - - return ret; -} - -static int msi3101_enum_freq_bands(struct file *file, void *priv, - struct v4l2_frequency_band *band) -{ - struct msi3101_state *s = video_drvdata(file); - int ret; - dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", - __func__, band->tuner, band->type, band->index); - - if (band->tuner == 0) { - if (band->index >= ARRAY_SIZE(bands)) { - ret = -EINVAL; - } else { - *band = bands[band->index]; - ret = 0; - } - } else if (band->tuner == 1) { - ret = v4l2_subdev_call(s->v4l2_subdev, tuner, - enum_freq_bands, band); - } else { - ret = -EINVAL; - } - - return ret; -} - -static const struct v4l2_ioctl_ops msi3101_ioctl_ops = { - .vidioc_querycap = msi3101_querycap, - - .vidioc_enum_fmt_sdr_cap = msi3101_enum_fmt_sdr_cap, - .vidioc_g_fmt_sdr_cap = msi3101_g_fmt_sdr_cap, - .vidioc_s_fmt_sdr_cap = msi3101_s_fmt_sdr_cap, - .vidioc_try_fmt_sdr_cap = msi3101_try_fmt_sdr_cap, - - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_g_tuner = msi3101_g_tuner, - .vidioc_s_tuner = msi3101_s_tuner, - - .vidioc_g_frequency = msi3101_g_frequency, - .vidioc_s_frequency = msi3101_s_frequency, - .vidioc_enum_freq_bands = msi3101_enum_freq_bands, - - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - .vidioc_log_status = v4l2_ctrl_log_status, -}; - -static const struct v4l2_file_operations msi3101_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static struct video_device msi3101_template = { - .name = "Mirics MSi3101 SDR Dongle", - .release = video_device_release_empty, - .fops = &msi3101_fops, - .ioctl_ops = &msi3101_ioctl_ops, -}; - -static void msi3101_video_release(struct v4l2_device *v) -{ - struct msi3101_state *s = - container_of(v, struct msi3101_state, v4l2_dev); - - v4l2_ctrl_handler_free(&s->hdl); - v4l2_device_unregister(&s->v4l2_dev); - kfree(s); -} - -static int msi3101_transfer_one_message(struct spi_master *master, - struct spi_message *m) -{ - struct msi3101_state *s = spi_master_get_devdata(master); - struct spi_transfer *t; - int ret = 0; - u32 data; - - list_for_each_entry(t, &m->transfers, transfer_list) { - dev_dbg(&s->udev->dev, "%s: msg=%*ph\n", - __func__, t->len, t->tx_buf); - data = 0x09; /* reg 9 is SPI adapter */ - data |= ((u8 *)t->tx_buf)[0] << 8; - data |= ((u8 *)t->tx_buf)[1] << 16; - data |= ((u8 *)t->tx_buf)[2] << 24; - ret = msi3101_ctrl_msg(s, CMD_WREG, data); - } - - m->status = ret; - spi_finalize_current_message(master); - return ret; -} - -static int msi3101_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct msi3101_state *s = NULL; - struct v4l2_subdev *sd; - struct spi_master *master; - int ret; - static struct spi_board_info board_info = { - .modalias = "msi001", - .bus_num = 0, - .chip_select = 0, - .max_speed_hz = 12000000, - }; - - s = kzalloc(sizeof(struct msi3101_state), GFP_KERNEL); - if (s == NULL) { - pr_err("Could not allocate memory for msi3101_state\n"); - return -ENOMEM; - } - - mutex_init(&s->v4l2_lock); - mutex_init(&s->vb_queue_lock); - spin_lock_init(&s->queued_bufs_lock); - INIT_LIST_HEAD(&s->queued_bufs); - s->udev = udev; - s->f_adc = bands[0].rangelow; - s->pixelformat = V4L2_SDR_FMT_CU8; - - /* Init videobuf2 queue structure */ - s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; - s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - s->vb_queue.drv_priv = s; - s->vb_queue.buf_struct_size = sizeof(struct msi3101_frame_buf); - s->vb_queue.ops = &msi3101_vb2_ops; - s->vb_queue.mem_ops = &vb2_vmalloc_memops; - s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - ret = vb2_queue_init(&s->vb_queue); - if (ret) { - dev_err(&s->udev->dev, "Could not initialize vb2 queue\n"); - goto err_free_mem; - } - - /* Init video_device structure */ - s->vdev = msi3101_template; - s->vdev.queue = &s->vb_queue; - s->vdev.queue->lock = &s->vb_queue_lock; - video_set_drvdata(&s->vdev, s); - - /* Register the v4l2_device structure */ - s->v4l2_dev.release = msi3101_video_release; - ret = v4l2_device_register(&intf->dev, &s->v4l2_dev); - if (ret) { - dev_err(&s->udev->dev, - "Failed to register v4l2-device (%d)\n", ret); - goto err_free_mem; - } - - /* SPI master adapter */ - master = spi_alloc_master(&s->udev->dev, 0); - if (master == NULL) { - ret = -ENOMEM; - goto err_unregister_v4l2_dev; - } - - s->master = master; - master->bus_num = 0; - master->num_chipselect = 1; - master->transfer_one_message = msi3101_transfer_one_message; - spi_master_set_devdata(master, s); - ret = spi_register_master(master); - if (ret) { - spi_master_put(master); - goto err_unregister_v4l2_dev; - } - - /* load v4l2 subdevice */ - sd = v4l2_spi_new_subdev(&s->v4l2_dev, master, &board_info); - s->v4l2_subdev = sd; - if (sd == NULL) { - dev_err(&s->udev->dev, "cannot get v4l2 subdevice\n"); - ret = -ENODEV; - goto err_unregister_master; - } - - /* Register controls */ - v4l2_ctrl_handler_init(&s->hdl, 0); - if (s->hdl.error) { - ret = s->hdl.error; - dev_err(&s->udev->dev, "Could not initialize controls\n"); - goto err_free_controls; - } - - /* currently all controls are from subdev */ - v4l2_ctrl_add_handler(&s->hdl, sd->ctrl_handler, NULL); - - s->v4l2_dev.ctrl_handler = &s->hdl; - s->vdev.v4l2_dev = &s->v4l2_dev; - s->vdev.lock = &s->v4l2_lock; - - ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); - if (ret) { - dev_err(&s->udev->dev, - "Failed to register as video device (%d)\n", - ret); - goto err_unregister_v4l2_dev; - } - dev_info(&s->udev->dev, "Registered as %s\n", - video_device_node_name(&s->vdev)); - - return 0; - -err_free_controls: - v4l2_ctrl_handler_free(&s->hdl); -err_unregister_master: - spi_unregister_master(s->master); -err_unregister_v4l2_dev: - v4l2_device_unregister(&s->v4l2_dev); -err_free_mem: - kfree(s); - return ret; -} - -/* USB device ID list */ -static struct usb_device_id msi3101_id_table[] = { - { USB_DEVICE(0x1df7, 0x2500) }, /* Mirics MSi3101 SDR Dongle */ - { USB_DEVICE(0x2040, 0xd300) }, /* Hauppauge WinTV 133559 LF */ - { } -}; -MODULE_DEVICE_TABLE(usb, msi3101_id_table); - -/* USB subsystem interface */ -static struct usb_driver msi3101_driver = { - .name = KBUILD_MODNAME, - .probe = msi3101_probe, - .disconnect = msi3101_disconnect, - .id_table = msi3101_id_table, -}; - -module_usb_driver(msi3101_driver); - -MODULE_AUTHOR("Antti Palosaari "); -MODULE_DESCRIPTION("Mirics MSi3101 SDR Dongle"); -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 7570589da5348bf1fe85f9cff7d868dda5cce924 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 13 Jul 2014 21:35:47 -0300 Subject: [media] MAINTAINERS: update MSI3101 / MSI2500 driver location MSi3101 driver is moved out of staging and renamed. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index df6aa2d..4384464 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5969,7 +5969,7 @@ T: git git://linuxtv.org/anttip/media_tree.git S: Maintained F: drivers/media/tuners/msi001* -MSI3101 MEDIA DRIVER +MSI2500 MEDIA DRIVER M: Antti Palosaari L: linux-media@vger.kernel.org W: http://linuxtv.org/ @@ -5977,7 +5977,7 @@ W: http://palosaari.fi/linux/ Q: http://patchwork.linuxtv.org/project/linux-media/list/ T: git git://linuxtv.org/anttip/media_tree.git S: Maintained -F: drivers/staging/media/msi3101/sdr-msi3101* +F: drivers/media/usb/msi2500/ MT9M032 APTINA SENSOR DRIVER M: Laurent Pinchart -- cgit v0.10.2 From 3912eb6d1e4a1193af3af2b4ea3ffca0bf401468 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 13 Jul 2014 22:27:21 -0300 Subject: [media] msi2500: change supported formats Enable two native formats, V4L2_SDR_FMT_CS8 and V4L2_SDR_FMT_CS14LE. Move emulated formats behind module parameter. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 53aca38..e4f3f4e 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -34,6 +34,10 @@ #include #include +static bool msi3101_emulated_fmt; +module_param_named(emulated_formats, msi3101_emulated_fmt, bool, 0644); +MODULE_PARM_DESC(emulated_formats, "enable emulated formats (disappears in future)"); + /* * iConfiguration 0 * bInterfaceNumber 0 @@ -52,9 +56,7 @@ #define MAX_ISOC_ERRORS 20 /* TODO: These should be moved to V4L2 API */ -#define V4L2_PIX_FMT_SDR_S8 v4l2_fourcc('D', 'S', '0', '8') /* signed 8-bit */ #define V4L2_PIX_FMT_SDR_S12 v4l2_fourcc('D', 'S', '1', '2') /* signed 12-bit */ -#define V4L2_PIX_FMT_SDR_S14 v4l2_fourcc('D', 'S', '1', '4') /* signed 14-bit */ #define V4L2_PIX_FMT_SDR_MSI2500_384 v4l2_fourcc('M', '3', '8', '4') /* Mirics MSi2500 format 384 */ static const struct v4l2_frequency_band bands[] = { @@ -77,25 +79,25 @@ struct msi3101_format { /* format descriptions for capture and preview */ static struct msi3101_format formats[] = { { - .name = "IQ U8", - .pixelformat = V4L2_SDR_FMT_CU8, - }, { - .name = "IQ U16LE", - .pixelformat = V4L2_SDR_FMT_CU16LE, + .name = "Complex S8", + .pixelformat = V4L2_SDR_FMT_CS8, #if 0 }, { - .name = "8-bit signed", - .pixelformat = V4L2_PIX_FMT_SDR_S8, - }, { .name = "10+2-bit signed", .pixelformat = V4L2_PIX_FMT_SDR_MSI2500_384, }, { .name = "12-bit signed", .pixelformat = V4L2_PIX_FMT_SDR_S12, - }, { - .name = "14-bit signed", - .pixelformat = V4L2_PIX_FMT_SDR_S14, #endif + }, { + .name = "Complex S14LE", + .pixelformat = V4L2_SDR_FMT_CS14LE, + }, { + .name = "Complex U8 (emulated)", + .pixelformat = V4L2_SDR_FMT_CU8, + }, { + .name = "Complex U16LE (emulated)", + .pixelformat = V4L2_SDR_FMT_CU16LE, }, }; @@ -127,6 +129,7 @@ struct msi3101_state { unsigned int f_adc; u32 pixelformat; + unsigned int num_formats; unsigned int isoc_errors; /* number of contiguous ISOC errors */ unsigned int vb_full; /* vb is full and packets dropped */ @@ -928,7 +931,7 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) s->convert_stream = msi3101_convert_stream_252_u16; reg7 = 0x00009407; break; - case V4L2_PIX_FMT_SDR_S8: + case V4L2_SDR_FMT_CS8: s->convert_stream = msi3101_convert_stream_504; reg7 = 0x000c9407; break; @@ -940,7 +943,7 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) s->convert_stream = msi3101_convert_stream_336; reg7 = 0x00008507; break; - case V4L2_PIX_FMT_SDR_S14: + case V4L2_SDR_FMT_CS14LE: s->convert_stream = msi3101_convert_stream_252; reg7 = 0x00009407; break; @@ -1115,7 +1118,7 @@ static int msi3101_enum_fmt_sdr_cap(struct file *file, void *priv, struct msi3101_state *s = video_drvdata(file); dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index); - if (f->index >= NUM_FORMATS) + if (f->index >= s->num_formats) return -EINVAL; strlcpy(f->description, formats[f->index].name, sizeof(f->description)); @@ -1150,7 +1153,7 @@ static int msi3101_s_fmt_sdr_cap(struct file *file, void *priv, return -EBUSY; memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < NUM_FORMATS; i++) { + for (i = 0; i < s->num_formats; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { s->pixelformat = f->fmt.sdr.pixelformat; return 0; @@ -1172,7 +1175,7 @@ static int msi3101_try_fmt_sdr_cap(struct file *file, void *priv, (char *)&f->fmt.sdr.pixelformat); memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < NUM_FORMATS; i++) { + for (i = 0; i < s->num_formats; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) return 0; } @@ -1398,7 +1401,10 @@ static int msi3101_probe(struct usb_interface *intf, INIT_LIST_HEAD(&s->queued_bufs); s->udev = udev; s->f_adc = bands[0].rangelow; - s->pixelformat = V4L2_SDR_FMT_CU8; + s->pixelformat = formats[0].pixelformat; + s->num_formats = NUM_FORMATS; + if (msi3101_emulated_fmt == false) + s->num_formats -= 2; /* Init videobuf2 queue structure */ s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; -- cgit v0.10.2 From c33590778f6af045588f183f24f1954848adb66e Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 14 Jul 2014 13:57:22 -0300 Subject: [media] msi2500: print notice to point SDR API is not 100% stable yet SDR API is very new and surprises may occur. Due to that print notice to remind possible users. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index e4f3f4e..75d4376 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -1486,6 +1486,9 @@ static int msi3101_probe(struct usb_interface *intf, } dev_info(&s->udev->dev, "Registered as %s\n", video_device_node_name(&s->vdev)); + dev_notice(&s->udev->dev, + "%s: SDR API is still slightly experimental and functionality changes may follow\n", + KBUILD_MODNAME); return 0; -- cgit v0.10.2 From a54e0fee61a585956b4e9a97ae2605600e5c1c82 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 Jul 2014 20:56:01 -0300 Subject: [media] msi2500: fill FMT buffer size Fill FMT buffer size field in order to inform app which will be used streaming buffer size. Currently driver doesn't allow buffer size value proposed by application. Cc: Hans Verkuil Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 75d4376..483dc6e 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -74,6 +74,7 @@ static const struct v4l2_frequency_band bands[] = { struct msi3101_format { char *name; u32 pixelformat; + u32 buffersize; }; /* format descriptions for capture and preview */ @@ -81,6 +82,7 @@ static struct msi3101_format formats[] = { { .name = "Complex S8", .pixelformat = V4L2_SDR_FMT_CS8, + .buffersize = 3 * 1008, #if 0 }, { .name = "10+2-bit signed", @@ -92,12 +94,15 @@ static struct msi3101_format formats[] = { }, { .name = "Complex S14LE", .pixelformat = V4L2_SDR_FMT_CS14LE, + .buffersize = 3 * 1008, }, { .name = "Complex U8 (emulated)", .pixelformat = V4L2_SDR_FMT_CU8, + .buffersize = 3 * 1008, }, { .name = "Complex U16LE (emulated)", .pixelformat = V4L2_SDR_FMT_CU16LE, + .buffersize = 3 * 1008, }, }; @@ -129,6 +134,7 @@ struct msi3101_state { unsigned int f_adc; u32 pixelformat; + u32 buffersize; unsigned int num_formats; unsigned int isoc_errors; /* number of contiguous ISOC errors */ @@ -836,13 +842,7 @@ static int msi3101_queue_setup(struct vb2_queue *vq, /* Absolute min and max number of buffers available for mmap() */ *nbuffers = clamp_t(unsigned int, *nbuffers, 8, 32); *nplanes = 1; - /* - * 3, wMaxPacketSize 3x 1024 bytes - * 504, max IQ sample pairs per 1024 frame - * 2, two samples, I and Q - * 2, 16-bit is enough for single sample - */ - sizes[0] = PAGE_ALIGN(3 * 504 * 2 * 2); + sizes[0] = PAGE_ALIGN(s->buffersize); dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", __func__, *nbuffers, sizes[0]); return 0; @@ -1134,8 +1134,9 @@ static int msi3101_g_fmt_sdr_cap(struct file *file, void *priv, dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, (char *)&s->pixelformat); - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); f->fmt.sdr.pixelformat = s->pixelformat; + f->fmt.sdr.buffersize = s->buffersize; + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); return 0; } @@ -1155,13 +1156,17 @@ static int msi3101_s_fmt_sdr_cap(struct file *file, void *priv, memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); for (i = 0; i < s->num_formats; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { - s->pixelformat = f->fmt.sdr.pixelformat; + s->pixelformat = formats[i].pixelformat; + s->buffersize = formats[i].buffersize; + f->fmt.sdr.buffersize = formats[i].buffersize; return 0; } } - f->fmt.sdr.pixelformat = formats[0].pixelformat; s->pixelformat = formats[0].pixelformat; + s->buffersize = formats[0].buffersize; + f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; return 0; } @@ -1176,11 +1181,14 @@ static int msi3101_try_fmt_sdr_cap(struct file *file, void *priv, memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); for (i = 0; i < s->num_formats; i++) { - if (formats[i].pixelformat == f->fmt.sdr.pixelformat) + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + f->fmt.sdr.buffersize = formats[i].buffersize; return 0; + } } f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; return 0; } @@ -1402,6 +1410,7 @@ static int msi3101_probe(struct usb_interface *intf, s->udev = udev; s->f_adc = bands[0].rangelow; s->pixelformat = formats[0].pixelformat; + s->buffersize = formats[0].buffersize; s->num_formats = NUM_FORMATS; if (msi3101_emulated_fmt == false) s->num_formats -= 2; -- cgit v0.10.2 From 8a75ffb81b1c1b6949d191fbef3eaa03fd648852 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jul 2014 06:53:08 -0300 Subject: [media] vb2: fix bytesused == 0 handling The original report from Nikhil was that if data_offset > 0 and bytesused == 0, then the check in __verify_length() would fail, even though the spec says that if bytes_used == 0, then it will be replaced by the actual length of the buffer. After digging into it a bit more I realized that there were several other things wrong: - in __verify_length() it would use the application-provided length value for USERPTR and the vb2 core length for other memory models, but it should have used the application-provided length as well for DMABUF. - in __fill_vb2_buffer() on the other hand it would replace bytesused == 0 by the application-provided length, even for MMAP buffers where the length is determined by the vb2 core. - in __fill_vb2_buffer() it tries to figure out if all the planes have bytesused == 0 before it will decide to replace bytesused by length. However, the spec makes no such provision, and it makes for convoluted code. So just replace any bytesused == 0 by the proper length. The idea behind this was that you could use bytesused to signal empty planes, something that is currently not supported. But that is better done in the future by using one of the reserved fields in strucy v4l2_plane. This patch fixes all these issues. Regards, Hans Signed-off-by: Hans Verkuil Reported-by: Nikhil Devshatwar Cc: Nikhil Devshatwar Acked-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index a5c41cc..f33508f 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -576,6 +576,7 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) { unsigned int length; + unsigned int bytesused; unsigned int plane; if (!V4L2_TYPE_IS_OUTPUT(b->type)) @@ -583,21 +584,24 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { for (plane = 0; plane < vb->num_planes; ++plane) { - length = (b->memory == V4L2_MEMORY_USERPTR) + length = (b->memory == V4L2_MEMORY_USERPTR || + b->memory == V4L2_MEMORY_DMABUF) ? b->m.planes[plane].length : vb->v4l2_planes[plane].length; + bytesused = b->m.planes[plane].bytesused + ? b->m.planes[plane].bytesused : length; if (b->m.planes[plane].bytesused > length) return -EINVAL; if (b->m.planes[plane].data_offset > 0 && - b->m.planes[plane].data_offset >= - b->m.planes[plane].bytesused) + b->m.planes[plane].data_offset >= bytesused) return -EINVAL; } } else { length = (b->memory == V4L2_MEMORY_USERPTR) ? b->length : vb->v4l2_planes[0].length; + bytesused = b->bytesused ? b->bytesused : length; if (b->bytesused > length) return -EINVAL; @@ -1234,35 +1238,6 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b unsigned int plane; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { - /* Fill in driver-provided information for OUTPUT types */ - if (V4L2_TYPE_IS_OUTPUT(b->type)) { - bool bytesused_is_used; - - /* Check if bytesused == 0 for all planes */ - for (plane = 0; plane < vb->num_planes; ++plane) - if (b->m.planes[plane].bytesused) - break; - bytesused_is_used = plane < vb->num_planes; - - /* - * Will have to go up to b->length when API starts - * accepting variable number of planes. - * - * If bytesused_is_used is false, then fall back to the - * full buffer size. In that case userspace clearly - * never bothered to set it and it's a safe assumption - * that they really meant to use the full plane sizes. - */ - for (plane = 0; plane < vb->num_planes; ++plane) { - struct v4l2_plane *pdst = &v4l2_planes[plane]; - struct v4l2_plane *psrc = &b->m.planes[plane]; - - pdst->bytesused = bytesused_is_used ? - psrc->bytesused : psrc->length; - pdst->data_offset = psrc->data_offset; - } - } - if (b->memory == V4L2_MEMORY_USERPTR) { for (plane = 0; plane < vb->num_planes; ++plane) { v4l2_planes[plane].m.userptr = @@ -1279,6 +1254,28 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b b->m.planes[plane].length; } } + + /* Fill in driver-provided information for OUTPUT types */ + if (V4L2_TYPE_IS_OUTPUT(b->type)) { + /* + * Will have to go up to b->length when API starts + * accepting variable number of planes. + * + * If bytesused == 0 for the output buffer, then fall + * back to the full buffer size. In that case + * userspace clearly never bothered to set it and + * it's a safe assumption that they really meant to + * use the full plane sizes. + */ + for (plane = 0; plane < vb->num_planes; ++plane) { + struct v4l2_plane *pdst = &v4l2_planes[plane]; + struct v4l2_plane *psrc = &b->m.planes[plane]; + + pdst->bytesused = psrc->bytesused ? + psrc->bytesused : pdst->length; + pdst->data_offset = psrc->data_offset; + } + } } else { /* * Single-planar buffers do not use planes array, @@ -1286,15 +1283,9 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b * In videobuf we use our internal V4l2_planes struct for * single-planar buffers as well, for simplicity. * - * If bytesused == 0, then fall back to the full buffer size - * as that's a sensible default. + * If bytesused == 0 for the output buffer, then fall back + * to the full buffer size as that's a sensible default. */ - if (V4L2_TYPE_IS_OUTPUT(b->type)) - v4l2_planes[0].bytesused = - b->bytesused ? b->bytesused : b->length; - else - v4l2_planes[0].bytesused = 0; - if (b->memory == V4L2_MEMORY_USERPTR) { v4l2_planes[0].m.userptr = b->m.userptr; v4l2_planes[0].length = b->length; @@ -1304,6 +1295,13 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b v4l2_planes[0].m.fd = b->m.fd; v4l2_planes[0].length = b->length; } + + if (V4L2_TYPE_IS_OUTPUT(b->type)) + v4l2_planes[0].bytesused = b->bytesused ? + b->bytesused : v4l2_planes[0].length; + else + v4l2_planes[0].bytesused = 0; + } /* Zero flags that the vb2 core handles */ -- cgit v0.10.2 From 5cbf17f5054ee964782c377b57bc438756355f57 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jul 2014 19:24:33 -0300 Subject: [media] DocBook media: fix incorrect note about packed RGB and colorspace The fact that the pixelformat is using a packed RGB format has nothing to do with the colorspace that is being used. Those are very different things. The colorspace decides what color a triplet of RGB numbers actually map to. E.g. a red color with values (255, 0, 0) is a different type of red depending on the colorspace. If the original pixelformat was e.g. YUV in colorspace REC709, then after the conversion to RGB the colorspace is still REC709. Unless the hardware actually converted the colorspace as well from REC709 to sRGB, but that rarely if ever happens. Remove this incorrect comment. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml b/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml index 5f1602f..2aae8e9 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml @@ -15,9 +15,6 @@ typical PC graphics frame buffers. They occupy 8, 16, 24 or 32 bits per pixel. These are all packed-pixel formats, meaning all the data for a pixel lie next to each other in memory. - When one of these formats is used, drivers shall report the -colorspace V4L2_COLORSPACE_SRGB. -
Packed RGB Image Formats -- cgit v0.10.2 From 9707cb55c9945ae06bafabc96bd278bede92564c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jul 2014 20:40:20 -0300 Subject: [media] go7007: update the README, fix checkpatch warnings This prepares the driver for moving out of staging. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/go7007/README b/drivers/staging/media/go7007/README index 3af0d90..34516ea 100644 --- a/drivers/staging/media/go7007/README +++ b/drivers/staging/media/go7007/README @@ -1,5 +1,4 @@ Todo: - - create an API for motion detection - let s2250-board use i2c subdevs as well instead of hardcoding support for the i2c devices. - when the driver is moved out of staging, support for saa7134-go7007 diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index c200601..95cffb7 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -9,10 +9,6 @@ * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. */ #include @@ -225,7 +221,7 @@ static int init_i2c_module(struct i2c_adapter *adapter, const struct go_i2c *con return 0; } - printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", i2c->type); + pr_info("go7007: probing for module i2c:%s failed\n", i2c->type); return -EINVAL; } diff --git a/drivers/staging/media/go7007/go7007-fw.c b/drivers/staging/media/go7007/go7007-fw.c index f60640b..5f4c9b9 100644 --- a/drivers/staging/media/go7007/go7007-fw.c +++ b/drivers/staging/media/go7007/go7007-fw.c @@ -9,10 +9,6 @@ * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. */ /* diff --git a/drivers/staging/media/go7007/go7007-i2c.c b/drivers/staging/media/go7007/go7007-i2c.c index 4cf4c0d..55addfa 100644 --- a/drivers/staging/media/go7007/go7007-i2c.c +++ b/drivers/staging/media/go7007/go7007-i2c.c @@ -9,10 +9,6 @@ * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. */ #include diff --git a/drivers/staging/media/go7007/go7007-loader.c b/drivers/staging/media/go7007/go7007-loader.c index 491d0e6..042f78a 100644 --- a/drivers/staging/media/go7007/go7007-loader.c +++ b/drivers/staging/media/go7007/go7007-loader.c @@ -9,10 +9,6 @@ * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. */ #include diff --git a/drivers/staging/media/go7007/go7007-priv.h b/drivers/staging/media/go7007/go7007-priv.h index a8aefed..2251c3f 100644 --- a/drivers/staging/media/go7007/go7007-priv.h +++ b/drivers/staging/media/go7007/go7007-priv.h @@ -9,10 +9,6 @@ * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. */ /* diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index 2f62be9..ece27ec 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -9,10 +9,6 @@ * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 4bf143f..ec799b4 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -9,10 +9,6 @@ * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. */ #include diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index eaa2b09..bb84668 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -9,10 +9,6 @@ * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. */ #include @@ -573,11 +569,12 @@ static int s2250_probe(struct i2c_client *client, if (mutex_lock_interruptible(&usb->i2c_lock) == 0) { data = kzalloc(16, GFP_KERNEL); if (data != NULL) { - int rc; - rc = go7007_usb_vendor_request(go, 0x41, 0, 0, + int rc = go7007_usb_vendor_request(go, 0x41, 0, 0, data, 16, 1); + if (rc > 0) { u8 mask; + data[0] = 0; mask = 1<<5; data[0] &= ~mask; diff --git a/drivers/staging/media/go7007/saa7134-go7007.c b/drivers/staging/media/go7007/saa7134-go7007.c index df5cb1c..b137432 100644 --- a/drivers/staging/media/go7007/saa7134-go7007.c +++ b/drivers/staging/media/go7007/saa7134-go7007.c @@ -9,10 +9,6 @@ * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. */ #include @@ -167,7 +163,7 @@ static int saa7134_go7007_interface_reset(struct go7007 *go) saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); status = saa_readb(SAA7134_GPIO_GPSTATUS2); - /*printk(KERN_DEBUG "status is %s\n", status & 0x40 ? "OK" : "not OK"); */ + /*pr_debug("status is %s\n", status & 0x40 ? "OK" : "not OK"); */ /* enter command mode...(?) */ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); @@ -177,14 +173,13 @@ static int saa7134_go7007_interface_reset(struct go7007 *go) saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); status = saa_readb(SAA7134_GPIO_GPSTATUS2); - /*printk(KERN_INFO "gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */ + /*pr_info("gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */ } while (--count > 0); /* Wait for an interrupt to indicate successful hardware reset */ if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || (intr_val & ~0x1) != 0x55aa) { - printk(KERN_ERR - "saa7134-go7007: unable to reset the GO7007\n"); + pr_err("saa7134-go7007: unable to reset the GO7007\n"); return -1; } return 0; @@ -198,8 +193,7 @@ static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data) u16 status_reg; #ifdef GO7007_HPI_DEBUG - printk(KERN_DEBUG - "saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data); + pr_debug("saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data); #endif for (i = 0; i < 100; ++i) { @@ -209,8 +203,7 @@ static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data) msleep(10); } if (i == 100) { - printk(KERN_ERR - "saa7134-go7007: device is hung, status reg = 0x%04x\n", + pr_err("saa7134-go7007: device is hung, status reg = 0x%04x\n", status_reg); return -1; } @@ -230,7 +223,7 @@ static int saa7134_go7007_read_interrupt(struct go7007 *go) gpio_read(dev, HPI_ADDR_INTR_RET_VALUE, &go->interrupt_value); gpio_read(dev, HPI_ADDR_INTR_RET_DATA, &go->interrupt_data); #ifdef GO7007_HPI_DEBUG - printk(KERN_DEBUG "saa7134-go7007: ReadInterrupt: %04x %04x\n", + pr_debug("saa7134-go7007: ReadInterrupt: %04x %04x\n", go->interrupt_value, go->interrupt_data); #endif return 0; @@ -245,7 +238,7 @@ static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev, if (!vb2_is_streaming(&go->vidq)) return; if (0 != (status & 0x000f0000)) - printk(KERN_DEBUG "saa7134-go7007: irq: lost %ld\n", + pr_debug("saa7134-go7007: irq: lost %ld\n", (status >> 16) & 0x0f); if (status & 0x100000) { dma_sync_single_for_cpu(&dev->pci->dev, @@ -355,8 +348,7 @@ static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len) int i; #ifdef GO7007_HPI_DEBUG - printk(KERN_DEBUG "saa7134-go7007: DownloadBuffer " - "sending %d bytes\n", len); + pr_debug("saa7134-go7007: DownloadBuffer sending %d bytes\n", len); #endif while (len > 0) { @@ -378,8 +370,8 @@ static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len) break; } if (i == 100) { - printk(KERN_ERR "saa7134-go7007: device is hung, " - "status reg = 0x%04x\n", status_reg); + pr_err("saa7134-go7007: device is hung, status reg = 0x%04x\n", + status_reg); return -1; } } @@ -416,6 +408,7 @@ static int saa7134_go7007_s_ctrl(struct v4l2_subdev *sd, { struct saa7134_go7007 *saa = to_state(sd); struct saa7134_dev *dev = saa->dev; + return saa7134_s_ctrl_internal(dev, NULL, ctrl); } @@ -424,6 +417,7 @@ static int saa7134_go7007_g_ctrl(struct v4l2_subdev *sd, { struct saa7134_go7007 *saa = to_state(sd); struct saa7134_dev *dev = saa->dev; + return saa7134_g_ctrl_internal(dev, NULL, ctrl); } @@ -455,7 +449,7 @@ static int saa7134_go7007_init(struct saa7134_dev *dev) struct saa7134_go7007 *saa; struct v4l2_subdev *sd; - printk(KERN_DEBUG "saa7134-go7007: probing new SAA713X board\n"); + pr_debug("saa7134-go7007: probing new SAA713X board\n"); go = go7007_alloc(&board_voyager, &dev->pci->dev); if (go == NULL) @@ -500,7 +494,7 @@ static int saa7134_go7007_init(struct saa7134_dev *dev) /* Register the subdevice interface with the go7007 device */ if (v4l2_device_register_subdev(&go->v4l2_dev, sd) < 0) - printk(KERN_INFO "saa7134-go7007: register subdev failed\n"); + pr_info("saa7134-go7007: register subdev failed\n"); dev->empress_dev = &go->vdev; diff --git a/drivers/staging/media/go7007/snd-go7007.c b/drivers/staging/media/go7007/snd-go7007.c index 9eb2a20..d22d7d5 100644 --- a/drivers/staging/media/go7007/snd-go7007.c +++ b/drivers/staging/media/go7007/snd-go7007.c @@ -9,10 +9,6 @@ * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. */ #include -- cgit v0.10.2 From 1c6f3db04fb470736d759d98a477541162e27fe2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jul 2014 20:40:22 -0300 Subject: [media] solo6x10: a few checkpatch fixes Added a blank line after variable declarations where checkpatch requested that, and removed the 'write to the FSF' paragraph, again as requested. This is in preparation of the move out of staging into drivers/media. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/solo6x10/solo6x10-core.c b/drivers/staging/media/solo6x10/solo6x10-core.c index f670469..172583d 100644 --- a/drivers/staging/media/solo6x10/solo6x10-core.c +++ b/drivers/staging/media/solo6x10/solo6x10-core.c @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include @@ -307,8 +303,8 @@ static ssize_t p2m_timeout_store(struct device *dev, struct solo_dev *solo_dev = container_of(dev, struct solo_dev, dev); unsigned long ms; - int ret = kstrtoul(buf, 10, &ms); + if (ret < 0 || ms > 200) return -EINVAL; solo_dev->p2m_jiffies = msecs_to_jiffies(ms); diff --git a/drivers/staging/media/solo6x10/solo6x10-disp.c b/drivers/staging/media/solo6x10/solo6x10-disp.c index b529a96..ed88ab4 100644 --- a/drivers/staging/media/solo6x10/solo6x10-disp.c +++ b/drivers/staging/media/solo6x10/solo6x10-disp.c @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include diff --git a/drivers/staging/media/solo6x10/solo6x10-eeprom.c b/drivers/staging/media/solo6x10/solo6x10-eeprom.c index 9d1c9bb..af40b3a 100644 --- a/drivers/staging/media/solo6x10/solo6x10-eeprom.c +++ b/drivers/staging/media/solo6x10/solo6x10-eeprom.c @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include diff --git a/drivers/staging/media/solo6x10/solo6x10-enc.c b/drivers/staging/media/solo6x10/solo6x10-enc.c index 2db53b6..d19c0ae 100644 --- a/drivers/staging/media/solo6x10/solo6x10-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-enc.c @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include diff --git a/drivers/staging/media/solo6x10/solo6x10-g723.c b/drivers/staging/media/solo6x10/solo6x10-g723.c index 74f037b..c7141f2 100644 --- a/drivers/staging/media/solo6x10/solo6x10-g723.c +++ b/drivers/staging/media/solo6x10/solo6x10-g723.c @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include diff --git a/drivers/staging/media/solo6x10/solo6x10-gpio.c b/drivers/staging/media/solo6x10/solo6x10-gpio.c index 73276dc..6d3b4a3 100644 --- a/drivers/staging/media/solo6x10/solo6x10-gpio.c +++ b/drivers/staging/media/solo6x10/solo6x10-gpio.c @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include diff --git a/drivers/staging/media/solo6x10/solo6x10-i2c.c b/drivers/staging/media/solo6x10/solo6x10-i2c.c index 01aa417..c908672 100644 --- a/drivers/staging/media/solo6x10/solo6x10-i2c.c +++ b/drivers/staging/media/solo6x10/solo6x10-i2c.c @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c diff --git a/drivers/staging/media/solo6x10/solo6x10-jpeg.h b/drivers/staging/media/solo6x10/solo6x10-jpeg.h index 9e41185..1c66a46 100644 --- a/drivers/staging/media/solo6x10/solo6x10-jpeg.h +++ b/drivers/staging/media/solo6x10/solo6x10-jpeg.h @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __SOLO6X10_JPEG_H diff --git a/drivers/staging/media/solo6x10/solo6x10-offsets.h b/drivers/staging/media/solo6x10/solo6x10-offsets.h index 13eeb44..d6aea7c 100644 --- a/drivers/staging/media/solo6x10/solo6x10-offsets.h +++ b/drivers/staging/media/solo6x10/solo6x10-offsets.h @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __SOLO6X10_OFFSETS_H diff --git a/drivers/staging/media/solo6x10/solo6x10-p2m.c b/drivers/staging/media/solo6x10/solo6x10-p2m.c index 7f2f247..8c84846 100644 --- a/drivers/staging/media/solo6x10/solo6x10-p2m.c +++ b/drivers/staging/media/solo6x10/solo6x10-p2m.c @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include diff --git a/drivers/staging/media/solo6x10/solo6x10-regs.h b/drivers/staging/media/solo6x10/solo6x10-regs.h index 428f6c9..e34ac56 100644 --- a/drivers/staging/media/solo6x10/solo6x10-regs.h +++ b/drivers/staging/media/solo6x10/solo6x10-regs.h @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __SOLO6X10_REGISTERS_H diff --git a/drivers/staging/media/solo6x10/solo6x10-tw28.c b/drivers/staging/media/solo6x10/solo6x10-tw28.c index 36daa17..edd0781 100644 --- a/drivers/staging/media/solo6x10/solo6x10-tw28.c +++ b/drivers/staging/media/solo6x10/solo6x10-tw28.c @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include @@ -214,6 +210,7 @@ static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off, for (i = 0; i < 5; i++) { u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off); + if (rval == val) return; diff --git a/drivers/staging/media/solo6x10/solo6x10-tw28.h b/drivers/staging/media/solo6x10/solo6x10-tw28.h index 1a02c87..0966b45 100644 --- a/drivers/staging/media/solo6x10/solo6x10-tw28.h +++ b/drivers/staging/media/solo6x10/solo6x10-tw28.h @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __SOLO6X10_TW28_H diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index 1fd6bec..2e07b49 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include @@ -704,6 +700,7 @@ static int solo_ring_thread(void *data) for (;;) { long timeout = schedule_timeout_interruptible(HZ); + if (timeout == -ERESTARTSYS || kthread_should_stop()) break; solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); @@ -750,6 +747,7 @@ static int solo_ring_start(struct solo_dev *solo_dev) SOLO6X10_NAME "_ring"); if (IS_ERR(solo_dev->ring_thread)) { int err = PTR_ERR(solo_dev->ring_thread); + solo_dev->ring_thread = NULL; return err; } @@ -1402,6 +1400,7 @@ int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr) if (i != solo_dev->nr_chans) { int ret = PTR_ERR(solo_dev->v4l2_enc[i]); + while (i--) solo_enc_free(solo_dev->v4l2_enc[i]); pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2.c b/drivers/staging/media/solo6x10/solo6x10-v4l2.c index ba2526c..63ae8a6 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2.c @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include @@ -98,6 +94,7 @@ static int solo_v4l2_ch_ext_4up(struct solo_dev *solo_dev, u8 idx, int on) if (!on) { u8 i; + for (i = ch; i < ch + 4; i++) solo_win_setup(solo_dev, i, solo_dev->video_hsize, solo_vlines(solo_dev), @@ -206,6 +203,7 @@ static void solo_fillbuf(struct solo_dev *solo_dev, if (erase_off(solo_dev)) { void *p = vb2_plane_vaddr(vb, 0); int image_size = solo_image_size(solo_dev); + for (i = 0; i < image_size; i += 2) { ((u8 *)p)[i] = 0x80; ((u8 *)p)[i + 1] = 0x00; @@ -275,6 +273,7 @@ static int solo_thread(void *data) for (;;) { long timeout = schedule_timeout_interruptible(HZ); + if (timeout == -ERESTARTSYS || kthread_should_stop()) break; solo_thread_try(solo_dev); @@ -414,6 +413,7 @@ static int solo_enum_input(struct file *file, void *priv, if (input->index >= solo_dev->nr_chans) { int ret = solo_enum_ext_input(solo_dev, input); + if (ret < 0) return ret; } else { diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h index 35f9486..c6154b0 100644 --- a/drivers/staging/media/solo6x10/solo6x10.h +++ b/drivers/staging/media/solo6x10/solo6x10.h @@ -16,10 +16,6 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __SOLO6X10_H -- cgit v0.10.2 From 273886b4f71a1a960a3625833df2f037efffab45 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 18 Jul 2014 02:58:41 -0300 Subject: [media] videodev2.h: add defines for the VBI field start lines While working with raw and sliced VBI support in several applications I noticed that you really need to know the start linenumbers for each video field in order to correctly convert the start line numbers reported by v4l2_vbi_format to the line numbers used in v4l2_sliced_vbi_format. This patch adds four defines that specify the start lines for each field for both 525 and 625 line standards. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 8fc4822..5fd4202 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1642,6 +1642,12 @@ struct v4l2_vbi_format { #define V4L2_VBI_UNSYNC (1 << 0) #define V4L2_VBI_INTERLACED (1 << 1) +/* ITU-R start lines for each field */ +#define V4L2_VBI_ITU_525_F1_START (1) +#define V4L2_VBI_ITU_525_F2_START (264) +#define V4L2_VBI_ITU_625_F1_START (1) +#define V4L2_VBI_ITU_625_F2_START (314) + /* Sliced VBI * * This implements is a proposal V4L2 API to allow SLICED VBI -- cgit v0.10.2 From 12c78e665f96b3f73dc0b6a6190a4871fe526293 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 18 Jul 2014 02:58:42 -0300 Subject: [media] DocBook media: document new VBI defines Add defines for the start line numbers of each field for both 525 and 625 line formats. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/dev-raw-vbi.xml b/Documentation/DocBook/media/v4l/dev-raw-vbi.xml index b788c72..f4b61b6 100644 --- a/Documentation/DocBook/media/v4l/dev-raw-vbi.xml +++ b/Documentation/DocBook/media/v4l/dev-raw-vbi.xml @@ -150,9 +150,15 @@ signal. Drivers shall not convert the sample format by software. This is the scanning system line number associated with the first line of the VBI image, of the first and the second field respectively. See and - for valid values. VBI input drivers can -return start values 0 if the hardware cannot reliable identify -scanning lines, VBI acquisition may not require this + for valid values. +The V4L2_VBI_ITU_525_F1_START, +V4L2_VBI_ITU_525_F2_START, +V4L2_VBI_ITU_625_F1_START and +V4L2_VBI_ITU_625_F2_START defines give the start line +numbers for each field for each 525 or 625 line format as a convenience. +Don't forget that ITU line numbering starts at 1, not 0. +VBI input drivers can return start values 0 if the hardware cannot +reliable identify scanning lines, VBI acquisition may not require this information. diff --git a/Documentation/DocBook/media/v4l/dev-sliced-vbi.xml b/Documentation/DocBook/media/v4l/dev-sliced-vbi.xml index 548f8ea..7a8bf30 100644 --- a/Documentation/DocBook/media/v4l/dev-sliced-vbi.xml +++ b/Documentation/DocBook/media/v4l/dev-sliced-vbi.xml @@ -185,7 +185,14 @@ tables, sigh. --> Drivers must set service_lines[0][0] and -service_lines[1][0] to zero. +service_lines[1][0] to zero. +The V4L2_VBI_ITU_525_F1_START, +V4L2_VBI_ITU_525_F2_START, +V4L2_VBI_ITU_625_F1_START and +V4L2_VBI_ITU_625_F2_START defines give the start +line numbers for each field for each 525 or 625 line format as a +convenience. Don't forget that ITU line numbering starts at 1, not 0. + __u32 -- cgit v0.10.2 From 958c7c7e65999c61af7da0812d2de12daa8fc29e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 18 Jul 2014 05:15:21 -0300 Subject: [media] v4l2-ctrls: fix corner case in round-to-range code If you have a maximum that is at the limit of what the type supports, and the step is > 1, then you can get wrap-around errors since the code assumes that the maximum that the type supports is ctrl->maximum + ctrl->step / 2. In practice this is always fine, but in artificially crafted ranges you will hit this bug. Since this is core code it should just work. This bug has always been there but since it doesn't cause problems in practice it was never noticed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 1acc7aa..004e7e8 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1295,11 +1295,19 @@ static void std_log(const struct v4l2_ctrl *ctrl) } } -/* Round towards the closest legal value */ +/* + * Round towards the closest legal value. Be careful when we are + * close to the maximum range of the control type to prevent + * wrap-arounds. + */ #define ROUND_TO_RANGE(val, offset_type, ctrl) \ ({ \ offset_type offset; \ - val += (ctrl)->step / 2; \ + if ((ctrl)->maximum >= 0 && \ + val >= (ctrl)->maximum - ((ctrl)->step / 2)) \ + val = (ctrl)->maximum; \ + else \ + val += (ctrl)->step / 2; \ val = clamp_t(typeof(val), val, \ (ctrl)->minimum, (ctrl)->maximum); \ offset = (val) - (ctrl)->minimum; \ @@ -1325,7 +1333,10 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, * the u64 divide that needs special care. */ val = ptr.p_s64[idx]; - val += ctrl->step / 2; + if (ctrl->maximum >= 0 && val >= ctrl->maximum - ctrl->step / 2) + val = ctrl->maximum; + else + val += ctrl->step / 2; val = clamp_t(s64, val, ctrl->minimum, ctrl->maximum); offset = val - ctrl->minimum; do_div(offset, ctrl->step); -- cgit v0.10.2 From 10f8dfdcb8f25cf62679940e3826a56a99b1cc1e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 20 Jul 2014 17:16:41 -0300 Subject: [media] DocBook media typo V4L2_CID_BASE_LASTP1 should be V4L2_CID_LASTP1. This has probably been wrong since the earliest days of this documentation until I did a copy-and-paste and found out that V4L2_CID_BASE_LASTP1 doesn't actually exist :-) Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml index 62163d9..2bd98fd 100644 --- a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml +++ b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml @@ -76,7 +76,7 @@ structure. The driver fills the rest of the structure or returns an VIDIOC_QUERYCTRL with successive id values starting from V4L2_CID_BASE up to and exclusive -V4L2_CID_BASE_LASTP1. Drivers may return +V4L2_CID_LASTP1. Drivers may return EINVAL if a control in this range is not supported. Further applications can enumerate private controls, which are not defined in this specification, by starting at -- cgit v0.10.2 From 796a2bd25b4fc3c4a1d6f04373ef2af1e12489b5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 04:14:46 -0300 Subject: [media] v4l2-ioctl: set V4L2_CAP_EXT_PIX_FORMAT for device_caps V4L2_CAP_EXT_PIX_FORMAT is set for capabilities, but it needs to be set for device_caps as well: device_caps should report all caps relevant to the device node, and this is one of them. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index e620387..00ceedf 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1014,6 +1014,7 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops, ret = ops->vidioc_querycap(file, fh, cap); cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT; + cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; return ret; } -- cgit v0.10.2 From 48f2650a87ae462598cf0a3b4b34ee5f52c34869 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 07:50:39 -0300 Subject: [media] v4l2-ioctl: don't set PRIV_MAGIC unconditionally in g_fmt() Regression fix: V4L2_PIX_FMT_PRIV_MAGIC should only be set for the VIDEO_CAPTURE and VIDEO_OUTPUT buffer types, and not for any others. In the case of the win format this overwrote a pointer value that is passed in from userspace. Just set it for V4L2_BUF_TYPE_VIDEO_CAPTURE and OUTPUT only. Set it before the callback is called, just as is done for try/s_fmt, and again afterwards in case the driver zeroed it. The latter was missing in try/s_fmt, so add it there as well. Currently it is quite likely that drivers clear priv (that was needed for a long time), so it makes sense to set it twice. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 00ceedf..d15e1673 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1144,8 +1144,6 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, bool is_tx = vfd->vfl_dir != VFL_DIR_RX; int ret; - p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; - /* * fmt can't be cleared for these overlay types due to the 'clips' * 'clipcount' and 'bitmap' pointers in struct v4l2_window. @@ -1174,7 +1172,9 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap)) break; + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg); + /* just in case the driver zeroed it again */ p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -1196,7 +1196,9 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out)) break; + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; ret = ops->vidioc_g_fmt_vid_out(file, fh, arg); + /* just in case the driver zeroed it again */ p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; return ret; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -1232,6 +1234,7 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + int ret; v4l_sanitize_format(p); @@ -1240,7 +1243,10 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap)) break; CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_s_fmt_vid_cap(file, fh, arg); + ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg); + /* just in case the driver zeroed it again */ + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap_mplane)) break; @@ -1265,7 +1271,10 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out)) break; CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_s_fmt_vid_out(file, fh, arg); + ret = ops->vidioc_s_fmt_vid_out(file, fh, arg); + /* just in case the driver zeroed it again */ + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_mplane)) break; @@ -1304,6 +1313,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + int ret; v4l_sanitize_format(p); @@ -1312,7 +1322,10 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap)) break; CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_try_fmt_vid_cap(file, fh, arg); + ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg); + /* just in case the driver zeroed it again */ + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap_mplane)) break; @@ -1337,7 +1350,10 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out)) break; CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_try_fmt_vid_out(file, fh, arg); + ret = ops->vidioc_try_fmt_vid_out(file, fh, arg); + /* just in case the driver zeroed it again */ + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_mplane)) break; -- cgit v0.10.2 From e9b5872ce5959298233ca43d03860eef85f4634f Mon Sep 17 00:00:00 2001 From: Prabhakar Lad Date: Fri, 18 Jul 2014 13:31:51 -0300 Subject: [media] media: davinci: vpif: fix array out of bound warnings This patch fixes following array out of bound warnings, drivers/media/platform/davinci/vpif_display.c: In function 'vpif_remove': drivers/media/platform/davinci/vpif_display.c:1389:36: warning: iteration 1u invokes undefined behavior [-Waggressive-loop-optimizations] vb2_dma_contig_cleanup_ctx(common->alloc_ctx); ^ drivers/media/platform/davinci/vpif_display.c:1385:2: note: containing loop for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { ^ drivers/media/platform/davinci/vpif_capture.c: In function 'vpif_remove': drivers/media/platform/davinci/vpif_capture.c:1581:36: warning: iteration 1u invokes undefined behavior [-Waggressive-loop-optimizations] vb2_dma_contig_cleanup_ctx(common->alloc_ctx); ^ drivers/media/platform/davinci/vpif_capture.c:1577:2: note: containing loop for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { ^ drivers/media/platform/davinci/vpif_capture.c:1580:23: warning: array subscript is above array bounds [-Warray-bounds] common = &ch->common[i]; Reported-by: Hans Verkuil Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 2f90f0d..3a85238 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1577,7 +1577,7 @@ static int vpif_remove(struct platform_device *device) for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; - common = &ch->common[i]; + common = &ch->common[VPIF_VIDEO_INDEX]; vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ video_unregister_device(ch->video_dev); diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 0bd6dcb..6c6bd6b 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1385,7 +1385,7 @@ static int vpif_remove(struct platform_device *device) for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; - common = &ch->common[i]; + common = &ch->common[VPIF_VIDEO_INDEX]; vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ video_unregister_device(ch->video_dev); -- cgit v0.10.2 From f17bc3f4707eb87bdb80b895911c551cdd606fbd Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 26 May 2014 10:55:51 -0300 Subject: [media] mt9v032: fix hblank calculation Since (min_row_time - crop->width) can be negative, we have to do a signed comparison here. Otherwise max_t casts the negative value to unsigned int and sets min_hblank to that invalid value. Cc: stable@vger.kernel.org Signed-off-by: Philipp Zabel Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 435e9e6..d481ed1 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -305,8 +305,8 @@ mt9v032_update_hblank(struct mt9v032 *mt9v032) if (mt9v032->version->version == MT9V034_CHIP_ID_REV1) min_hblank += (mt9v032->hratio - 1) * 10; - min_hblank = max_t(unsigned int, (int)mt9v032->model->data->min_row_time - crop->width, - (int)min_hblank); + min_hblank = max_t(int, mt9v032->model->data->min_row_time - crop->width, + min_hblank); hblank = max_t(unsigned int, mt9v032->hblank, min_hblank); return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, hblank); -- cgit v0.10.2 From d131e54b686049f90a34f195a7d276083ef68aa0 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 3 Jun 2014 06:35:53 -0300 Subject: [media] mt9v032: do not clear reserved bits in read mode register The read mode register bits 8 and 9 are set and marked as reserved. Don't clear them. Signed-off-by: Philipp Zabel Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index d481ed1..edbe80c 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -87,6 +87,7 @@ #define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) #define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) #define MT9V032_READ_MODE_DARK_ROWS (1 << 7) +#define MT9V032_READ_MODE_RESERVED 0x0300 #define MT9V032_PIXEL_OPERATION_MODE 0x0f #define MT9V034_PIXEL_OPERATION_MODE_HDR (1 << 0) #define MT9V034_PIXEL_OPERATION_MODE_COLOR (1 << 1) @@ -414,6 +415,7 @@ static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) struct i2c_client *client = v4l2_get_subdevdata(subdev); struct mt9v032 *mt9v032 = to_mt9v032(subdev); struct v4l2_rect *crop = &mt9v032->crop; + unsigned int read_mode; unsigned int hbin; unsigned int vbin; int ret; @@ -424,9 +426,13 @@ static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) /* Configure the window size and row/column bin */ hbin = fls(mt9v032->hratio) - 1; vbin = fls(mt9v032->vratio) - 1; - ret = mt9v032_write(client, MT9V032_READ_MODE, - hbin << MT9V032_READ_MODE_COLUMN_BIN_SHIFT | - vbin << MT9V032_READ_MODE_ROW_BIN_SHIFT); + read_mode = mt9v032_read(client, MT9V032_READ_MODE); + if (read_mode < 0) + return read_mode; + read_mode &= MT9V032_READ_MODE_RESERVED; + read_mode |= hbin << MT9V032_READ_MODE_COLUMN_BIN_SHIFT | + vbin << MT9V032_READ_MODE_ROW_BIN_SHIFT; + ret = mt9v032_write(client, MT9V032_READ_MODE, read_mode); if (ret < 0) return ret; -- cgit v0.10.2 From d8dde6c8dd3916f532b5fd3b1fc377e01ea41e01 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 3 Jun 2014 06:35:54 -0300 Subject: [media] mt9v032: add support for mt9v022 and mt9v024 as are mt9v024 and mt9v034. With minimal changes it is possible to support mt9v02[24] with the same driver. Signed-off-by: Philipp Zabel Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index edbe80c..86c3833 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -1,5 +1,5 @@ /* - * Driver for MT9V032 CMOS Image Sensor from Micron + * Driver for MT9V022, MT9V024, MT9V032, and MT9V034 CMOS Image Sensors * * Copyright (C) 2010, Laurent Pinchart * @@ -134,8 +134,12 @@ #define MT9V032_THERMAL_INFO 0xc1 enum mt9v032_model { - MT9V032_MODEL_V032_COLOR, - MT9V032_MODEL_V032_MONO, + MT9V032_MODEL_V022_COLOR, /* MT9V022IX7ATC */ + MT9V032_MODEL_V022_MONO, /* MT9V022IX7ATM */ + MT9V032_MODEL_V024_COLOR, /* MT9V024IA7XTC */ + MT9V032_MODEL_V024_MONO, /* MT9V024IA7XTM */ + MT9V032_MODEL_V032_COLOR, /* MT9V032C12STM */ + MT9V032_MODEL_V032_MONO, /* MT9V032C12STC */ MT9V032_MODEL_V034_COLOR, MT9V032_MODEL_V034_MONO, }; @@ -161,14 +165,14 @@ struct mt9v032_model_info { }; static const struct mt9v032_model_version mt9v032_versions[] = { - { MT9V032_CHIP_ID_REV1, "MT9V032 rev1/2" }, - { MT9V032_CHIP_ID_REV3, "MT9V032 rev3" }, - { MT9V034_CHIP_ID_REV1, "MT9V034 rev1" }, + { MT9V032_CHIP_ID_REV1, "MT9V022/MT9V032 rev1/2" }, + { MT9V032_CHIP_ID_REV3, "MT9V022/MT9V032 rev3" }, + { MT9V034_CHIP_ID_REV1, "MT9V024/MT9V034 rev1" }, }; static const struct mt9v032_model_data mt9v032_model_data[] = { { - /* MT9V032 revisions 1/2/3 */ + /* MT9V022, MT9V032 revisions 1/2/3 */ .min_row_time = 660, .min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN, .min_vblank = MT9V032_VERTICAL_BLANKING_MIN, @@ -177,7 +181,7 @@ static const struct mt9v032_model_data mt9v032_model_data[] = { .max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX, .pclk_reg = MT9V032_PIXEL_CLOCK, }, { - /* MT9V034 */ + /* MT9V024, MT9V034 */ .min_row_time = 690, .min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN, .min_vblank = MT9V034_VERTICAL_BLANKING_MIN, @@ -189,6 +193,22 @@ static const struct mt9v032_model_data mt9v032_model_data[] = { }; static const struct mt9v032_model_info mt9v032_models[] = { + [MT9V032_MODEL_V022_COLOR] = { + .data = &mt9v032_model_data[0], + .color = true, + }, + [MT9V032_MODEL_V022_MONO] = { + .data = &mt9v032_model_data[0], + .color = false, + }, + [MT9V032_MODEL_V024_COLOR] = { + .data = &mt9v032_model_data[1], + .color = true, + }, + [MT9V032_MODEL_V024_MONO] = { + .data = &mt9v032_model_data[1], + .color = false, + }, [MT9V032_MODEL_V032_COLOR] = { .data = &mt9v032_model_data[0], .color = true, @@ -1010,6 +1030,10 @@ static int mt9v032_remove(struct i2c_client *client) } static const struct i2c_device_id mt9v032_id[] = { + { "mt9v022", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_COLOR] }, + { "mt9v022m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_MONO] }, + { "mt9v024", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V024_COLOR] }, + { "mt9v024m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V024_MONO] }, { "mt9v032", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_COLOR] }, { "mt9v032m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_MONO] }, { "mt9v034", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_COLOR] }, -- cgit v0.10.2 From 94b76ce83de2fb4dd12001bbaef4301dc681700a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 4 Jun 2014 13:57:02 -0300 Subject: [media] mt9v032: register v4l2 asynchronous subdevice Add support for registering the sensor subdevice using the v4l2-async API. Signed-off-by: Philipp Zabel Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 86c3833..c8a4088 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -1010,10 +1010,19 @@ static int mt9v032_probe(struct i2c_client *client, mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); + if (ret < 0) + goto err; + mt9v032->subdev.dev = &client->dev; + ret = v4l2_async_register_subdev(&mt9v032->subdev); if (ret < 0) - v4l2_ctrl_handler_free(&mt9v032->ctrls); + goto err; + + return 0; +err: + media_entity_cleanup(&mt9v032->subdev.entity); + v4l2_ctrl_handler_free(&mt9v032->ctrls); return ret; } @@ -1022,6 +1031,7 @@ static int mt9v032_remove(struct i2c_client *client) struct v4l2_subdev *subdev = i2c_get_clientdata(client); struct mt9v032 *mt9v032 = to_mt9v032(subdev); + v4l2_async_unregister_subdev(subdev); v4l2_ctrl_handler_free(&mt9v032->ctrls); v4l2_device_unregister_subdev(subdev); media_entity_cleanup(&subdev->entity); -- cgit v0.10.2 From 80b44ef23a746af5a05562d314184b5923574bb6 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 4 Jun 2014 13:57:03 -0300 Subject: [media] mt9v032: use regmap This switches all register accesses to use regmap. It allows to use the regmap cache, tracing, and debug register dump facilities, and removes the need to open code read-modify-writes. Signed-off-by: Philipp Zabel Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 441053b..f40b4cf 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -551,6 +551,7 @@ config VIDEO_MT9V032 tristate "Micron MT9V032 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT + select REGMAP_I2C ---help--- This is a Video4Linux2 sensor-level driver for the Micron MT9V032 752x480 CMOS sensor. diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index c8a4088..d044bce 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -245,6 +246,7 @@ struct mt9v032 { struct mutex power_lock; int power_count; + struct regmap *regmap; struct clk *clk; struct mt9v032_platform_data *pdata; @@ -252,7 +254,6 @@ struct mt9v032 { const struct mt9v032_model_version *version; u32 sysclk; - u16 chip_control; u16 aec_agc; u16 hblank; struct { @@ -266,40 +267,10 @@ static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) return container_of(sd, struct mt9v032, subdev); } -static int mt9v032_read(struct i2c_client *client, const u8 reg) -{ - s32 data = i2c_smbus_read_word_swapped(client, reg); - dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__, - data, reg); - return data; -} - -static int mt9v032_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__, - data, reg); - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); - u16 value = (mt9v032->chip_control & ~clear) | set; - int ret; - - ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value); - if (ret < 0) - return ret; - - mt9v032->chip_control = value; - return 0; -} - static int mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) { - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + struct regmap *map = mt9v032->regmap; u16 value = mt9v032->aec_agc; int ret; @@ -308,7 +279,7 @@ mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) else value &= ~which; - ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value); + ret = regmap_write(map, MT9V032_AEC_AGC_ENABLE, value); if (ret < 0) return ret; @@ -319,7 +290,6 @@ mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) static int mt9v032_update_hblank(struct mt9v032 *mt9v032) { - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); struct v4l2_rect *crop = &mt9v032->crop; unsigned int min_hblank = mt9v032->model->data->min_hblank; unsigned int hblank; @@ -330,12 +300,13 @@ mt9v032_update_hblank(struct mt9v032 *mt9v032) min_hblank); hblank = max_t(unsigned int, mt9v032->hblank, min_hblank); - return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, hblank); + return regmap_write(mt9v032->regmap, MT9V032_HORIZONTAL_BLANKING, + hblank); } static int mt9v032_power_on(struct mt9v032 *mt9v032) { - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + struct regmap *map = mt9v032->regmap; int ret; ret = clk_set_rate(mt9v032->clk, mt9v032->sysclk); @@ -349,15 +320,15 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032) udelay(1); /* Reset the chip and stop data read out */ - ret = mt9v032_write(client, MT9V032_RESET, 1); + ret = regmap_write(map, MT9V032_RESET, 1); if (ret < 0) return ret; - ret = mt9v032_write(client, MT9V032_RESET, 0); + ret = regmap_write(map, MT9V032_RESET, 0); if (ret < 0) return ret; - return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0); + return regmap_write(map, MT9V032_CHIP_CONTROL, 0); } static void mt9v032_power_off(struct mt9v032 *mt9v032) @@ -367,7 +338,7 @@ static void mt9v032_power_off(struct mt9v032 *mt9v032) static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) { - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + struct regmap *map = mt9v032->regmap; int ret; if (!on) { @@ -381,14 +352,14 @@ static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) /* Configure the pixel clock polarity */ if (mt9v032->pdata && mt9v032->pdata->clk_pol) { - ret = mt9v032_write(client, mt9v032->model->data->pclk_reg, + ret = regmap_write(map, mt9v032->model->data->pclk_reg, MT9V032_PIXEL_CLOCK_INV_PXL_CLK); if (ret < 0) return ret; } /* Disable the noise correction algorithm and restore the controls. */ - ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0); + ret = regmap_write(map, MT9V032_ROW_NOISE_CORR_CONTROL, 0); if (ret < 0) return ret; @@ -432,43 +403,39 @@ static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE | MT9V032_CHIP_CONTROL_DOUT_ENABLE | MT9V032_CHIP_CONTROL_SEQUENTIAL; - struct i2c_client *client = v4l2_get_subdevdata(subdev); struct mt9v032 *mt9v032 = to_mt9v032(subdev); struct v4l2_rect *crop = &mt9v032->crop; - unsigned int read_mode; + struct regmap *map = mt9v032->regmap; unsigned int hbin; unsigned int vbin; int ret; if (!enable) - return mt9v032_set_chip_control(mt9v032, mode, 0); + return regmap_update_bits(map, MT9V032_CHIP_CONTROL, mode, 0); /* Configure the window size and row/column bin */ hbin = fls(mt9v032->hratio) - 1; vbin = fls(mt9v032->vratio) - 1; - read_mode = mt9v032_read(client, MT9V032_READ_MODE); - if (read_mode < 0) - return read_mode; - read_mode &= MT9V032_READ_MODE_RESERVED; - read_mode |= hbin << MT9V032_READ_MODE_COLUMN_BIN_SHIFT | - vbin << MT9V032_READ_MODE_ROW_BIN_SHIFT; - ret = mt9v032_write(client, MT9V032_READ_MODE, read_mode); + ret = regmap_update_bits(map, MT9V032_READ_MODE, + ~MT9V032_READ_MODE_RESERVED, + hbin << MT9V032_READ_MODE_COLUMN_BIN_SHIFT | + vbin << MT9V032_READ_MODE_ROW_BIN_SHIFT); if (ret < 0) return ret; - ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left); + ret = regmap_write(map, MT9V032_COLUMN_START, crop->left); if (ret < 0) return ret; - ret = mt9v032_write(client, MT9V032_ROW_START, crop->top); + ret = regmap_write(map, MT9V032_ROW_START, crop->top); if (ret < 0) return ret; - ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width); + ret = regmap_write(map, MT9V032_WINDOW_WIDTH, crop->width); if (ret < 0) return ret; - ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height); + ret = regmap_write(map, MT9V032_WINDOW_HEIGHT, crop->height); if (ret < 0) return ret; @@ -477,7 +444,7 @@ static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) return ret; /* Switch to master "normal" mode */ - return mt9v032_set_chip_control(mt9v032, 0, mode); + return regmap_update_bits(map, MT9V032_CHIP_CONTROL, mode, mode); } static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, @@ -659,7 +626,7 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) { struct mt9v032 *mt9v032 = container_of(ctrl->handler, struct mt9v032, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); + struct regmap *map = mt9v032->regmap; u32 freq; u16 data; @@ -669,23 +636,23 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) ctrl->val); case V4L2_CID_GAIN: - return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val); + return regmap_write(map, MT9V032_ANALOG_GAIN, ctrl->val); case V4L2_CID_EXPOSURE_AUTO: return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, !ctrl->val); case V4L2_CID_EXPOSURE: - return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, - ctrl->val); + return regmap_write(map, MT9V032_TOTAL_SHUTTER_WIDTH, + ctrl->val); case V4L2_CID_HBLANK: mt9v032->hblank = ctrl->val; return mt9v032_update_hblank(mt9v032); case V4L2_CID_VBLANK: - return mt9v032_write(client, MT9V032_VERTICAL_BLANKING, - ctrl->val); + return regmap_write(map, MT9V032_VERTICAL_BLANKING, + ctrl->val); case V4L2_CID_PIXEL_RATE: case V4L2_CID_LINK_FREQ: @@ -722,7 +689,7 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) | MT9V032_TEST_PATTERN_FLIP; break; } - return mt9v032_write(client, MT9V032_TEST_PATTERN, data); + return regmap_write(map, MT9V032_TEST_PATTERN, data); } return 0; @@ -790,7 +757,7 @@ static int mt9v032_registered(struct v4l2_subdev *subdev) struct i2c_client *client = v4l2_get_subdevdata(subdev); struct mt9v032 *mt9v032 = to_mt9v032(subdev); unsigned int i; - s32 version; + u32 version; int ret; dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", @@ -803,10 +770,10 @@ static int mt9v032_registered(struct v4l2_subdev *subdev) } /* Read and check the sensor version */ - version = mt9v032_read(client, MT9V032_CHIP_VERSION); - if (version < 0) { + ret = regmap_read(mt9v032->regmap, MT9V032_CHIP_VERSION, &version); + if (ret < 0) { dev_err(&client->dev, "Failed reading chip version\n"); - return version; + return ret; } for (i = 0; i < ARRAY_SIZE(mt9v032_versions); ++i) { @@ -893,6 +860,13 @@ static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { .close = mt9v032_close, }; +static const struct regmap_config mt9v032_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xff, + .cache_type = REGCACHE_RBTREE, +}; + /* ----------------------------------------------------------------------------- * Driver initialization and probing */ @@ -916,6 +890,10 @@ static int mt9v032_probe(struct i2c_client *client, if (!mt9v032) return -ENOMEM; + mt9v032->regmap = devm_regmap_init_i2c(client, &mt9v032_regmap_config); + if (IS_ERR(mt9v032->regmap)) + return PTR_ERR(mt9v032->regmap); + mt9v032->clk = devm_clk_get(&client->dev, NULL); if (IS_ERR(mt9v032->clk)) return PTR_ERR(mt9v032->clk); -- cgit v0.10.2 From 3b12c89d0d21cec5853d641d8c5afd875bcecc83 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 29 Mar 2014 01:56:13 -0300 Subject: [media] staging: media: davinci_vpfe: fix checkpatch warning This patch fixes following checkpatch warning, media/davinci_vpfe/dm365_ipipe.c:1271: WARNING: Missing a blank line after declarations media/davinci_vpfe/dm365_ipipe.c:1313: WARNING: Missing a blank line after declarations Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c index b7044a3..bdc7f00 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -1268,6 +1268,7 @@ static int ipipe_s_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg) for (i = 0; i < ARRAY_SIZE(ipipe_modules); i++) { unsigned int bit = 1 << i; + if (cfg->flag & bit) { const struct ipipe_module_if *module_if = &ipipe_modules[i]; @@ -1310,6 +1311,7 @@ static int ipipe_g_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg) for (i = 1; i < ARRAY_SIZE(ipipe_modules); i++) { unsigned int bit = 1 << i; + if (cfg->flag & bit) { const struct ipipe_module_if *module_if = &ipipe_modules[i]; -- cgit v0.10.2 From 509578f9b654f19efd6fd0c12b7c3519fdc4e1a0 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 29 Apr 2014 03:49:34 -0300 Subject: [media] media: davinci_vpfe: dm365_resizer: fix sparse warning this patch fixes following sparse warning, dm365_resizer.c:223:1: warning: symbol 'resizer_calculate_resize_ratios' was not declared. Should it be static? dm365_resizer.c:313:5: warning: symbol 'resizer_configure_output_win' was not declared. Should it be static? Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c index 8e13bd4..8828d6c 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c @@ -219,7 +219,7 @@ configure_resizer_out_params(struct vpfe_resizer_device *resizer, int index, * @resizer: Pointer to VPFE resizer subdevice. * @index: index RSZ_A-resizer-A RSZ_B-resizer-B. */ -void +static void resizer_calculate_resize_ratios(struct vpfe_resizer_device *resizer, int index) { struct resizer_params *param = &resizer->config; @@ -310,7 +310,7 @@ resizer_calculate_sdram_offsets(struct vpfe_resizer_device *resizer, int index) return 0; } -int resizer_configure_output_win(struct vpfe_resizer_device *resizer) +static int resizer_configure_output_win(struct vpfe_resizer_device *resizer) { struct resizer_params *param = &resizer->config; struct vpfe_rsz_output_spec output_specs; -- cgit v0.10.2 From 7e0ffd1f4046cf94c0feaffcc5644ff90aadc984 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 9 Jun 2014 12:10:46 -0300 Subject: [media] davinci: vpfe: dm365: remove duplicate RSZ_LPF_INT_MASK The RSZ_LPF_INT_MASK define is cut and pasted twice so we can remove the second instance. Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h index 010fdb2..81176fb 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h @@ -479,7 +479,6 @@ #define RSZ_TYP_Y_SHIFT 0 #define RSZ_TYP_C_SHIFT 1 #define RSZ_LPF_INT_MASK 0x3f -#define RSZ_LPF_INT_MASK 0x3f #define RSZ_LPF_INT_C_SHIFT 6 #define RSZ_H_PHS_MASK 0x3fff #define RSZ_H_DIF_MASK 0x3fff -- cgit v0.10.2 From fb3e0bc6330e69ce5ca60b2303b53d7588204105 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 19 May 2014 12:04:32 -0300 Subject: [media] v4l: noon010p30: Return V4L2_FIELD_NONE from pad-level set format The sensor is progressive, always return the field order set to V4L2_FIELD_NONE. Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c index 271d0b7..7eae487 100644 --- a/drivers/media/i2c/noon010pc30.c +++ b/drivers/media/i2c/noon010pc30.c @@ -554,6 +554,7 @@ static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, nf = noon010_try_fmt(sd, &fmt->format); noon010_try_frame_size(&fmt->format, &size); fmt->format.colorspace = V4L2_COLORSPACE_JPEG; + fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { if (fh) { -- cgit v0.10.2 From 776a347df61780111574ee8875e5f7d756817de8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 19 May 2014 12:04:32 -0300 Subject: [media] v4l: s5k4ecgx: Return V4L2_FIELD_NONE from pad-level set format The sensor is progressive, always return the field order set to V4L2_FIELD_NONE. Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c index 2750de6..1fcc76f 100644 --- a/drivers/media/i2c/s5k4ecgx.c +++ b/drivers/media/i2c/s5k4ecgx.c @@ -594,6 +594,7 @@ static int s5k4ecgx_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, pf = s5k4ecgx_try_fmt(sd, &fmt->format); s5k4ecgx_try_frame_size(&fmt->format, &fsize); fmt->format.colorspace = V4L2_COLORSPACE_JPEG; + fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { if (fh) { -- cgit v0.10.2 From 2f7844ecb753b352ef8db56b443618a45e478f28 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 19 May 2014 12:04:32 -0300 Subject: [media] v4l: s5k5baf: Return V4L2_FIELD_NONE from pad-level set format The sensor is progressive, always return the field order set to V4L2_FIELD_NONE. Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 2d768ef..564f05f 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -1313,6 +1313,8 @@ static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, const struct s5k5baf_pixfmt *pixfmt; int ret = 0; + mf->field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { *v4l2_subdev_get_try_format(fh, fmt->pad) = *mf; return 0; -- cgit v0.10.2 From d53ce5acf8d02f2d55702c17a875c8fb351cc66b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 19 May 2014 12:04:32 -0300 Subject: [media] v4l: s5k6a3: Return V4L2_FIELD_NONE from pad-level set format The sensor is progressive, always return the field order set to V4L2_FIELD_NONE. Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index 7bc2271..c11a408 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -115,6 +115,7 @@ static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf) fmt = find_sensor_format(mf); mf->code = fmt->code; + mf->field = V4L2_FIELD_NONE; v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH, S5K6A3_SENSOR_MAX_WIDTH, 0, &mf->height, S5K6A3_SENSOR_MIN_HEIGHT, -- cgit v0.10.2 From 7ed0b291b497c241cd567ff9d9b3b9cbf714466e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 19 May 2014 12:04:32 -0300 Subject: [media] v4l: smiapp: Return V4L2_FIELD_NONE from pad-level get/set format The SMIA++ sensors are progressive, always return the field order set to V4L2_FIELD_NONE. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 13411e9..1eaf975 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1554,6 +1554,7 @@ static int __smiapp_get_format(struct v4l2_subdev *subdev, fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); fmt->format.width = r->width; fmt->format.height = r->height; + fmt->format.field = V4L2_FIELD_NONE; } return 0; @@ -1687,6 +1688,7 @@ static int smiapp_set_format(struct v4l2_subdev *subdev, fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); fmt->format.width &= ~1; fmt->format.height &= ~1; + fmt->format.field = V4L2_FIELD_NONE; fmt->format.width = clamp(fmt->format.width, @@ -2674,6 +2676,7 @@ static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; try_fmt->code = mbus_code; + try_fmt->field = V4L2_FIELD_NONE; try_crop->top = 0; try_crop->left = 0; -- cgit v0.10.2 From 6c5bba9b0ae140e76782fcd4d3dd83fa6d6db417 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 19 May 2014 12:04:32 -0300 Subject: [media] v4l: s3c-camif: Return V4L2_FIELD_NONE from pad-level set format The bridge driver doesn't support interlaced formats, always return the field order set to V4L2_FIELD_NONE. Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 8ea5209..f336413 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -1270,6 +1270,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, } mutex_unlock(&camif->lock); + mf->field = V4L2_FIELD_NONE; mf->colorspace = V4L2_COLORSPACE_JPEG; return 0; } @@ -1318,6 +1319,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %ux%u\n", fmt->pad, mf->code, mf->width, mf->height); + mf->field = V4L2_FIELD_NONE; mf->colorspace = V4L2_COLORSPACE_JPEG; mutex_lock(&camif->lock); -- cgit v0.10.2 From b225e398f622c5f5579fb4fd14f8bcb0f953e650 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 4 Jun 2014 08:22:03 -0300 Subject: [media] v4l: subdev: Unify argument validation across IOCTLs Separate validation of different argument types. There's no reason to do this separately for every IOCTL. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index b984f33d..b92cf69 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -126,6 +126,55 @@ static int subdev_close(struct file *file) return 0; } +static int check_format(struct v4l2_subdev *sd, + struct v4l2_subdev_format *format) +{ + if (format->which != V4L2_SUBDEV_FORMAT_TRY && + format->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (format->pad >= sd->entity.num_pads) + return -EINVAL; + + return 0; +} + +static int check_crop(struct v4l2_subdev *sd, struct v4l2_subdev_crop *crop) +{ + if (crop->which != V4L2_SUBDEV_FORMAT_TRY && + crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (crop->pad >= sd->entity.num_pads) + return -EINVAL; + + return 0; +} + +static int check_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_selection *sel) +{ + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return 0; +} + +static int check_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) +{ + if (edid->pad >= sd->entity.num_pads) + return -EINVAL; + + if (edid->blocks && edid->edid == NULL) + return -EINVAL; + + return 0; +} + static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); @@ -134,6 +183,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); #endif + int rval; switch (cmd) { case VIDIOC_QUERYCTRL: @@ -206,12 +256,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_G_FMT: { struct v4l2_subdev_format *format = arg; - if (format->which != V4L2_SUBDEV_FORMAT_TRY && - format->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (format->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_format(sd, format); + if (rval) + return rval; return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format); } @@ -219,12 +266,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_FMT: { struct v4l2_subdev_format *format = arg; - if (format->which != V4L2_SUBDEV_FORMAT_TRY && - format->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (format->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_format(sd, format); + if (rval) + return rval; return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format); } @@ -232,14 +276,10 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_G_CROP: { struct v4l2_subdev_crop *crop = arg; struct v4l2_subdev_selection sel; - int rval; - - if (crop->which != V4L2_SUBDEV_FORMAT_TRY && - crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - if (crop->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_crop(sd, crop); + if (rval) + return rval; rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); if (rval != -ENOIOCTLCMD) @@ -261,14 +301,10 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_CROP: { struct v4l2_subdev_crop *crop = arg; struct v4l2_subdev_selection sel; - int rval; - - if (crop->which != V4L2_SUBDEV_FORMAT_TRY && - crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - if (crop->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_crop(sd, crop); + if (rval) + return rval; rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); if (rval != -ENOIOCTLCMD) @@ -339,12 +375,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_G_SELECTION: { struct v4l2_subdev_selection *sel = arg; - if (sel->which != V4L2_SUBDEV_FORMAT_TRY && - sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (sel->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_selection(sd, sel); + if (rval) + return rval; return v4l2_subdev_call( sd, pad, get_selection, subdev_fh, sel); @@ -353,12 +386,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_SELECTION: { struct v4l2_subdev_selection *sel = arg; - if (sel->which != V4L2_SUBDEV_FORMAT_TRY && - sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (sel->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_selection(sd, sel); + if (rval) + return rval; return v4l2_subdev_call( sd, pad, set_selection, subdev_fh, sel); @@ -367,10 +397,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_G_EDID: { struct v4l2_subdev_edid *edid = arg; - if (edid->pad >= sd->entity.num_pads) - return -EINVAL; - if (edid->blocks && edid->edid == NULL) - return -EINVAL; + rval = check_edid(sd, edid); + if (rval) + return rval; return v4l2_subdev_call(sd, pad, get_edid, edid); } @@ -378,10 +407,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_S_EDID: { struct v4l2_subdev_edid *edid = arg; - if (edid->pad >= sd->entity.num_pads) - return -EINVAL; - if (edid->blocks && edid->edid == NULL) - return -EINVAL; + rval = check_edid(sd, edid); + if (rval) + return rval; return v4l2_subdev_call(sd, pad, set_edid, edid); } -- cgit v0.10.2 From f8ca6ac00d2ba24c5557f08f81439cd3432f0802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Salva=20Peir=C3=B3?= Date: Sat, 7 Jun 2014 11:41:44 -0300 Subject: [media] media-device: Remove duplicated memset() in media_enum_entities() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the zeroing the whole struct struct media_entity_desc u_ent, it is no longer necessary to memset(0) its u_ent.name field. CC: stable@vger.kernel.org Signed-off-by: Salva Peiró Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 88b97c9..73a4329 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -106,8 +106,6 @@ static long media_device_enum_entities(struct media_device *mdev, if (ent->name) { strncpy(u_ent.name, ent->name, sizeof(u_ent.name)); u_ent.name[sizeof(u_ent.name) - 1] = '\0'; - } else { - memset(u_ent.name, 0, sizeof(u_ent.name)); } u_ent.type = ent->type; u_ent.revision = ent->revision; -- cgit v0.10.2 From 9aabd95a2d531308ad997d2b92f46a3635782e0c Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Fri, 4 Jul 2014 04:51:47 -0300 Subject: [media] media:platform: OMAP3 camera support needs VIDEOBUF2_DMA_CONTIG drivers/built-in.o: In function `isp_video_open': /src/linux/drivers/media/platform/omap3isp/ispvideo.c:1253: undefined reference to `vb2_dma_contig_memops' drivers/built-in.o: In function `omap3isp_video_init': /src/linux/drivers/media/platform/omap3isp/ispvideo.c:1344: undefined reference to `vb2_dma_contig_init_ctx' /src/linux/drivers/media/platform/omap3isp/ispvideo.c:1350: undefined reference to `vb2_dma_contig_cleanup_ctx' drivers/built-in.o: In function `omap3isp_video_cleanup': /src/linux/drivers/media/platform/omap3isp/ispvideo.c:1381: undefined reference to `vb2_dma_contig_cleanup_ctx' make: *** [vmlinux] Error 1 Signed-off-by: Peter Meerwald Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 8108c69..49e8c0a 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -96,6 +96,7 @@ config VIDEO_OMAP3 depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 select ARM_DMA_USE_IOMMU select OMAP_IOMMU + select VIDEOBUF2_DMA_CONTIG ---help--- Driver for an OMAP 3 camera controller. -- cgit v0.10.2 From a98ccfcf4804beb2651b9f44a4bc5cbb387019ec Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 22 Jul 2014 00:18:19 -0300 Subject: [media] rtl2832_sdr: remove plain 64-bit divisions Commit 0ba2aeb6dab80920edd9cf5b93b1ea4d6913b8f3 (v4l2-ctrls: increase internal min/max/step/def to 64 bit) changes v4l2 controls to 64-bit. Driver it not working on 32-bit arch as it uses directly control 'step' which is changed to 64-bit. Reported-by: kbuild test robot Cc: Hans Verkuil Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index f58bd74..023e0f4 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -1364,17 +1364,16 @@ static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) /* TODO: these controls should be moved to tuner drivers */ if (s->bandwidth_auto->val) { /* Round towards the closest legal value */ - s32 val = s->f_adc + s->bandwidth->step / 2; + s32 val = s->f_adc + div_u64(s->bandwidth->step, 2); u32 offset; val = clamp_t(s32, val, s->bandwidth->minimum, s->bandwidth->maximum); offset = val - s->bandwidth->minimum; offset = s->bandwidth->step * - (offset / s->bandwidth->step); + div_u64(offset, s->bandwidth->step); s->bandwidth->val = s->bandwidth->minimum + offset; } - c->bandwidth_hz = s->bandwidth->val; if (!test_bit(POWER_ON, &s->flags)) -- cgit v0.10.2 From 64a43323e429d2b183fc02451b3201cce873807d Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 22 Jul 2014 01:28:00 -0300 Subject: [media] rtl2832_sdr: fix Kconfig dependencies MEDIA_SDR_SUPPORT and I2C_MUX are needed for rtl2832_sdr. Reported-by: kbuild test robot Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 7225f05..78a95a6 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -448,7 +448,7 @@ config DVB_RTL2832 config DVB_RTL2832_SDR tristate "Realtek RTL2832 SDR" - depends on DVB_CORE && I2C && VIDEO_V4L2 + depends on DVB_CORE && I2C && I2C_MUX && VIDEO_V4L2 && MEDIA_SDR_SUPPORT select DVB_RTL2832 select VIDEOBUF2_VMALLOC default m if !MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 0ea144e..66645b0 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -129,7 +129,7 @@ config DVB_USB_RTL28XXU depends on DVB_USB_V2 && I2C_MUX select DVB_RTL2830 select DVB_RTL2832 - select DVB_RTL2832_SDR if MEDIA_SUBDRV_AUTOSELECT + select DVB_RTL2832_SDR if (MEDIA_SUBDRV_AUTOSELECT && MEDIA_SDR_SUPPORT) select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT -- cgit v0.10.2 From d352bcc99067d8e5caf2c8794d0dd9e63f835e1a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jul 2014 01:40:00 -0300 Subject: [media] v4l2-subdev: Fix compilation when !VIDEO_V4L2_SUBDEV_API As reported by Kbuildtest: drivers/media/v4l2-core/v4l2-subdev.c: In function 'check_format': drivers/media/v4l2-core/v4l2-subdev.c:136:23: error: 'struct v4l2_subdev' has no member named 'entity' if (format->pad >= sd->entity.num_pads) ^ drivers/media/v4l2-core/v4l2-subdev.c: In function 'check_crop': drivers/media/v4l2-core/v4l2-subdev.c:148:21: error: 'struct v4l2_subdev' has no member named 'entity' if (crop->pad >= sd->entity.num_pads) ^ drivers/media/v4l2-core/v4l2-subdev.c: In function 'check_selection': drivers/media/v4l2-core/v4l2-subdev.c:161:20: error: 'struct v4l2_subdev' has no member named 'entity' if (sel->pad >= sd->entity.num_pads) ^ drivers/media/v4l2-core/v4l2-subdev.c: In function 'check_edid': drivers/media/v4l2-core/v4l2-subdev.c:169:21: error: 'struct v4l2_subdev' has no member named 'entity' if (edid->pad >= sd->entity.num_pads) ^ drivers/media/v4l2-core/v4l2-subdev.c: In function 'subdev_do_ioctl': >> drivers/media/v4l2-core/v4l2-subdev.c:186:6: warning: unused variable 'rval' [-Wunused-variable] int rval; ^ drivers/media/v4l2-core/v4l2-subdev.c: At top level: drivers/media/v4l2-core/v4l2-subdev.c:129:12: warning: 'check_format' defined but not used [-Wunused-function] static int check_format(struct v4l2_subdev *sd, ^ drivers/media/v4l2-core/v4l2-subdev.c:142:12: warning: 'check_crop' defined but not used [-Wunused-function] static int check_crop(struct v4l2_subdev *sd, struct v4l2_subdev_crop *crop) ^ drivers/media/v4l2-core/v4l2-subdev.c:154:12: warning: 'check_selection' defined but not used [-Wunused-function] static int check_selection(struct v4l2_subdev *sd, ^ drivers/media/v4l2-core/v4l2-subdev.c:167:12: warning: 'check_edid' defined but not used [-Wunused-function] static int check_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) The above warnins happen because those functions are used only when the V4L2 subdev API is enabled. Reported-by: kbuild test robot Cc: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index b92cf69..b4d235c 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -126,6 +126,7 @@ static int subdev_close(struct file *file) return 0; } +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static int check_format(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { @@ -174,6 +175,7 @@ static int check_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) return 0; } +#endif static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) { @@ -182,8 +184,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_fh *vfh = file->private_data; #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); -#endif int rval; +#endif switch (cmd) { case VIDIOC_QUERYCTRL: -- cgit v0.10.2 From 4d1ab185db4131695e844916a7479576179dee31 Mon Sep 17 00:00:00 2001 From: CrazyCat Date: Tue, 3 Jun 2014 14:19:07 -0300 Subject: [media] cxd2820r: TS clock inversion in config TS clock inversion in config. Signed-off-by: Evgeny Plehov Reviewed-by: Antti Palosaari Acked-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/cxd2820r.h b/drivers/media/dvb-frontends/cxd2820r.h index 82b3d93..6095dbc 100644 --- a/drivers/media/dvb-frontends/cxd2820r.h +++ b/drivers/media/dvb-frontends/cxd2820r.h @@ -52,6 +52,12 @@ struct cxd2820r_config { */ u8 ts_mode; + /* TS clock inverted. + * Default: 0 + * Values: 0, 1 + */ + bool ts_clock_inv; + /* IF AGC polarity. * Default: 0 * Values: 0, 1 diff --git a/drivers/media/dvb-frontends/cxd2820r_c.c b/drivers/media/dvb-frontends/cxd2820r_c.c index 5c6ab49..0f4657e 100644 --- a/drivers/media/dvb-frontends/cxd2820r_c.c +++ b/drivers/media/dvb-frontends/cxd2820r_c.c @@ -45,6 +45,7 @@ int cxd2820r_set_frontend_c(struct dvb_frontend *fe) { 0x1008b, 0x07, 0xff }, { 0x1001f, priv->cfg.if_agc_polarity << 7, 0x80 }, { 0x10070, priv->cfg.ts_mode, 0xff }, + { 0x10071, !priv->cfg.ts_clock_inv << 4, 0x10 }, }; dev_dbg(&priv->i2c->dev, "%s: frequency=%d symbol_rate=%d\n", __func__, diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c index fa184ca..9b5a45b 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t.c +++ b/drivers/media/dvb-frontends/cxd2820r_t.c @@ -46,6 +46,7 @@ int cxd2820r_set_frontend_t(struct dvb_frontend *fe) { 0x00088, 0x01, 0xff }, { 0x00070, priv->cfg.ts_mode, 0xff }, + { 0x00071, !priv->cfg.ts_clock_inv << 4, 0x10 }, { 0x000cb, priv->cfg.if_agc_polarity << 6, 0x40 }, { 0x000a5, 0x00, 0x01 }, { 0x00082, 0x20, 0x60 }, diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c index 2ba130e..9c0c4f4 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t2.c +++ b/drivers/media/dvb-frontends/cxd2820r_t2.c @@ -47,6 +47,7 @@ int cxd2820r_set_frontend_t2(struct dvb_frontend *fe) { 0x02083, 0x0a, 0xff }, { 0x020cb, priv->cfg.if_agc_polarity << 6, 0x40 }, { 0x02070, priv->cfg.ts_mode, 0xff }, + { 0x02071, !priv->cfg.ts_clock_inv << 6, 0x40 }, { 0x020b5, priv->cfg.spec_inv << 4, 0x10 }, { 0x02567, 0x07, 0x0f }, { 0x02569, 0x03, 0x03 }, -- cgit v0.10.2 From 7b71ef7eac86a8cb8d41fc0d0f37b5f1917a9ea4 Mon Sep 17 00:00:00 2001 From: CrazyCat Date: Tue, 3 Jun 2014 14:22:44 -0300 Subject: [media] dw2102: Geniatech T220 init fixed Geniatech T220 init fixed - reset cmd from windows driver and fixed TS bus config for cxd2820r. Signed-off-by: Evgeny Plehov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index ae0f56a..7135a3e 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -1109,6 +1109,7 @@ static struct ds3000_config su3000_ds3000_config = { static struct cxd2820r_config cxd2820r_config = { .i2c_address = 0x6c, /* (0xd8 >> 1) */ .ts_mode = 0x38, + .ts_clock_inv = 1, }; static struct tda18271_config tda18271_config = { @@ -1387,20 +1388,27 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) static int t220_frontend_attach(struct dvb_usb_adapter *d) { - u8 obuf[3] = { 0xe, 0x80, 0 }; + u8 obuf[3] = { 0xe, 0x87, 0 }; u8 ibuf[] = { 0 }; if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) err("command 0x0e transfer failed."); obuf[0] = 0xe; - obuf[1] = 0x83; + obuf[1] = 0x86; + obuf[2] = 1; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + + obuf[0] = 0xe; + obuf[1] = 0x80; obuf[2] = 0; if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) err("command 0x0e transfer failed."); - msleep(100); + msleep(50); obuf[0] = 0xe; obuf[1] = 0x80; -- cgit v0.10.2 From 3c4b422adb7694418848cefc2a4669d63192c649 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Wed, 16 Apr 2014 12:47:43 -0300 Subject: [media] sms: Remove CONFIG_ prefix from Kconfig symbols X-Patchwork-Delegate: mchehab@redhat.com Remove the CONFIG_ prefix from two Kconfig symbols in a dependency for SMS_SIANO_DEBUGFS. This prefix is invalid inside Kconfig files. Note that the current (common sense) dependency on SMS_USB_DRV and SMS_SDIO_DRV being equal ensures that SMS_SIANO_DEBUGFS will not violate its constraints. These constraint are that: - it should only be built if SMS_USB_DRV is set; - it can't be builtin if USB support is modular. So drop the dependency on SMS_USB_DRV, as it is unneeded. Fixes: 6c84b214284e ("[media] sms: fix randconfig building error") Reported-by: Martin Walch Signed-off-by: Paul Bolle Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/siano/Kconfig b/drivers/media/common/siano/Kconfig index f953d33..4bfbd5f 100644 --- a/drivers/media/common/siano/Kconfig +++ b/drivers/media/common/siano/Kconfig @@ -22,8 +22,7 @@ config SMS_SIANO_DEBUGFS bool "Enable debugfs for smsdvb" depends on SMS_SIANO_MDTV depends on DEBUG_FS - depends on SMS_USB_DRV - depends on CONFIG_SMS_USB_DRV = CONFIG_SMS_SDIO_DRV + depends on SMS_USB_DRV = SMS_SDIO_DRV ---help--- Choose Y to enable visualizing a dump of the frontend -- cgit v0.10.2 From 385dded8066aec9cb78cfbcc842532dff7946704 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 26 May 2014 16:28:31 -0300 Subject: [media] tvp5150: Fix device ID kernel log message X-Patchwork-Delegate: mchehab@redhat.com The driver mistakenly prints the ROM version instead of the device ID to the kernel log when detecting the chip. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index a912125..937e48b 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1148,10 +1148,10 @@ static int tvp5150_probe(struct i2c_client *c, /* Is TVP5150A */ if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) { v4l2_info(sd, "tvp%02x%02xa detected.\n", - tvp5150_id[2], tvp5150_id[3]); + tvp5150_id[0], tvp5150_id[1]); } else { v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", - tvp5150_id[2], tvp5150_id[3]); + tvp5150_id[0], tvp5150_id[1]); v4l2_info(sd, "*** Rom ver is %d.%d\n", tvp5150_id[2], tvp5150_id[3]); } -- cgit v0.10.2 From e35ce2e4b2c7657e9e23b618c861c258ce4a57bf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 26 May 2014 16:31:28 -0300 Subject: [media] tvp5150: Use i2c_smbus_(read|write)_byte_data X-Patchwork-Delegate: mchehab@redhat.com Replace the custom I2C read/write implementation with SMBUS functions to simplify the driver. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 937e48b..193e7d6 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -56,38 +56,29 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) { struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[1]; int rc; - struct i2c_msg msg[] = { - { .addr = c->addr, .flags = 0, - .buf = &addr, .len = 1 }, - { .addr = c->addr, .flags = I2C_M_RD, - .buf = buffer, .len = 1 } - }; - - rc = i2c_transfer(c->adapter, msg, 2); - if (rc < 0 || rc != 2) { - v4l2_err(sd, "i2c i/o error: rc == %d (should be 2)\n", rc); - return rc < 0 ? rc : -EIO; + + rc = i2c_smbus_read_byte_data(c, addr); + if (rc < 0) { + v4l2_err(sd, "i2c i/o error: rc == %d\n", rc); + return rc; } - v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); + v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, rc); - return (buffer[0]); + return rc; } static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, unsigned char value) { struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[2]; int rc; - buffer[0] = addr; - buffer[1] = value; - v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); - if (2 != (rc = i2c_master_send(c, buffer, 2))) - v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc); + v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", addr, value); + rc = i2c_smbus_write_byte_data(c, addr, value); + if (rc < 0) + v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d\n", rc); } static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init, -- cgit v0.10.2 From 46075006d98eb59a0f7c8bc04035eb7e40b0a067 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Wed, 21 May 2014 06:29:29 -0300 Subject: [media] s5p-mfc: Remove duplicate function s5p_mfc_reload_firmware The function s5p_mfc_reload_firmware is exactly same as s5p_mfc_load_firmware. So removing the duplicate function. Signed-off-by: Arun Kumar K Reviewed-by: Tomasz Figa Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 4172318..356eb5e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -162,7 +162,7 @@ static void s5p_mfc_watchdog_worker(struct work_struct *work) /* Double check if there is at least one instance running. * If no instance is in memory than no firmware should be present */ if (dev->num_inst > 0) { - ret = s5p_mfc_reload_firmware(dev); + ret = s5p_mfc_load_firmware(dev); if (ret) { mfc_err("Failed to reload FW\n"); goto unlock; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index 390ca20..19f833e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -106,39 +106,6 @@ int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev) return 0; } -/* Reload firmware to MFC */ -int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev) -{ - struct firmware *fw_blob; - int err; - - /* Firmare has to be present as a separate file or compiled - * into kernel. */ - mfc_debug_enter(); - - err = request_firmware((const struct firmware **)&fw_blob, - dev->variant->fw_name, dev->v4l2_dev.dev); - if (err != 0) { - mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n"); - return -EINVAL; - } - if (fw_blob->size > dev->fw_size) { - mfc_err("MFC firmware is too big to be loaded\n"); - release_firmware(fw_blob); - return -ENOMEM; - } - if (!dev->fw_virt_addr) { - mfc_err("MFC firmware is not allocated\n"); - release_firmware(fw_blob); - return -EINVAL; - } - memcpy(dev->fw_virt_addr, fw_blob->data, fw_blob->size); - wmb(); - release_firmware(fw_blob); - mfc_debug_leave(); - return 0; -} - /* Release firmware memory */ int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev) { -- cgit v0.10.2 From 77ba6b7396047736f37c4f78d1f24f662e655a7e Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Wed, 21 May 2014 06:29:30 -0300 Subject: [media] s5p-mfc: Support multiple firmware sub-versions For MFC firmwares, improved versions with bug fixes and feature additions are released keeping the firmware version including major and minor number same. The issue came with the release of a new MFCv6 firmware with an interface change. This patch adds the support of accepting multiple firmware binaries for every version with the driver trying to load firmwares starting from latest. This ensures full backward compatibility regardless of which firmware version and kernel version is used. Signed-off-by: Arun Kumar K Reviewed-by: Tomasz Figa Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 356eb5e..e3f0b54 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1351,7 +1351,7 @@ static struct s5p_mfc_variant mfc_drvdata_v5 = { .port_num = MFC_NUM_PORTS, .buf_size = &buf_size_v5, .buf_align = &mfc_buf_align_v5, - .fw_name = "s5p-mfc.fw", + .fw_name[0] = "s5p-mfc.fw", }; struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = { @@ -1378,7 +1378,12 @@ static struct s5p_mfc_variant mfc_drvdata_v6 = { .port_num = MFC_NUM_PORTS_V6, .buf_size = &buf_size_v6, .buf_align = &mfc_buf_align_v6, - .fw_name = "s5p-mfc-v6.fw", + .fw_name[0] = "s5p-mfc-v6.fw", + /* + * v6-v2 firmware contains bug fixes and interface change + * for init buffer command + */ + .fw_name[1] = "s5p-mfc-v6-v2.fw", }; struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = { @@ -1405,7 +1410,7 @@ static struct s5p_mfc_variant mfc_drvdata_v7 = { .port_num = MFC_NUM_PORTS_V7, .buf_size = &buf_size_v7, .buf_align = &mfc_buf_align_v7, - .fw_name = "s5p-mfc-v7.fw", + .fw_name[0] = "s5p-mfc-v7.fw", }; struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = { @@ -1432,7 +1437,7 @@ static struct s5p_mfc_variant mfc_drvdata_v8 = { .port_num = MFC_NUM_PORTS_V8, .buf_size = &buf_size_v8, .buf_align = &mfc_buf_align_v8, - .fw_name = "s5p-mfc-v8.fw", + .fw_name[0] = "s5p-mfc-v8.fw", }; static struct platform_device_id mfc_driver_ids[] = { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index b04360c..01816ff 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -38,6 +38,8 @@ #define MFC_BANK2_ALIGN_ORDER 13 #define MFC_BASE_ALIGN_ORDER 17 +#define MFC_FW_MAX_VERSIONS 2 + #include static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b) @@ -163,6 +165,11 @@ enum s5p_mfc_decode_arg { MFC_DEC_RES_CHANGE, }; +enum s5p_mfc_fw_ver { + MFC_FW_V1, + MFC_FW_V2, +}; + #define MFC_BUF_FLAG_USED (1 << 0) #define MFC_BUF_FLAG_EOS (1 << 1) @@ -225,7 +232,7 @@ struct s5p_mfc_variant { u32 version_bit; struct s5p_mfc_buf_size *buf_size; struct s5p_mfc_buf_align *buf_align; - char *fw_name; + char *fw_name[MFC_FW_MAX_VERSIONS]; }; /** @@ -287,6 +294,7 @@ struct s5p_mfc_priv_buf { * @warn_start: hardware error code from which warnings start * @mfc_ops: ops structure holding HW operation function pointers * @mfc_cmds: cmd structure holding HW commands function pointers + * @fw_ver: loaded firmware sub-version * */ struct s5p_mfc_dev { @@ -331,6 +339,7 @@ struct s5p_mfc_dev { struct s5p_mfc_hw_ops *mfc_ops; struct s5p_mfc_hw_cmds *mfc_cmds; const struct s5p_mfc_regs *mfc_regs; + enum s5p_mfc_fw_ver fw_ver; }; /** diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index 19f833e..ca9f789 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -77,14 +77,23 @@ int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev) int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev) { struct firmware *fw_blob; - int err; + int i, err = -EINVAL; /* Firmare has to be present as a separate file or compiled * into kernel. */ mfc_debug_enter(); - err = request_firmware((const struct firmware **)&fw_blob, - dev->variant->fw_name, dev->v4l2_dev.dev); + for (i = MFC_FW_MAX_VERSIONS - 1; i >= 0; i--) { + if (!dev->variant->fw_name[i]) + continue; + err = request_firmware((const struct firmware **)&fw_blob, + dev->variant->fw_name[i], dev->v4l2_dev.dev); + if (!err) { + dev->fw_ver = (enum s5p_mfc_fw_ver) i; + break; + } + } + if (err != 0) { mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n"); return -EINVAL; -- cgit v0.10.2 From 9ab10e469fe117b6c4e2337158d1a97f804f8de7 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Wed, 21 May 2014 06:29:31 -0300 Subject: [media] s5p-mfc: Add init buffer cmd to MFCV6 Latest MFC v6 firmware requires tile mode and loop filter setting to be done as part of Init buffer command, in sync with v7. This patch adds this support for new v6 firmware. Signed-off-by: Arun Kumar K Signed-off-by: Kiran AVND Reviewed-by: Tomasz Figa Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 4f5e0ea..c1c12f8 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -48,6 +48,8 @@ #define WRITEL(data, reg) \ (WARN_ON_ONCE(!(reg)) ? 0 : writel((data), (reg))) +#define IS_MFCV6_V2(dev) (!IS_MFCV7_PLUS(dev) && dev->fw_ver == MFC_FW_V2) + /* Allocate temporary buffers for decoding */ static int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx) { @@ -1352,7 +1354,7 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) WRITEL(ctx->display_delay, mfc_regs->d_display_delay); } - if (IS_MFCV7_PLUS(dev)) { + if (IS_MFCV7_PLUS(dev) || IS_MFCV6_V2(dev)) { WRITEL(reg, mfc_regs->d_dec_options); reg = 0; } @@ -1367,7 +1369,7 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) reg |= (0x1 << S5P_FIMV_D_OPT_TILE_MODE_SHIFT_V6); - if (IS_MFCV7_PLUS(dev)) + if (IS_MFCV7_PLUS(dev) || IS_MFCV6_V2(dev)) WRITEL(reg, mfc_regs->d_init_buffer_options); else WRITEL(reg, mfc_regs->d_dec_options); -- cgit v0.10.2 From c08de62f1fa8813de5bcc0db48570b2340cbaf18 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 22 Jul 2014 02:44:12 -0300 Subject: [media] msi2500: correct style issues Correct some style issues, mostly reported by checkpatch.pl. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 483dc6e..755f959 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -55,9 +55,14 @@ MODULE_PARM_DESC(emulated_formats, "enable emulated formats (disappears in futur #define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) #define MAX_ISOC_ERRORS 20 -/* TODO: These should be moved to V4L2 API */ -#define V4L2_PIX_FMT_SDR_S12 v4l2_fourcc('D', 'S', '1', '2') /* signed 12-bit */ -#define V4L2_PIX_FMT_SDR_MSI2500_384 v4l2_fourcc('M', '3', '8', '4') /* Mirics MSi2500 format 384 */ +/* + * TODO: These formats should be moved to V4L2 API. Formats are currently + * disabled from formats[] table, not visible to userspace. + */ + /* signed 12-bit */ +#define MSI2500_PIX_FMT_SDR_S12 v4l2_fourcc('D', 'S', '1', '2') +/* Mirics MSi2500 format 384 */ +#define MSI2500_PIX_FMT_SDR_MSI2500_384 v4l2_fourcc('M', '3', '8', '4') static const struct v4l2_frequency_band bands[] = { { @@ -86,10 +91,10 @@ static struct msi3101_format formats[] = { #if 0 }, { .name = "10+2-bit signed", - .pixelformat = V4L2_PIX_FMT_SDR_MSI2500_384, + .pixelformat = MSI2500_PIX_FMT_SDR_MSI2500_384, }, { .name = "12-bit signed", - .pixelformat = V4L2_PIX_FMT_SDR_S12, + .pixelformat = MSI2500_PIX_FMT_SDR_S12, #endif }, { .name = "Complex S14LE", @@ -221,6 +226,7 @@ static int msi3101_convert_stream_504(struct msi3101_state *s, u8 *dst, unsigned long jiffies_now = jiffies; unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies_now; s->sample = sample_num[i_max - 1]; dev_dbg(&s->udev->dev, @@ -278,6 +284,7 @@ static int msi3101_convert_stream_504_u8(struct msi3101_state *s, u8 *dst, if (unlikely(time_is_before_jiffies(s->jiffies_next))) { #define MSECS 10000UL unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); s->sample = sample_num[i_max - 1]; dev_dbg(&s->udev->dev, @@ -373,6 +380,7 @@ static int msi3101_convert_stream_384(struct msi3101_state *s, u8 *dst, unsigned long jiffies_now = jiffies; unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies_now; s->sample = sample_num[i_max - 1]; dev_dbg(&s->udev->dev, @@ -438,6 +446,7 @@ static int msi3101_convert_stream_336(struct msi3101_state *s, u8 *dst, unsigned long jiffies_now = jiffies; unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies_now; s->sample = sample_num[i_max - 1]; dev_dbg(&s->udev->dev, @@ -501,6 +510,7 @@ static int msi3101_convert_stream_252(struct msi3101_state *s, u8 *dst, unsigned long jiffies_now = jiffies; unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies_now; s->sample = sample_num[i_max - 1]; dev_dbg(&s->udev->dev, @@ -521,7 +531,7 @@ static int msi3101_convert_stream_252_u16(struct msi3101_state *s, u8 *dst, int i, j, i_max, dst_len = 0; u32 sample_num[3]; u16 *u16dst = (u16 *) dst; - struct {signed int x:14;} se; + struct {signed int x:14; } se; /* There could be 1-3 1024 bytes URB frames */ i_max = src_len / 1024; @@ -573,6 +583,7 @@ static int msi3101_convert_stream_252_u16(struct msi3101_state *s, u8 *dst, if (unlikely(time_is_before_jiffies(s->jiffies_next))) { #define MSECS 10000UL unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); s->sample = sample_num[i_max - 1]; dev_dbg(&s->udev->dev, @@ -667,6 +678,7 @@ handler_end: static void msi3101_iso_stop(struct msi3101_state *s) { int i; + dev_dbg(&s->udev->dev, "%s:\n", __func__); /* Unlinking ISOC buffers one by one */ @@ -682,6 +694,7 @@ static void msi3101_iso_stop(struct msi3101_state *s) static void msi3101_iso_free(struct msi3101_state *s) { int i; + dev_dbg(&s->udev->dev, "%s:\n", __func__); /* Freeing ISOC buffers one by one */ @@ -715,6 +728,7 @@ static int msi3101_isoc_init(struct msi3101_state *s) struct usb_device *udev; struct urb *urb; int i, j, ret; + dev_dbg(&s->udev->dev, "%s:\n", __func__); s->isoc_errors = 0; @@ -781,6 +795,7 @@ static int msi3101_isoc_init(struct msi3101_state *s) static void msi3101_cleanup_queued_bufs(struct msi3101_state *s) { unsigned long flags = 0; + dev_dbg(&s->udev->dev, "%s:\n", __func__); spin_lock_irqsave(&s->queued_bufs_lock, flags); @@ -801,6 +816,7 @@ static void msi3101_disconnect(struct usb_interface *intf) struct v4l2_device *v = usb_get_intfdata(intf); struct msi3101_state *s = container_of(v, struct msi3101_state, v4l2_dev); + dev_dbg(&s->udev->dev, "%s:\n", __func__); mutex_lock(&s->vb_queue_lock); @@ -820,6 +836,7 @@ static int msi3101_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { struct msi3101_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s:\n", __func__); strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); @@ -837,6 +854,7 @@ static int msi3101_queue_setup(struct vb2_queue *vq, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { struct msi3101_state *s = vb2_get_drv_priv(vq); + dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); /* Absolute min and max number of buffers available for mmap() */ @@ -871,15 +889,16 @@ static void msi3101_buf_queue(struct vb2_buffer *vb) #define CMD_STOP_STREAMING 0x45 #define CMD_READ_UNKNOW 0x48 -#define msi3101_dbg_usb_control_msg(udev, r, t, v, _i, b, l) { \ - char *direction; \ - if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ - direction = ">>>"; \ +#define msi3101_dbg_usb_control_msg(_udev, _r, _t, _v, _i, _b, _l) { \ + char *_direction; \ + if (_t & USB_DIR_IN) \ + _direction = "<<<"; \ else \ - direction = "<<<"; \ - dev_dbg(&udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ - "%s %*ph\n", __func__, t, r, v & 0xff, v >> 8, \ - _i & 0xff, _i >> 8, l & 0xff, l >> 8, direction, l, b); \ + _direction = ">>>"; \ + dev_dbg(&_udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \ + "%s %*ph\n", __func__, _t, _r, _v & 0xff, _v >> 8, \ + _i & 0xff, _i >> 8, _l & 0xff, _l >> 8, _direction, \ + _l, _b); \ } static int msi3101_ctrl_msg(struct msi3101_state *s, u8 cmd, u32 data) @@ -915,9 +934,11 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) f_sr = s->f_adc; /* set tuner, subdev, filters according to sampling rate */ - bandwidth_auto = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); + bandwidth_auto = v4l2_ctrl_find(&s->hdl, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); if (v4l2_ctrl_g_ctrl(bandwidth_auto)) { - bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); + bandwidth = v4l2_ctrl_find(&s->hdl, + V4L2_CID_RF_TUNER_BANDWIDTH); v4l2_ctrl_s_ctrl(bandwidth, s->f_adc); } @@ -935,11 +956,11 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) s->convert_stream = msi3101_convert_stream_504; reg7 = 0x000c9407; break; - case V4L2_PIX_FMT_SDR_MSI2500_384: + case MSI2500_PIX_FMT_SDR_MSI2500_384: s->convert_stream = msi3101_convert_stream_384; reg7 = 0x0000a507; break; - case V4L2_PIX_FMT_SDR_S12: + case MSI2500_PIX_FMT_SDR_S12: s->convert_stream = msi3101_convert_stream_336; reg7 = 0x00008507; break; @@ -1012,7 +1033,8 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) dev_dbg(&s->udev->dev, "%s: f_sr=%d f_vco=%d div_n=%d div_m=%d div_r_out=%d reg3=%08x reg4=%08x\n", - __func__, f_sr, f_vco, div_n, div_m, div_r_out, reg3, reg4); + __func__, f_sr, f_vco, div_n, div_m, div_r_out, reg3, + reg4); ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00608008); if (ret) @@ -1053,6 +1075,7 @@ static int msi3101_start_streaming(struct vb2_queue *vq, unsigned int count) { struct msi3101_state *s = vb2_get_drv_priv(vq); int ret; + dev_dbg(&s->udev->dev, "%s:\n", __func__); if (!s->udev) @@ -1116,6 +1139,7 @@ static int msi3101_enum_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct msi3101_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index); if (f->index >= s->num_formats) @@ -1131,6 +1155,7 @@ static int msi3101_g_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_format *f) { struct msi3101_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, (char *)&s->pixelformat); @@ -1147,6 +1172,7 @@ static int msi3101_s_fmt_sdr_cap(struct file *file, void *priv, struct msi3101_state *s = video_drvdata(file); struct vb2_queue *q = &s->vb_queue; int i; + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, (char *)&f->fmt.sdr.pixelformat); @@ -1176,6 +1202,7 @@ static int msi3101_try_fmt_sdr_cap(struct file *file, void *priv, { struct msi3101_state *s = video_drvdata(file); int i; + dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, (char *)&f->fmt.sdr.pixelformat); @@ -1198,6 +1225,7 @@ static int msi3101_s_tuner(struct file *file, void *priv, { struct msi3101_state *s = video_drvdata(file); int ret; + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); if (v->index == 0) @@ -1214,6 +1242,7 @@ static int msi3101_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { struct msi3101_state *s = video_drvdata(file); int ret; + dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); if (v->index == 0) { @@ -1237,6 +1266,7 @@ static int msi3101_g_frequency(struct file *file, void *priv, { struct msi3101_state *s = video_drvdata(file); int ret = 0; + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", __func__, f->tuner, f->type); @@ -1258,6 +1288,7 @@ static int msi3101_s_frequency(struct file *file, void *priv, { struct msi3101_state *s = video_drvdata(file); int ret; + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", __func__, f->tuner, f->type, f->frequency); @@ -1282,6 +1313,7 @@ static int msi3101_enum_freq_bands(struct file *file, void *priv, { struct msi3101_state *s = video_drvdata(file); int ret; + dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", __func__, band->tuner, band->type, band->index); -- cgit v0.10.2 From 8591f708045d8d706adebeaf03e667aa58ccf6ea Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 22 Jul 2014 05:29:57 -0300 Subject: [media] msi2500: refactor USB stream copying There was a lot of duplicated functionality between different stream format handling. Refactor functionality in order to get rid of duplicated code. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 755f959..5bce38e 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -146,8 +146,6 @@ struct msi3101_state { unsigned int vb_full; /* vb is full and packets dropped */ struct urb *urbs[MAX_ISO_BUFS]; - int (*convert_stream)(struct msi3101_state *s, u8 *dst, u8 *src, - unsigned int src_len); /* Controls */ struct v4l2_ctrl_handler hdl; @@ -188,118 +186,8 @@ leave: * +--------------------------------------------------------------------------- * signed 8-bit sample * 504 * 2 = 1008 samples - */ -static int msi3101_convert_stream_504(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) -{ - int i, i_max, dst_len = 0; - u32 sample_num[3]; - - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; - - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { - dev_dbg_ratelimited(&s->udev->dev, - "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); - } - - /* - * Dump all unknown 'garbage' data - maybe we will discover - * someday if there is something rational... - */ - dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); - - /* 504 x I+Q samples */ - src += 16; - memcpy(dst, src, 1008); - src += 1008; - dst += 1008; - dst_len += 1008; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { - unsigned long jiffies_now = jiffies; - unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); - unsigned int samples = sample_num[i_max - 1] - s->sample; - - s->jiffies_next = jiffies_now; - s->sample = sample_num[i_max - 1]; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, msecs, - samples * 1000UL / msecs); - } - - /* next sample (sample = sample + i * 504) */ - s->next_sample = sample_num[i_max - 1] + 504; - - return dst_len; -} - -static int msi3101_convert_stream_504_u8(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) -{ - int i, j, i_max, dst_len = 0; - u32 sample_num[3]; - s8 *s8src; - u8 *u8dst; - - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; - u8dst = (u8 *) dst; - - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { - dev_dbg_ratelimited(&s->udev->dev, - "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); - } - - /* - * Dump all unknown 'garbage' data - maybe we will discover - * someday if there is something rational... - */ - dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); - - /* 504 x I+Q samples */ - src += 16; - - s8src = (s8 *) src; - for (j = 0; j < 1008; j++) - *u8dst++ = *s8src++ + 128; - - src += 1008; - dst += 1008; - dst_len += 1008; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if (unlikely(time_is_before_jiffies(s->jiffies_next))) { -#define MSECS 10000UL - unsigned int samples = sample_num[i_max - 1] - s->sample; - - s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); - s->sample = sample_num[i_max - 1]; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, MSECS, - samples * 1000UL / MSECS); - } - - /* next sample (sample = sample + i * 504) */ - s->next_sample = sample_num[i_max - 1] + 504; - - return dst_len; -} - -/* + * + * * +=========================================================================== * | 00-1023 | USB packet type '384' * +=========================================================================== @@ -342,62 +230,8 @@ static int msi3101_convert_stream_504_u8(struct msi3101_state *s, u8 *dst, * Number 2 (0b10) was never seen. * * 6 * 16 * 2 * 4 = 768 samples. 768 * 4 = 3072 bytes - */ -static int msi3101_convert_stream_384(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) -{ - int i, i_max, dst_len = 0; - u32 sample_num[3]; - - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { - dev_dbg_ratelimited(&s->udev->dev, - "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); - } - - /* - * Dump all unknown 'garbage' data - maybe we will discover - * someday if there is something rational... - */ - dev_dbg_ratelimited(&s->udev->dev, - "%*ph %*ph\n", 12, &src[4], 24, &src[1000]); - - /* 384 x I+Q samples */ - src += 16; - memcpy(dst, src, 984); - src += 984 + 24; - dst += 984; - dst_len += 984; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { - unsigned long jiffies_now = jiffies; - unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); - unsigned int samples = sample_num[i_max - 1] - s->sample; - - s->jiffies_next = jiffies_now; - s->sample = sample_num[i_max - 1]; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu bits=%d.%d.%d.%d\n", - src_len, samples, msecs, - samples * 1000UL / msecs, - s->sample_ctrl_bit[0], s->sample_ctrl_bit[1], - s->sample_ctrl_bit[2], s->sample_ctrl_bit[3]); - } - - /* next sample (sample = sample + i * 384) */ - s->next_sample = sample_num[i_max - 1] + 384; - - return dst_len; -} - -/* + * + * * +=========================================================================== * | 00-1023 | USB packet type '336' * +=========================================================================== @@ -408,60 +242,8 @@ static int msi3101_convert_stream_384(struct msi3101_state *s, u8 *dst, * | 16-1023 | samples * +--------------------------------------------------------------------------- * signed 12-bit sample - */ -static int msi3101_convert_stream_336(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) -{ - int i, i_max, dst_len = 0; - u32 sample_num[3]; - - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; - - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { - dev_dbg_ratelimited(&s->udev->dev, - "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); - } - - /* - * Dump all unknown 'garbage' data - maybe we will discover - * someday if there is something rational... - */ - dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); - - /* 336 x I+Q samples */ - src += 16; - memcpy(dst, src, 1008); - src += 1008; - dst += 1008; - dst_len += 1008; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { - unsigned long jiffies_now = jiffies; - unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); - unsigned int samples = sample_num[i_max - 1] - s->sample; - - s->jiffies_next = jiffies_now; - s->sample = sample_num[i_max - 1]; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, msecs, - samples * 1000UL / msecs); - } - - /* next sample (sample = sample + i * 336) */ - s->next_sample = sample_num[i_max - 1] + 336; - - return dst_len; -} - -/* + * + * * +=========================================================================== * | 00-1023 | USB packet type '252' * +=========================================================================== @@ -473,76 +255,24 @@ static int msi3101_convert_stream_336(struct msi3101_state *s, u8 *dst, * +--------------------------------------------------------------------------- * signed 14-bit sample */ -static int msi3101_convert_stream_252(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) -{ - int i, i_max, dst_len = 0; - u32 sample_num[3]; - - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { - dev_dbg_ratelimited(&s->udev->dev, - "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); - } - - /* - * Dump all unknown 'garbage' data - maybe we will discover - * someday if there is something rational... - */ - dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); - - /* 252 x I+Q samples */ - src += 16; - memcpy(dst, src, 1008); - src += 1008; - dst += 1008; - dst_len += 1008; - } - - /* calculate samping rate and output it in 10 seconds intervals */ - if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) { - unsigned long jiffies_now = jiffies; - unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next); - unsigned int samples = sample_num[i_max - 1] - s->sample; - - s->jiffies_next = jiffies_now; - s->sample = sample_num[i_max - 1]; - dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, msecs, - samples * 1000UL / msecs); - } - - /* next sample (sample = sample + i * 252) */ - s->next_sample = sample_num[i_max - 1] + 252; - - return dst_len; -} - -static int msi3101_convert_stream_252_u16(struct msi3101_state *s, u8 *dst, - u8 *src, unsigned int src_len) +static int msi3101_convert_stream(struct msi3101_state *s, u8 *dst, u8 *src, + unsigned int src_len) { - int i, j, i_max, dst_len = 0; - u32 sample_num[3]; - u16 *u16dst = (u16 *) dst; - struct {signed int x:14; } se; + unsigned int i, j, transactions, dst_len = 0; + u32 sample[3]; - /* There could be 1-3 1024 bytes URB frames */ - i_max = src_len / 1024; + /* There could be 1-3 1024 byte transactions per packet */ + transactions = src_len / 1024; - for (i = 0; i < i_max; i++) { - sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample_num[0]) { + for (i = 0; i < transactions; i++) { + sample[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | + src[0] << 0; + if (i == 0 && s->next_sample != sample[0]) { dev_dbg_ratelimited(&s->udev->dev, "%d samples lost, %d %08x:%08x\n", - sample_num[0] - s->next_sample, - src_len, s->next_sample, sample_num[0]); + sample[0] - s->next_sample, + src_len, s->next_sample, sample[0]); } /* @@ -551,50 +281,96 @@ static int msi3101_convert_stream_252_u16(struct msi3101_state *s, u8 *dst, */ dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]); - /* 252 x I+Q samples */ - src += 16; - - for (j = 0; j < 1008; j += 4) { - unsigned int usample[2]; - int ssample[2]; + src += 16; /* skip header */ - usample[0] = src[j + 0] >> 0 | src[j + 1] << 8; - usample[1] = src[j + 2] >> 0 | src[j + 3] << 8; + switch (s->pixelformat) { + case V4L2_SDR_FMT_CU8: /* 504 x IQ samples */ + { + s8 *s8src = (s8 *) src; + u8 *u8dst = (u8 *) dst; - /* sign extension from 14-bit to signed int */ - ssample[0] = se.x = usample[0]; - ssample[1] = se.x = usample[1]; + for (j = 0; j < 1008; j++) + *u8dst++ = *s8src++ + 128; - /* from signed to unsigned */ - usample[0] = ssample[0] + 8192; - usample[1] = ssample[1] + 8192; - - /* from 14-bit to 16-bit */ - *u16dst++ = (usample[0] << 2) | (usample[0] >> 12); - *u16dst++ = (usample[1] << 2) | (usample[1] >> 12); + src += 1008; + dst += 1008; + dst_len += 1008; + s->next_sample = sample[i] + 504; + break; } + case V4L2_SDR_FMT_CU16LE: /* 252 x IQ samples */ + { + s16 *s16src = (s16 *) src; + u16 *u16dst = (u16 *) dst; + struct {signed int x:14; } se; /* sign extension */ + unsigned int utmp; + + for (j = 0; j < 1008; j += 2) { + /* sign extension from 14-bit to signed int */ + se.x = *s16src++; + /* from signed int to unsigned int */ + utmp = se.x + 8192; + /* from 14-bit to 16-bit */ + *u16dst++ = utmp << 2 | utmp >> 12; + } - src += 1008; - dst += 1008; - dst_len += 1008; + src += 1008; + dst += 1008; + dst_len += 1008; + s->next_sample = sample[i] + 252; + break; + } + case MSI2500_PIX_FMT_SDR_MSI2500_384: /* 384 x IQ samples */ + /* Dump unknown 'garbage' data */ + dev_dbg_ratelimited(&s->udev->dev, + "%*ph\n", 24, &src[1000]); + memcpy(dst, src, 984); + src += 984 + 24; + dst += 984; + dst_len += 984; + s->next_sample = sample[i] + 384; + break; + case V4L2_SDR_FMT_CS8: /* 504 x IQ samples */ + memcpy(dst, src, 1008); + src += 1008; + dst += 1008; + dst_len += 1008; + s->next_sample = sample[i] + 504; + break; + case MSI2500_PIX_FMT_SDR_S12: /* 336 x IQ samples */ + memcpy(dst, src, 1008); + src += 1008; + dst += 1008; + dst_len += 1008; + s->next_sample = sample[i] + 336; + break; + case V4L2_SDR_FMT_CS14LE: /* 252 x IQ samples */ + memcpy(dst, src, 1008); + src += 1008; + dst += 1008; + dst_len += 1008; + s->next_sample = sample[i] + 252; + break; + default: + break; + } } - /* calculate samping rate and output it in 10 seconds intervals */ + /* calculate sample rate and output it in 10 seconds intervals */ if (unlikely(time_is_before_jiffies(s->jiffies_next))) { -#define MSECS 10000UL - unsigned int samples = sample_num[i_max - 1] - s->sample; + #define MSECS 10000UL + unsigned int msecs = jiffies_to_msecs(jiffies - + s->jiffies_next + msecs_to_jiffies(MSECS)); + unsigned int samples = s->next_sample - s->sample; s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); - s->sample = sample_num[i_max - 1]; + s->sample = s->next_sample; dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", - src_len, samples, MSECS, - samples * 1000UL / MSECS); + "size=%u samples=%u msecs=%u sample rate=%lu\n", + src_len, samples, msecs, + samples * 1000UL / msecs); } - /* next sample (sample = sample + i * 252) */ - s->next_sample = sample_num[i_max - 1] + 252; - return dst_len; } @@ -662,7 +438,7 @@ static void msi3101_isoc_handler(struct urb *urb) /* fill framebuffer */ ptr = vb2_plane_vaddr(&fbuf->vb, 0); - flen = s->convert_stream(s, ptr, iso_buf, flen); + flen = msi3101_convert_stream(s, ptr, iso_buf, flen); vb2_set_plane_payload(&fbuf->vb, 0, flen); vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); } @@ -945,32 +721,25 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) /* select stream format */ switch (s->pixelformat) { case V4L2_SDR_FMT_CU8: - s->convert_stream = msi3101_convert_stream_504_u8; - reg7 = 0x000c9407; + reg7 = 0x000c9407; /* 504 */ break; case V4L2_SDR_FMT_CU16LE: - s->convert_stream = msi3101_convert_stream_252_u16; - reg7 = 0x00009407; + reg7 = 0x00009407; /* 252 */ break; case V4L2_SDR_FMT_CS8: - s->convert_stream = msi3101_convert_stream_504; - reg7 = 0x000c9407; + reg7 = 0x000c9407; /* 504 */ break; case MSI2500_PIX_FMT_SDR_MSI2500_384: - s->convert_stream = msi3101_convert_stream_384; - reg7 = 0x0000a507; + reg7 = 0x0000a507; /* 384 */ break; case MSI2500_PIX_FMT_SDR_S12: - s->convert_stream = msi3101_convert_stream_336; - reg7 = 0x00008507; + reg7 = 0x00008507; /* 336 */ break; case V4L2_SDR_FMT_CS14LE: - s->convert_stream = msi3101_convert_stream_252; - reg7 = 0x00009407; + reg7 = 0x00009407; /* 252 */ break; default: - s->convert_stream = msi3101_convert_stream_504_u8; - reg7 = 0x000c9407; + reg7 = 0x000c9407; /* 504 */ break; } -- cgit v0.10.2 From 06ce32cbad2f03f2ed7026a1cb8026db50f021bc Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 22 Jul 2014 05:43:06 -0300 Subject: [media] msi2500: rename namespace msi3101 => msi2500 Rename driver namespace according to current driver name. Old msi3101 was name of reference design, msi2500 is name of chip itself. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 5bce38e..26b1334 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -34,8 +34,8 @@ #include #include -static bool msi3101_emulated_fmt; -module_param_named(emulated_formats, msi3101_emulated_fmt, bool, 0644); +static bool msi2500_emulated_fmt; +module_param_named(emulated_formats, msi2500_emulated_fmt, bool, 0644); MODULE_PARM_DESC(emulated_formats, "enable emulated formats (disappears in future)"); /* @@ -76,14 +76,14 @@ static const struct v4l2_frequency_band bands[] = { }; /* stream formats */ -struct msi3101_format { +struct msi2500_format { char *name; u32 pixelformat; u32 buffersize; }; /* format descriptions for capture and preview */ -static struct msi3101_format formats[] = { +static struct msi2500_format formats[] = { { .name = "Complex S8", .pixelformat = V4L2_SDR_FMT_CS8, @@ -114,12 +114,12 @@ static struct msi3101_format formats[] = { static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); /* intermediate buffers with raw data from the USB device */ -struct msi3101_frame_buf { +struct msi2500_frame_buf { struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ struct list_head list; }; -struct msi3101_state { +struct msi2500_state { struct video_device vdev; struct v4l2_device v4l2_dev; struct v4l2_subdev *v4l2_subdev; @@ -157,17 +157,17 @@ struct msi3101_state { }; /* Private functions */ -static struct msi3101_frame_buf *msi3101_get_next_fill_buf( - struct msi3101_state *s) +static struct msi2500_frame_buf *msi2500_get_next_fill_buf( + struct msi2500_state *s) { unsigned long flags = 0; - struct msi3101_frame_buf *buf = NULL; + struct msi2500_frame_buf *buf = NULL; spin_lock_irqsave(&s->queued_bufs_lock, flags); if (list_empty(&s->queued_bufs)) goto leave; - buf = list_entry(s->queued_bufs.next, struct msi3101_frame_buf, list); + buf = list_entry(s->queued_bufs.next, struct msi2500_frame_buf, list); list_del(&buf->list); leave: spin_unlock_irqrestore(&s->queued_bufs_lock, flags); @@ -256,7 +256,7 @@ leave: * signed 14-bit sample */ -static int msi3101_convert_stream(struct msi3101_state *s, u8 *dst, u8 *src, +static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src, unsigned int src_len) { unsigned int i, j, transactions, dst_len = 0; @@ -378,12 +378,12 @@ static int msi3101_convert_stream(struct msi3101_state *s, u8 *dst, u8 *src, * This gets called for the Isochronous pipe (stream). This is done in interrupt * time, so it has to be fast, not crash, and not stall. Neat. */ -static void msi3101_isoc_handler(struct urb *urb) +static void msi2500_isoc_handler(struct urb *urb) { - struct msi3101_state *s = (struct msi3101_state *)urb->context; + struct msi2500_state *s = (struct msi2500_state *)urb->context; int i, flen, fstatus; unsigned char *iso_buf = NULL; - struct msi3101_frame_buf *fbuf; + struct msi2500_frame_buf *fbuf; if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) { @@ -394,7 +394,7 @@ static void msi3101_isoc_handler(struct urb *urb) if (unlikely(urb->status != 0)) { dev_dbg(&s->udev->dev, - "msi3101_isoc_handler() called with status %d\n", + "msi2500_isoc_handler() called with status %d\n", urb->status); /* Give up after a number of contiguous errors */ if (++s->isoc_errors > MAX_ISOC_ERRORS) @@ -427,7 +427,7 @@ static void msi3101_isoc_handler(struct urb *urb) iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; /* Get free framebuffer */ - fbuf = msi3101_get_next_fill_buf(s); + fbuf = msi2500_get_next_fill_buf(s); if (unlikely(fbuf == NULL)) { s->vb_full++; dev_dbg_ratelimited(&s->udev->dev, @@ -438,7 +438,7 @@ static void msi3101_isoc_handler(struct urb *urb) /* fill framebuffer */ ptr = vb2_plane_vaddr(&fbuf->vb, 0); - flen = msi3101_convert_stream(s, ptr, iso_buf, flen); + flen = msi2500_convert_stream(s, ptr, iso_buf, flen); vb2_set_plane_payload(&fbuf->vb, 0, flen); vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); } @@ -447,11 +447,11 @@ handler_end: i = usb_submit_urb(urb, GFP_ATOMIC); if (unlikely(i != 0)) dev_dbg(&s->udev->dev, - "Error (%d) re-submitting urb in msi3101_isoc_handler\n", + "Error (%d) re-submitting urb in msi2500_isoc_handler\n", i); } -static void msi3101_iso_stop(struct msi3101_state *s) +static void msi2500_iso_stop(struct msi2500_state *s) { int i; @@ -467,7 +467,7 @@ static void msi3101_iso_stop(struct msi3101_state *s) } } -static void msi3101_iso_free(struct msi3101_state *s) +static void msi2500_iso_free(struct msi2500_state *s) { int i; @@ -490,16 +490,16 @@ static void msi3101_iso_free(struct msi3101_state *s) } /* Both v4l2_lock and vb_queue_lock should be locked when calling this */ -static void msi3101_isoc_cleanup(struct msi3101_state *s) +static void msi2500_isoc_cleanup(struct msi2500_state *s) { dev_dbg(&s->udev->dev, "%s:\n", __func__); - msi3101_iso_stop(s); - msi3101_iso_free(s); + msi2500_iso_stop(s); + msi2500_iso_free(s); } /* Both v4l2_lock and vb_queue_lock should be locked when calling this */ -static int msi3101_isoc_init(struct msi3101_state *s) +static int msi2500_isoc_init(struct msi2500_state *s) { struct usb_device *udev; struct urb *urb; @@ -520,7 +520,7 @@ static int msi3101_isoc_init(struct msi3101_state *s) if (urb == NULL) { dev_err(&s->udev->dev, "Failed to allocate urb %d\n", i); - msi3101_isoc_cleanup(s); + msi2500_isoc_cleanup(s); return -ENOMEM; } s->urbs[i] = urb; @@ -536,11 +536,11 @@ static int msi3101_isoc_init(struct msi3101_state *s) dev_err(&s->udev->dev, "Failed to allocate urb buffer %d\n", i); - msi3101_isoc_cleanup(s); + msi2500_isoc_cleanup(s); return -ENOMEM; } urb->transfer_buffer_length = ISO_BUFFER_SIZE; - urb->complete = msi3101_isoc_handler; + urb->complete = msi2500_isoc_handler; urb->context = s; urb->start_frame = 0; urb->number_of_packets = ISO_FRAMES_PER_DESC; @@ -557,7 +557,7 @@ static int msi3101_isoc_init(struct msi3101_state *s) dev_err(&s->udev->dev, "isoc_init() submit_urb %d failed with error %d\n", i, ret); - msi3101_isoc_cleanup(s); + msi2500_isoc_cleanup(s); return ret; } dev_dbg(&s->udev->dev, "URB 0x%p submitted.\n", s->urbs[i]); @@ -568,7 +568,7 @@ static int msi3101_isoc_init(struct msi3101_state *s) } /* Must be called with vb_queue_lock hold */ -static void msi3101_cleanup_queued_bufs(struct msi3101_state *s) +static void msi2500_cleanup_queued_bufs(struct msi2500_state *s) { unsigned long flags = 0; @@ -576,9 +576,9 @@ static void msi3101_cleanup_queued_bufs(struct msi3101_state *s) spin_lock_irqsave(&s->queued_bufs_lock, flags); while (!list_empty(&s->queued_bufs)) { - struct msi3101_frame_buf *buf; + struct msi2500_frame_buf *buf; - buf = list_entry(s->queued_bufs.next, struct msi3101_frame_buf, + buf = list_entry(s->queued_bufs.next, struct msi2500_frame_buf, list); list_del(&buf->list); vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); @@ -587,11 +587,11 @@ static void msi3101_cleanup_queued_bufs(struct msi3101_state *s) } /* The user yanked out the cable... */ -static void msi3101_disconnect(struct usb_interface *intf) +static void msi2500_disconnect(struct usb_interface *intf) { struct v4l2_device *v = usb_get_intfdata(intf); - struct msi3101_state *s = - container_of(v, struct msi3101_state, v4l2_dev); + struct msi2500_state *s = + container_of(v, struct msi2500_state, v4l2_dev); dev_dbg(&s->udev->dev, "%s:\n", __func__); @@ -608,10 +608,10 @@ static void msi3101_disconnect(struct usb_interface *intf) v4l2_device_put(&s->v4l2_dev); } -static int msi3101_querycap(struct file *file, void *fh, +static int msi2500_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { - struct msi3101_state *s = video_drvdata(file); + struct msi2500_state *s = video_drvdata(file); dev_dbg(&s->udev->dev, "%s:\n", __func__); @@ -625,11 +625,11 @@ static int msi3101_querycap(struct file *file, void *fh, } /* Videobuf2 operations */ -static int msi3101_queue_setup(struct vb2_queue *vq, +static int msi2500_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { - struct msi3101_state *s = vb2_get_drv_priv(vq); + struct msi2500_state *s = vb2_get_drv_priv(vq); dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers); @@ -642,11 +642,11 @@ static int msi3101_queue_setup(struct vb2_queue *vq, return 0; } -static void msi3101_buf_queue(struct vb2_buffer *vb) +static void msi2500_buf_queue(struct vb2_buffer *vb) { - struct msi3101_state *s = vb2_get_drv_priv(vb->vb2_queue); - struct msi3101_frame_buf *buf = - container_of(vb, struct msi3101_frame_buf, vb); + struct msi2500_state *s = vb2_get_drv_priv(vb->vb2_queue); + struct msi2500_frame_buf *buf = + container_of(vb, struct msi2500_frame_buf, vb); unsigned long flags = 0; /* Check the device has not disconnected between prep and queuing */ @@ -665,7 +665,7 @@ static void msi3101_buf_queue(struct vb2_buffer *vb) #define CMD_STOP_STREAMING 0x45 #define CMD_READ_UNKNOW 0x48 -#define msi3101_dbg_usb_control_msg(_udev, _r, _t, _v, _i, _b, _l) { \ +#define msi2500_dbg_usb_control_msg(_udev, _r, _t, _v, _i, _b, _l) { \ char *_direction; \ if (_t & USB_DIR_IN) \ _direction = "<<<"; \ @@ -677,7 +677,7 @@ static void msi3101_buf_queue(struct vb2_buffer *vb) _l, _b); \ } -static int msi3101_ctrl_msg(struct msi3101_state *s, u8 cmd, u32 data) +static int msi2500_ctrl_msg(struct msi2500_state *s, u8 cmd, u32 data) { int ret; u8 request = cmd; @@ -685,7 +685,7 @@ static int msi3101_ctrl_msg(struct msi3101_state *s, u8 cmd, u32 data) u16 value = (data >> 0) & 0xffff; u16 index = (data >> 16) & 0xffff; - msi3101_dbg_usb_control_msg(s->udev, + msi2500_dbg_usb_control_msg(s->udev, request, requesttype, value, index, NULL, 0); ret = usb_control_msg(s->udev, usb_sndctrlpipe(s->udev, 0), @@ -700,7 +700,7 @@ static int msi3101_ctrl_msg(struct msi3101_state *s, u8 cmd, u32 data) #define F_REF 24000000 #define DIV_R_IN 2 -static int msi3101_set_usb_adc(struct msi3101_state *s) +static int msi2500_set_usb_adc(struct msi2500_state *s) { int ret, div_n, div_m, div_r_out, f_sr, f_vco, fract; u32 reg3, reg4, reg7; @@ -805,44 +805,44 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) __func__, f_sr, f_vco, div_n, div_m, div_r_out, reg3, reg4); - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00608008); + ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00608008); if (ret) goto err; - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00000c05); + ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00000c05); if (ret) goto err; - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00020000); + ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00020000); if (ret) goto err; - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00480102); + ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00480102); if (ret) goto err; - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00f38008); + ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00f38008); if (ret) goto err; - ret = msi3101_ctrl_msg(s, CMD_WREG, reg7); + ret = msi2500_ctrl_msg(s, CMD_WREG, reg7); if (ret) goto err; - ret = msi3101_ctrl_msg(s, CMD_WREG, reg4); + ret = msi2500_ctrl_msg(s, CMD_WREG, reg4); if (ret) goto err; - ret = msi3101_ctrl_msg(s, CMD_WREG, reg3); + ret = msi2500_ctrl_msg(s, CMD_WREG, reg3); if (ret) goto err; err: return ret; }; -static int msi3101_start_streaming(struct vb2_queue *vq, unsigned int count) +static int msi2500_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct msi3101_state *s = vb2_get_drv_priv(vq); + struct msi2500_state *s = vb2_get_drv_priv(vq); int ret; dev_dbg(&s->udev->dev, "%s:\n", __func__); @@ -856,37 +856,37 @@ static int msi3101_start_streaming(struct vb2_queue *vq, unsigned int count) /* wake-up tuner */ v4l2_subdev_call(s->v4l2_subdev, core, s_power, 1); - ret = msi3101_set_usb_adc(s); + ret = msi2500_set_usb_adc(s); - ret = msi3101_isoc_init(s); + ret = msi2500_isoc_init(s); if (ret) - msi3101_cleanup_queued_bufs(s); + msi2500_cleanup_queued_bufs(s); - ret = msi3101_ctrl_msg(s, CMD_START_STREAMING, 0); + ret = msi2500_ctrl_msg(s, CMD_START_STREAMING, 0); mutex_unlock(&s->v4l2_lock); return ret; } -static void msi3101_stop_streaming(struct vb2_queue *vq) +static void msi2500_stop_streaming(struct vb2_queue *vq) { - struct msi3101_state *s = vb2_get_drv_priv(vq); + struct msi2500_state *s = vb2_get_drv_priv(vq); dev_dbg(&s->udev->dev, "%s:\n", __func__); mutex_lock(&s->v4l2_lock); if (s->udev) - msi3101_isoc_cleanup(s); + msi2500_isoc_cleanup(s); - msi3101_cleanup_queued_bufs(s); + msi2500_cleanup_queued_bufs(s); /* according to tests, at least 700us delay is required */ msleep(20); - if (!msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0)) { + if (!msi2500_ctrl_msg(s, CMD_STOP_STREAMING, 0)) { /* sleep USB IF / ADC */ - msi3101_ctrl_msg(s, CMD_WREG, 0x01000003); + msi2500_ctrl_msg(s, CMD_WREG, 0x01000003); } /* sleep tuner */ @@ -895,19 +895,19 @@ static void msi3101_stop_streaming(struct vb2_queue *vq) mutex_unlock(&s->v4l2_lock); } -static struct vb2_ops msi3101_vb2_ops = { - .queue_setup = msi3101_queue_setup, - .buf_queue = msi3101_buf_queue, - .start_streaming = msi3101_start_streaming, - .stop_streaming = msi3101_stop_streaming, +static struct vb2_ops msi2500_vb2_ops = { + .queue_setup = msi2500_queue_setup, + .buf_queue = msi2500_buf_queue, + .start_streaming = msi2500_start_streaming, + .stop_streaming = msi2500_stop_streaming, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, }; -static int msi3101_enum_fmt_sdr_cap(struct file *file, void *priv, +static int msi2500_enum_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct msi3101_state *s = video_drvdata(file); + struct msi2500_state *s = video_drvdata(file); dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index); @@ -920,10 +920,10 @@ static int msi3101_enum_fmt_sdr_cap(struct file *file, void *priv, return 0; } -static int msi3101_g_fmt_sdr_cap(struct file *file, void *priv, +static int msi2500_g_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct msi3101_state *s = video_drvdata(file); + struct msi2500_state *s = video_drvdata(file); dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, (char *)&s->pixelformat); @@ -935,10 +935,10 @@ static int msi3101_g_fmt_sdr_cap(struct file *file, void *priv, return 0; } -static int msi3101_s_fmt_sdr_cap(struct file *file, void *priv, +static int msi2500_s_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct msi3101_state *s = video_drvdata(file); + struct msi2500_state *s = video_drvdata(file); struct vb2_queue *q = &s->vb_queue; int i; @@ -966,10 +966,10 @@ static int msi3101_s_fmt_sdr_cap(struct file *file, void *priv, return 0; } -static int msi3101_try_fmt_sdr_cap(struct file *file, void *priv, +static int msi2500_try_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct msi3101_state *s = video_drvdata(file); + struct msi2500_state *s = video_drvdata(file); int i; dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__, @@ -989,10 +989,10 @@ static int msi3101_try_fmt_sdr_cap(struct file *file, void *priv, return 0; } -static int msi3101_s_tuner(struct file *file, void *priv, +static int msi2500_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *v) { - struct msi3101_state *s = video_drvdata(file); + struct msi2500_state *s = video_drvdata(file); int ret; dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); @@ -1007,9 +1007,9 @@ static int msi3101_s_tuner(struct file *file, void *priv, return ret; } -static int msi3101_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) +static int msi2500_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct msi3101_state *s = video_drvdata(file); + struct msi2500_state *s = video_drvdata(file); int ret; dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index); @@ -1030,10 +1030,10 @@ static int msi3101_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) return ret; } -static int msi3101_g_frequency(struct file *file, void *priv, +static int msi2500_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct msi3101_state *s = video_drvdata(file); + struct msi2500_state *s = video_drvdata(file); int ret = 0; dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n", @@ -1052,10 +1052,10 @@ static int msi3101_g_frequency(struct file *file, void *priv, return ret; } -static int msi3101_s_frequency(struct file *file, void *priv, +static int msi2500_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { - struct msi3101_state *s = video_drvdata(file); + struct msi2500_state *s = video_drvdata(file); int ret; dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n", @@ -1067,7 +1067,7 @@ static int msi3101_s_frequency(struct file *file, void *priv, bands[0].rangehigh); dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n", __func__, s->f_adc); - ret = msi3101_set_usb_adc(s); + ret = msi2500_set_usb_adc(s); } else if (f->tuner == 1) { ret = v4l2_subdev_call(s->v4l2_subdev, tuner, s_frequency, f); } else { @@ -1077,10 +1077,10 @@ static int msi3101_s_frequency(struct file *file, void *priv, return ret; } -static int msi3101_enum_freq_bands(struct file *file, void *priv, +static int msi2500_enum_freq_bands(struct file *file, void *priv, struct v4l2_frequency_band *band) { - struct msi3101_state *s = video_drvdata(file); + struct msi2500_state *s = video_drvdata(file); int ret; dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n", @@ -1103,13 +1103,13 @@ static int msi3101_enum_freq_bands(struct file *file, void *priv, return ret; } -static const struct v4l2_ioctl_ops msi3101_ioctl_ops = { - .vidioc_querycap = msi3101_querycap, +static const struct v4l2_ioctl_ops msi2500_ioctl_ops = { + .vidioc_querycap = msi2500_querycap, - .vidioc_enum_fmt_sdr_cap = msi3101_enum_fmt_sdr_cap, - .vidioc_g_fmt_sdr_cap = msi3101_g_fmt_sdr_cap, - .vidioc_s_fmt_sdr_cap = msi3101_s_fmt_sdr_cap, - .vidioc_try_fmt_sdr_cap = msi3101_try_fmt_sdr_cap, + .vidioc_enum_fmt_sdr_cap = msi2500_enum_fmt_sdr_cap, + .vidioc_g_fmt_sdr_cap = msi2500_g_fmt_sdr_cap, + .vidioc_s_fmt_sdr_cap = msi2500_s_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = msi2500_try_fmt_sdr_cap, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, @@ -1121,19 +1121,19 @@ static const struct v4l2_ioctl_ops msi3101_ioctl_ops = { .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_g_tuner = msi3101_g_tuner, - .vidioc_s_tuner = msi3101_s_tuner, + .vidioc_g_tuner = msi2500_g_tuner, + .vidioc_s_tuner = msi2500_s_tuner, - .vidioc_g_frequency = msi3101_g_frequency, - .vidioc_s_frequency = msi3101_s_frequency, - .vidioc_enum_freq_bands = msi3101_enum_freq_bands, + .vidioc_g_frequency = msi2500_g_frequency, + .vidioc_s_frequency = msi2500_s_frequency, + .vidioc_enum_freq_bands = msi2500_enum_freq_bands, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, .vidioc_log_status = v4l2_ctrl_log_status, }; -static const struct v4l2_file_operations msi3101_fops = { +static const struct v4l2_file_operations msi2500_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, .release = vb2_fop_release, @@ -1143,27 +1143,27 @@ static const struct v4l2_file_operations msi3101_fops = { .unlocked_ioctl = video_ioctl2, }; -static struct video_device msi3101_template = { +static struct video_device msi2500_template = { .name = "Mirics MSi3101 SDR Dongle", .release = video_device_release_empty, - .fops = &msi3101_fops, - .ioctl_ops = &msi3101_ioctl_ops, + .fops = &msi2500_fops, + .ioctl_ops = &msi2500_ioctl_ops, }; -static void msi3101_video_release(struct v4l2_device *v) +static void msi2500_video_release(struct v4l2_device *v) { - struct msi3101_state *s = - container_of(v, struct msi3101_state, v4l2_dev); + struct msi2500_state *s = + container_of(v, struct msi2500_state, v4l2_dev); v4l2_ctrl_handler_free(&s->hdl); v4l2_device_unregister(&s->v4l2_dev); kfree(s); } -static int msi3101_transfer_one_message(struct spi_master *master, +static int msi2500_transfer_one_message(struct spi_master *master, struct spi_message *m) { - struct msi3101_state *s = spi_master_get_devdata(master); + struct msi2500_state *s = spi_master_get_devdata(master); struct spi_transfer *t; int ret = 0; u32 data; @@ -1175,7 +1175,7 @@ static int msi3101_transfer_one_message(struct spi_master *master, data |= ((u8 *)t->tx_buf)[0] << 8; data |= ((u8 *)t->tx_buf)[1] << 16; data |= ((u8 *)t->tx_buf)[2] << 24; - ret = msi3101_ctrl_msg(s, CMD_WREG, data); + ret = msi2500_ctrl_msg(s, CMD_WREG, data); } m->status = ret; @@ -1183,11 +1183,11 @@ static int msi3101_transfer_one_message(struct spi_master *master, return ret; } -static int msi3101_probe(struct usb_interface *intf, +static int msi2500_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); - struct msi3101_state *s = NULL; + struct msi2500_state *s = NULL; struct v4l2_subdev *sd; struct spi_master *master; int ret; @@ -1198,9 +1198,9 @@ static int msi3101_probe(struct usb_interface *intf, .max_speed_hz = 12000000, }; - s = kzalloc(sizeof(struct msi3101_state), GFP_KERNEL); + s = kzalloc(sizeof(struct msi2500_state), GFP_KERNEL); if (s == NULL) { - pr_err("Could not allocate memory for msi3101_state\n"); + pr_err("Could not allocate memory for msi2500_state\n"); return -ENOMEM; } @@ -1213,15 +1213,15 @@ static int msi3101_probe(struct usb_interface *intf, s->pixelformat = formats[0].pixelformat; s->buffersize = formats[0].buffersize; s->num_formats = NUM_FORMATS; - if (msi3101_emulated_fmt == false) + if (msi2500_emulated_fmt == false) s->num_formats -= 2; /* Init videobuf2 queue structure */ s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; s->vb_queue.drv_priv = s; - s->vb_queue.buf_struct_size = sizeof(struct msi3101_frame_buf); - s->vb_queue.ops = &msi3101_vb2_ops; + s->vb_queue.buf_struct_size = sizeof(struct msi2500_frame_buf); + s->vb_queue.ops = &msi2500_vb2_ops; s->vb_queue.mem_ops = &vb2_vmalloc_memops; s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(&s->vb_queue); @@ -1231,13 +1231,13 @@ static int msi3101_probe(struct usb_interface *intf, } /* Init video_device structure */ - s->vdev = msi3101_template; + s->vdev = msi2500_template; s->vdev.queue = &s->vb_queue; s->vdev.queue->lock = &s->vb_queue_lock; video_set_drvdata(&s->vdev, s); /* Register the v4l2_device structure */ - s->v4l2_dev.release = msi3101_video_release; + s->v4l2_dev.release = msi2500_video_release; ret = v4l2_device_register(&intf->dev, &s->v4l2_dev); if (ret) { dev_err(&s->udev->dev, @@ -1255,7 +1255,7 @@ static int msi3101_probe(struct usb_interface *intf, s->master = master; master->bus_num = 0; master->num_chipselect = 1; - master->transfer_one_message = msi3101_transfer_one_message; + master->transfer_one_message = msi2500_transfer_one_message; spi_master_set_devdata(master, s); ret = spi_register_master(master); if (ret) { @@ -1314,22 +1314,22 @@ err_free_mem: } /* USB device ID list */ -static struct usb_device_id msi3101_id_table[] = { +static struct usb_device_id msi2500_id_table[] = { { USB_DEVICE(0x1df7, 0x2500) }, /* Mirics MSi3101 SDR Dongle */ { USB_DEVICE(0x2040, 0xd300) }, /* Hauppauge WinTV 133559 LF */ { } }; -MODULE_DEVICE_TABLE(usb, msi3101_id_table); +MODULE_DEVICE_TABLE(usb, msi2500_id_table); /* USB subsystem interface */ -static struct usb_driver msi3101_driver = { +static struct usb_driver msi2500_driver = { .name = KBUILD_MODNAME, - .probe = msi3101_probe, - .disconnect = msi3101_disconnect, - .id_table = msi3101_id_table, + .probe = msi2500_probe, + .disconnect = msi2500_disconnect, + .id_table = msi2500_id_table, }; -module_usb_driver(msi3101_driver); +module_usb_driver(msi2500_driver); MODULE_AUTHOR("Antti Palosaari "); MODULE_DESCRIPTION("Mirics MSi3101 SDR Dongle"); -- cgit v0.10.2 From 8954844195670217f57ed185f003bca287fd8ebd Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:17 -0300 Subject: [media] coda: Add encoder/decoder support for CODA960 This patch adds support for the CODA960 VPU in Freescale i.MX6 SoCs. It enables h.264 and MPEG4 encoding and decoding support. Besides the usual register shifting, the CODA960 gains frame memory control and GDI registers that are set up for linear mapping right now, needs ENC_PIC_SRC_INDEX to be set beyond the number of internal buffers for some reason, and has subsampling buffers that need to be set up. Also, the work buffer size is increased to 80 KiB. The CODA960 firmware spins if there is not enough input data in the bitstream buffer. To make it continue, buffers need to be copied into the bitstream as soon as they are queued. As the bitstream fifo is written into from two places, it must be protected with a mutex. For that, using a threaded interrupt handler is necessary. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 31a1848..6671494 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -44,19 +44,24 @@ #define CODA_FMO_BUF_SIZE 32 #define CODADX6_WORK_BUF_SIZE (288 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024) #define CODA7_WORK_BUF_SIZE (128 * 1024) +#define CODA9_WORK_BUF_SIZE (80 * 1024) #define CODA7_TEMP_BUF_SIZE (304 * 1024) +#define CODA9_TEMP_BUF_SIZE (204 * 1024) #define CODA_PARA_BUF_SIZE (10 * 1024) #define CODA_ISRAM_SIZE (2048 * 2) #define CODADX6_IRAM_SIZE 0xb000 #define CODA7_IRAM_SIZE 0x14000 +#define CODA9_IRAM_SIZE 0x21000 #define CODA7_PS_BUF_SIZE 0x28000 +#define CODA9_PS_SAVE_SIZE (512 * 1024) #define CODA_MAX_FRAMEBUFFERS 8 #define CODA_MAX_FRAME_SIZE 0x100000 #define FMO_SLICE_SAVE_BUF_SIZE (32) #define CODA_DEFAULT_GAMMA 4096 +#define CODA9_DEFAULT_GAMMA 24576 /* 0.75 * 32768 */ #define MIN_W 176 #define MIN_H 144 @@ -84,6 +89,7 @@ enum coda_inst_type { enum coda_product { CODA_DX6 = 0xf001, CODA_7541 = 0xf012, + CODA_960 = 0xf020, }; struct coda_fmt { @@ -177,6 +183,16 @@ struct coda_iram_info { phys_addr_t next_paddr; }; +struct gdi_tiled_map { + int xy2ca_map[16]; + int xy2ba_map[16]; + int xy2ra_map[16]; + int rbc2axi_map[32]; + int xy2rbc_config; + int map_type; +#define GDI_LINEAR_FRAME_MAP 0 +}; + struct coda_ctx { struct coda_dev *dev; struct mutex buffer_mutex; @@ -215,8 +231,10 @@ struct coda_ctx { int idx; int reg_idx; struct coda_iram_info iram_info; + struct gdi_tiled_map tiled_map; u32 bit_stream_param; u32 frm_dis_flg; + u32 frame_mem_ctrl; int display_idx; }; @@ -265,15 +283,23 @@ static void coda_command_async(struct coda_ctx *ctx, int cmd) { struct coda_dev *dev = ctx->dev; - if (dev->devtype->product == CODA_7541) { + if (dev->devtype->product == CODA_960 || + dev->devtype->product == CODA_7541) { /* Restore context related registers to CODA */ coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM); coda_write(dev, ctx->frm_dis_flg, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + coda_write(dev, ctx->frame_mem_ctrl, + CODA_REG_BIT_FRAME_MEM_CTRL); coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); } + if (dev->devtype->product == CODA_960) { + coda_write(dev, 1, CODA9_GDI_WPROT_ERR_CLR); + coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); + } + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX); @@ -349,6 +375,13 @@ static struct coda_codec coda7_codecs[] = { CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1080), }; +static struct coda_codec coda9_codecs[] = { + CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1080), + CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1080), + CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1080), + CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1080), +}; + static bool coda_format_is_yuv(u32 fourcc) { switch (fourcc) { @@ -427,6 +460,8 @@ static char *coda_product_name(int product) return "CodaDx6"; case CODA_7541: return "CODA7541"; + case CODA_960: + return "CODA960"; default: snprintf(buf, sizeof(buf), "(0x%04x)", product); return buf; @@ -855,6 +890,7 @@ static int coda_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc) { struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_dev *dev = ctx->dev; int ret; ret = coda_try_decoder_cmd(file, fh, dc); @@ -868,6 +904,13 @@ static int coda_decoder_cmd(struct file *file, void *fh, /* Set the strem-end flag on this context */ ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + if ((dev->devtype->product == CODA_960) && + coda_isbusy(dev) && + (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) { + /* If this context is currently running, update the hardware flag */ + coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM); + } + return 0; } @@ -1023,6 +1066,27 @@ static void coda_fill_bitstream(struct coda_ctx *ctx) } } +static void coda_set_gdi_regs(struct coda_ctx *ctx) +{ + struct gdi_tiled_map *tiled_map = &ctx->tiled_map; + struct coda_dev *dev = ctx->dev; + int i; + + for (i = 0; i < 16; i++) + coda_write(dev, tiled_map->xy2ca_map[i], + CODA9_GDI_XY2_CAS_0 + 4 * i); + for (i = 0; i < 4; i++) + coda_write(dev, tiled_map->xy2ba_map[i], + CODA9_GDI_XY2_BA_0 + 4 * i); + for (i = 0; i < 16; i++) + coda_write(dev, tiled_map->xy2ra_map[i], + CODA9_GDI_XY2_RAS_0 + 4 * i); + coda_write(dev, tiled_map->xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG); + for (i = 0; i < 32; i++) + coda_write(dev, tiled_map->rbc2axi_map[i], + CODA9_GDI_RBC2_AXI_0 + 4 * i); +} + /* * Mem-to-mem operations. */ @@ -1071,6 +1135,9 @@ static int coda_prepare_decode(struct coda_ctx *ctx) } } + if (dev->devtype->product == CODA_960) + coda_set_gdi_regs(ctx); + /* Set rotator output */ picture_y = vb2_dma_contig_plane_dma_addr(dst_buf, 0); if (q_data_dst->fourcc == V4L2_PIX_FMT_YVU420) { @@ -1081,10 +1148,26 @@ static int coda_prepare_decode(struct coda_ctx *ctx) picture_cb = picture_y + stridey * height; picture_cr = picture_cb + stridey / 2 * height / 2; } - coda_write(dev, picture_y, CODA_CMD_DEC_PIC_ROT_ADDR_Y); - coda_write(dev, picture_cb, CODA_CMD_DEC_PIC_ROT_ADDR_CB); - coda_write(dev, picture_cr, CODA_CMD_DEC_PIC_ROT_ADDR_CR); - coda_write(dev, stridey, CODA_CMD_DEC_PIC_ROT_STRIDE); + + if (dev->devtype->product == CODA_960) { + /* + * The CODA960 seems to have an internal list of buffers with + * 64 entries that includes the registered frame buffers as + * well as the rotator buffer output. + * ROT_INDEX needs to be < 0x40, but > ctx->num_internal_frames. + */ + coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->v4l2_buf.index, + CODA9_CMD_DEC_PIC_ROT_INDEX); + coda_write(dev, picture_y, CODA9_CMD_DEC_PIC_ROT_ADDR_Y); + coda_write(dev, picture_cb, CODA9_CMD_DEC_PIC_ROT_ADDR_CB); + coda_write(dev, picture_cr, CODA9_CMD_DEC_PIC_ROT_ADDR_CR); + coda_write(dev, stridey, CODA9_CMD_DEC_PIC_ROT_STRIDE); + } else { + coda_write(dev, picture_y, CODA_CMD_DEC_PIC_ROT_ADDR_Y); + coda_write(dev, picture_cb, CODA_CMD_DEC_PIC_ROT_ADDR_CB); + coda_write(dev, picture_cr, CODA_CMD_DEC_PIC_ROT_ADDR_CR); + coda_write(dev, stridey, CODA_CMD_DEC_PIC_ROT_STRIDE); + } coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode, CODA_CMD_DEC_PIC_ROT_MODE); @@ -1094,6 +1177,9 @@ static int coda_prepare_decode(struct coda_ctx *ctx) case CODA_7541: coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION); break; + case CODA_960: + coda_write(dev, (1 << 10), CODA_CMD_DEC_PIC_OPTION); /* 'hardcode to use interrupt disable mode'? */ + break; } coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM); @@ -1138,6 +1224,9 @@ static void coda_prepare_encode(struct coda_ctx *ctx) src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME; } + if (dev->devtype->product == CODA_960) + coda_set_gdi_regs(ctx); + /* * Copy headers at the beginning of the first frame for H.264 only. * In MPEG4 they are already copied by the coda. @@ -1216,15 +1305,31 @@ static void coda_prepare_encode(struct coda_ctx *ctx) break; } - coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y); - coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB); - coda_write(dev, picture_cr, CODA_CMD_ENC_PIC_SRC_ADDR_CR); + if (dev->devtype->product == CODA_960) { + coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX); + coda_write(dev, q_data_src->width, CODA9_CMD_ENC_PIC_SRC_STRIDE); + coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC); + + coda_write(dev, picture_y, CODA9_CMD_ENC_PIC_SRC_ADDR_Y); + coda_write(dev, picture_cb, CODA9_CMD_ENC_PIC_SRC_ADDR_CB); + coda_write(dev, picture_cr, CODA9_CMD_ENC_PIC_SRC_ADDR_CR); + } else { + coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y); + coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB); + coda_write(dev, picture_cr, CODA_CMD_ENC_PIC_SRC_ADDR_CR); + } coda_write(dev, force_ipicture << 1 & 0x2, CODA_CMD_ENC_PIC_OPTION); coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START); coda_write(dev, pic_stream_buffer_size / 1024, CODA_CMD_ENC_PIC_BB_SIZE); + + if (!ctx->streamon_out) { + /* After streamoff on the output side, set the stream end flag */ + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM); + } } static void coda_device_run(void *m2m_priv) @@ -1350,6 +1455,32 @@ static struct v4l2_m2m_ops coda_m2m_ops = { .unlock = coda_unlock, }; +static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type) +{ + struct gdi_tiled_map *tiled_map = &ctx->tiled_map; + int luma_map, chro_map, i; + + memset(tiled_map, 0, sizeof(*tiled_map)); + + luma_map = 64; + chro_map = 64; + tiled_map->map_type = tiled_map_type; + for (i = 0; i < 16; i++) + tiled_map->xy2ca_map[i] = luma_map << 8 | chro_map; + for (i = 0; i < 4; i++) + tiled_map->xy2ba_map[i] = luma_map << 8 | chro_map; + for (i = 0; i < 16; i++) + tiled_map->xy2ra_map[i] = luma_map << 8 | chro_map; + + if (tiled_map_type == GDI_LINEAR_FRAME_MAP) { + tiled_map->xy2rbc_config = 0; + } else { + dev_err(&ctx->dev->plat_dev->dev, "invalid map type: %d\n", + tiled_map_type); + return; + } +} + static void set_default_params(struct coda_ctx *ctx) { int max_w; @@ -1373,6 +1504,9 @@ static void set_default_params(struct coda_ctx *ctx) ctx->q_data[V4L2_M2M_DST].width = max_w; ctx->q_data[V4L2_M2M_DST].height = max_h; ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE; + + if (ctx->dev->devtype->product == CODA_960) + coda_set_tiled_map_type(ctx, GDI_LINEAR_FRAME_MAP); } /* @@ -1422,6 +1556,7 @@ static int coda_buf_prepare(struct vb2_buffer *vb) static void coda_buf_queue(struct vb2_buffer *vb) { struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct coda_dev *dev = ctx->dev; struct coda_q_data *q_data; q_data = get_q_data(ctx, vb->vb2_queue->type); @@ -1436,8 +1571,15 @@ static void coda_buf_queue(struct vb2_buffer *vb) * For backwards compatibility, queuing an empty buffer marks * the stream end */ - if (vb2_get_plane_payload(vb, 0) == 0) + if (vb2_get_plane_payload(vb, 0) == 0) { ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + if ((dev->devtype->product == CODA_960) && + coda_isbusy(dev) && + (ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX))) { + /* if this decoder instance is running, set the stream end flag */ + coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM); + } + } mutex_lock(&ctx->bitstream_mutex); v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); coda_fill_bitstream(ctx); @@ -1612,6 +1754,11 @@ static void coda_setup_iram(struct coda_ctx *ctx) bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; break; + case CODA_960: + dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE; + bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE; + ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE; + break; default: /* CODA_DX6 */ return; } @@ -1721,6 +1868,11 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, case CODA_7541: size = CODA7_WORK_BUF_SIZE; break; + case CODA_960: + size = CODA9_WORK_BUF_SIZE; + if (q_data->fourcc == V4L2_PIX_FMT_H264) + size += CODA9_PS_SAVE_SIZE; + break; default: return 0; } @@ -1805,12 +1957,17 @@ static int coda_start_decoding(struct coda_ctx *ctx) coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START); coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE); val = 0; - if (dev->devtype->product == CODA_7541) + if ((dev->devtype->product == CODA_7541) || + (dev->devtype->product == CODA_960)) val |= CODA_REORDER_ENABLE; coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION); ctx->params.codec_mode = ctx->codec->mode; - ctx->params.codec_mode_aux = 0; + if (dev->devtype->product == CODA_960 && + src_fourcc == V4L2_PIX_FMT_MPEG4) + ctx->params.codec_mode_aux = CODA_MP4_AUX_MPEG4; + else + ctx->params.codec_mode_aux = 0; if (src_fourcc == V4L2_PIX_FMT_H264) { if (dev->devtype->product == CODA_7541) { coda_write(dev, ctx->psbuf.paddr, @@ -1818,6 +1975,13 @@ static int coda_start_decoding(struct coda_ctx *ctx) coda_write(dev, (CODA7_PS_BUF_SIZE / 1024), CODA_CMD_DEC_SEQ_PS_BB_SIZE); } + if (dev->devtype->product == CODA_960) { + coda_write(dev, 0, CODA_CMD_DEC_SEQ_X264_MV_EN); + coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE); + } + } + if (dev->devtype->product != CODA_960) { + coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE); } if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) { @@ -1889,6 +2053,20 @@ static int coda_start_decoding(struct coda_ctx *ctx) CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); coda_write(dev, ctx->iram_info.buf_ovl_use, CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); + if (dev->devtype->product == CODA_960) + coda_write(dev, ctx->iram_info.buf_btp_use, + CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); + } + + if (dev->devtype->product == CODA_960) { + coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY); + + coda_write(dev, 0x20262024, CODA9_CMD_SET_FRAME_CACHE_SIZE); + coda_write(dev, 2 << CODA9_CACHE_PAGEMERGE_OFFSET | + 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET, + CODA9_CMD_SET_FRAME_CACHE_CONFIG); } if (src_fourcc == V4L2_PIX_FMT_H264) { @@ -1902,8 +2080,16 @@ static int coda_start_decoding(struct coda_ctx *ctx) int max_mb_x = 1920 / 16; int max_mb_y = 1088 / 16; int max_mb_num = max_mb_x * max_mb_y; + coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, CODA7_CMD_SET_FRAME_MAX_DEC_SIZE); + } else if (dev->devtype->product == CODA_960) { + int max_mb_x = 1920 / 16; + int max_mb_y = 1088 / 16; + int max_mb_num = max_mb_x * max_mb_y; + + coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y, + CODA9_CMD_SET_FRAME_MAX_DEC_SIZE); } if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) { @@ -1919,34 +2105,49 @@ static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf, int header_code, u8 *header, int *size) { struct coda_dev *dev = ctx->dev; + size_t bufsize; int ret; + int i; + + if (dev->devtype->product == CODA_960) + memset(vb2_plane_vaddr(buf, 0), 0, 64); coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START); - coda_write(dev, vb2_plane_size(buf, 0), CODA_CMD_ENC_HEADER_BB_SIZE); + bufsize = vb2_plane_size(buf, 0); + if (dev->devtype->product == CODA_960) + bufsize /= 1024; + coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE); coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE); ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); return ret; } - *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) - - coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); + + if (dev->devtype->product == CODA_960) { + for (i = 63; i > 0; i--) + if (((char *)vb2_plane_vaddr(buf, 0))[i] != 0) + break; + *size = i + 1; + } else { + *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) - + coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); + } memcpy(header, vb2_plane_vaddr(buf, 0), *size); return 0; } +static int coda_start_encoding(struct coda_ctx *ctx); + static int coda_start_streaming(struct vb2_queue *q, unsigned int count) { struct coda_ctx *ctx = vb2_get_drv_priv(q); struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev; - u32 bitstream_buf, bitstream_size; struct coda_dev *dev = ctx->dev; struct coda_q_data *q_data_src, *q_data_dst; - struct vb2_buffer *buf; u32 dst_fourcc; - u32 value; int ret = 0; q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); @@ -1981,10 +2182,7 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) v4l2_m2m_set_src_buffered(ctx->m2m_ctx, true); ctx->gopcounter = ctx->params.gop_size - 1; - buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0); q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - bitstream_size = q_data_dst->sizeimage; dst_fourcc = q_data_dst->fourcc; ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc, @@ -2003,16 +2201,36 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) mutex_lock(&dev->coda_mutex); ret = coda_start_decoding(ctx); mutex_unlock(&dev->coda_mutex); - if (ret == -EAGAIN) { + if (ret == -EAGAIN) return 0; - } else if (ret < 0) { + else if (ret < 0) return ret; - } else { - ctx->initialized = 1; - return 0; - } + } else { + ret = coda_start_encoding(ctx); } + ctx->initialized = 1; + return ret; +} + +static int coda_start_encoding(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct coda_q_data *q_data_src, *q_data_dst; + u32 bitstream_buf, bitstream_size; + struct vb2_buffer *buf; + int gamma, ret, value; + u32 dst_fourcc; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_fourcc = q_data_dst->fourcc; + + buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0); + bitstream_size = q_data_dst->sizeimage; + if (!coda_is_initialized(dev)) { v4l2_err(v4l2_dev, "coda is not initialized.\n"); return -EFAULT; @@ -2028,11 +2246,20 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN | CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); break; - default: + case CODA_960: + coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); + /* fallthrough */ + case CODA_7541: coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN | CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); + break; } + value = coda_read(dev, CODA_REG_BIT_FRAME_MEM_CTRL); + value &= ~(1 << 2 | 0x7 << 9); + ctx->frame_mem_ctrl = value; + coda_write(dev, value, CODA_REG_BIT_FRAME_MEM_CTRL); + if (dev->devtype->product == CODA_DX6) { /* Configure the coda */ coda_write(dev, dev->iram.paddr, CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR); @@ -2055,11 +2282,17 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) ctx->params.codec_mode = ctx->codec->mode; switch (dst_fourcc) { case V4L2_PIX_FMT_MPEG4: - coda_write(dev, CODA_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD); + if (dev->devtype->product == CODA_960) + coda_write(dev, CODA9_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD); + else + coda_write(dev, CODA_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD); coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA); break; case V4L2_PIX_FMT_H264: - coda_write(dev, CODA_STD_H264, CODA_CMD_ENC_SEQ_COD_STD); + if (dev->devtype->product == CODA_960) + coda_write(dev, CODA9_STD_H264, CODA_CMD_ENC_SEQ_COD_STD); + else + coda_write(dev, CODA_STD_H264, CODA_CMD_ENC_SEQ_COD_STD); coda_write(dev, 0, CODA_CMD_ENC_SEQ_264_PARA); break; default: @@ -2092,6 +2325,8 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) /* Rate control enabled */ value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) << CODA_RATECONTROL_BITRATE_OFFSET; value |= 1 & CODA_RATECONTROL_ENABLE_MASK; + if (dev->devtype->product == CODA_960) + value |= BIT(31); /* disable autoskip */ } else { value = 0; } @@ -2103,31 +2338,48 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START); coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE); - /* set default gamma */ - value = (CODA_DEFAULT_GAMMA & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET; - coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_GAMMA); - if (CODA_DEFAULT_GAMMA > 0) { - if (dev->devtype->product == CODA_DX6) - value = 1 << CODADX6_OPTION_GAMMA_OFFSET; - else - value = 1 << CODA7_OPTION_GAMMA_OFFSET; + value = 0; + if (dev->devtype->product == CODA_960) + gamma = CODA9_DEFAULT_GAMMA; + else + gamma = CODA_DEFAULT_GAMMA; + if (gamma > 0) { + coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET, + CODA_CMD_ENC_SEQ_RC_GAMMA); + } + if (dev->devtype->product == CODA_960) { + if (CODA_DEFAULT_GAMMA > 0) + value |= 1 << CODA9_OPTION_GAMMA_OFFSET; } else { - value = 0; + if (CODA_DEFAULT_GAMMA > 0) { + if (dev->devtype->product == CODA_DX6) + value |= 1 << CODADX6_OPTION_GAMMA_OFFSET; + else + value |= 1 << CODA7_OPTION_GAMMA_OFFSET; + } } coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); + coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE); + coda_setup_iram(ctx); if (dst_fourcc == V4L2_PIX_FMT_H264) { - if (dev->devtype->product == CODA_DX6) { + switch (dev->devtype->product) { + case CODA_DX6: value = FMO_SLICE_SAVE_BUF_SIZE << 7; coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO); - } else { + break; + case CODA_7541: coda_write(dev, ctx->iram_info.search_ram_paddr, CODA7_CMD_ENC_SEQ_SEARCH_BASE); coda_write(dev, ctx->iram_info.search_ram_size, CODA7_CMD_ENC_SEQ_SEARCH_SIZE); + break; + case CODA_960: + coda_write(dev, 0, CODA9_CMD_ENC_SEQ_ME_OPTION); + coda_write(dev, 0, CODA9_CMD_ENC_SEQ_INTRA_WEIGHT); } } @@ -2143,7 +2395,10 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) goto out; } - ctx->num_internal_frames = 2; + if (dev->devtype->product == CODA_960) + ctx->num_internal_frames = 4; + else + ctx->num_internal_frames = 2; ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc); if (ret < 0) { v4l2_err(v4l2_dev, "failed to allocate framebuffers\n"); @@ -2166,7 +2421,16 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); coda_write(dev, ctx->iram_info.buf_ovl_use, CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); + if (dev->devtype->product == CODA_960) { + coda_write(dev, ctx->iram_info.buf_btp_use, + CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); + + /* FIXME */ + coda_write(dev, ctx->internal_frames[2].paddr, CODA9_CMD_SET_FRAME_SUBSAMP_A); + coda_write(dev, ctx->internal_frames[3].paddr, CODA9_CMD_SET_FRAME_SUBSAMP_B); + } } + ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF); if (ret < 0) { v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n"); @@ -2250,6 +2514,17 @@ static void coda_stop_streaming(struct vb2_queue *q) "%s: output\n", __func__); ctx->streamon_out = 0; + if (ctx->inst_type == CODA_INST_DECODER && + coda_isbusy(dev) && ctx->idx == coda_read(dev, CODA_REG_BIT_RUN_INDEX)) { + /* if this decoder instance is running, set the stream end flag */ + if (dev->devtype->product == CODA_960) { + u32 val = coda_read(dev, CODA_REG_BIT_BIT_STREAM_PARAM); + + val |= CODA_BIT_STREAM_END_FLAG; + coda_write(dev, val, CODA_REG_BIT_BIT_STREAM_PARAM); + ctx->bit_stream_param = val; + } + } ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; ctx->isequence = 0; @@ -2451,6 +2726,7 @@ static int coda_open(struct file *file) ctx->idx = idx; switch (dev->devtype->product) { case CODA_7541: + case CODA_960: ctx->reg_idx = 0; break; default: @@ -2771,7 +3047,7 @@ static void coda_finish_encode(struct coda_ctx *ctx) u32 wr_ptr, start_ptr; src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); /* Get results from the coda */ start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START); @@ -2808,6 +3084,8 @@ static void coda_finish_encode(struct coda_ctx *ctx) dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); ctx->gopcounter--; @@ -2906,6 +3184,7 @@ static void coda_timeout(struct work_struct *work) static u32 coda_supported_firmwares[] = { CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5), CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), + CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5), }; static bool coda_firmware_supported(u32 vernum) @@ -2960,7 +3239,8 @@ static int coda_hw_init(struct coda_dev *dev) coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4); /* Tell the BIT where to find everything it needs */ - if (dev->devtype->product == CODA_7541) { + if (dev->devtype->product == CODA_960 || + dev->devtype->product == CODA_7541) { coda_write(dev, dev->tempbuf.paddr, CODA_REG_BIT_TEMP_BUF_ADDR); coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); @@ -2980,7 +3260,10 @@ static int coda_hw_init(struct coda_dev *dev) default: coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, CODA_REG_BIT_STREAM_CTRL); } - coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL); + if (dev->devtype->product == CODA_960) + coda_write(dev, 1 << 12, CODA_REG_BIT_FRAME_MEM_CTRL); + else + coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL); if (dev->devtype->product != CODA_DX6) coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE); @@ -3010,6 +3293,12 @@ static int coda_hw_init(struct coda_dev *dev) return -EIO; } + if (dev->devtype->product == CODA_960) { + data = coda_read(dev, CODA9_CMD_FIRMWARE_CODE_REV); + v4l2_info(&dev->v4l2_dev, "Firmware code revision: %d\n", + data); + } + /* Check we are compatible with the loaded firmware */ data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM); product = CODA_FIRMWARE_PRODUCT(data); @@ -3125,6 +3414,8 @@ static int coda_firmware_request(struct coda_dev *dev) enum coda_platform { CODA_IMX27, CODA_IMX53, + CODA_IMX6Q, + CODA_IMX6DL, }; static const struct coda_devtype coda_devdata[] = { @@ -3140,6 +3431,18 @@ static const struct coda_devtype coda_devdata[] = { .codecs = coda7_codecs, .num_codecs = ARRAY_SIZE(coda7_codecs), }, + [CODA_IMX6Q] = { + .firmware = "v4l-coda960-imx6q.bin", + .product = CODA_960, + .codecs = coda9_codecs, + .num_codecs = ARRAY_SIZE(coda9_codecs), + }, + [CODA_IMX6DL] = { + .firmware = "v4l-coda960-imx6dl.bin", + .product = CODA_960, + .codecs = coda9_codecs, + .num_codecs = ARRAY_SIZE(coda9_codecs), + }, }; static struct platform_device_id coda_platform_ids[] = { @@ -3153,6 +3456,8 @@ MODULE_DEVICE_TABLE(platform, coda_platform_ids); static const struct of_device_id coda_dt_ids[] = { { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, + { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] }, + { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, coda_dt_ids); @@ -3255,6 +3560,9 @@ static int coda_probe(struct platform_device *pdev) case CODA_7541: dev->tempbuf.size = CODA7_TEMP_BUF_SIZE; break; + case CODA_960: + dev->tempbuf.size = CODA9_TEMP_BUF_SIZE; + break; } if (dev->tempbuf.size) { ret = coda_alloc_aux_buf(dev, &dev->tempbuf, @@ -3273,6 +3581,8 @@ static int coda_probe(struct platform_device *pdev) case CODA_7541: dev->iram.size = CODA7_IRAM_SIZE; break; + case CODA_960: + dev->iram.size = CODA9_IRAM_SIZE; } dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size, &dev->iram.paddr); diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda.h index 4e32e2e..c791275 100644 --- a/drivers/media/platform/coda.h +++ b/drivers/media/platform/coda.h @@ -27,6 +27,14 @@ #define CODA_REG_BIT_CODE_RESET 0x014 #define CODA_REG_RESET_ENABLE (1 << 0) #define CODA_REG_BIT_CUR_PC 0x018 +#define CODA9_REG_BIT_SW_RESET 0x024 +#define CODA9_SW_RESET_BPU_CORE 0x008 +#define CODA9_SW_RESET_BPU_BUS 0x010 +#define CODA9_SW_RESET_VCE_CORE 0x020 +#define CODA9_SW_RESET_VCE_BUS 0x040 +#define CODA9_SW_RESET_GDI_CORE 0x080 +#define CODA9_SW_RESET_GDI_BUS 0x100 +#define CODA9_REG_BIT_SW_RESET_STATUS 0x034 /* Static SW registers */ #define CODA_REG_BIT_CODE_BUF_ADDR 0x100 @@ -39,9 +47,11 @@ #define CODADX6_STREAM_BUF_PIC_FLUSH (1 << 2) #define CODA7_STREAM_BUF_DYNALLOC_EN (1 << 5) #define CODADX6_STREAM_BUF_DYNALLOC_EN (1 << 4) -#define CODA_STREAM_CHKDIS_OFFSET (1 << 1) +#define CODADX6_STREAM_CHKDIS_OFFSET (1 << 1) +#define CODA7_STREAM_SEL_64BITS_ENDIAN (1 << 1) #define CODA_STREAM_ENDIAN_SELECT (1 << 0) #define CODA_REG_BIT_FRAME_MEM_CTRL 0x110 +#define CODA_FRAME_CHROMA_INTERLEAVE (1 << 2) #define CODA_IMAGE_ENDIAN_SELECT (1 << 0) #define CODA_REG_BIT_BIT_STREAM_PARAM 0x114 #define CODA_BIT_STREAM_END_FLAG (1 << 2) @@ -52,13 +62,21 @@ #define CODA_REG_BIT_FRM_DIS_FLG(x) (0x150 + 4 * (x)) #define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140 #define CODA7_REG_BIT_AXI_SRAM_USE 0x140 +#define CODA9_USE_HOST_BTP_ENABLE (1 << 13) +#define CODA9_USE_HOST_OVL_ENABLE (1 << 12) #define CODA7_USE_HOST_ME_ENABLE (1 << 11) +#define CODA9_USE_HOST_DBK_ENABLE (3 << 10) #define CODA7_USE_HOST_OVL_ENABLE (1 << 10) #define CODA7_USE_HOST_DBK_ENABLE (1 << 9) +#define CODA9_USE_HOST_IP_ENABLE (1 << 9) #define CODA7_USE_HOST_IP_ENABLE (1 << 8) +#define CODA9_USE_HOST_BIT_ENABLE (1 << 8) #define CODA7_USE_HOST_BIT_ENABLE (1 << 7) +#define CODA9_USE_BTP_ENABLE (1 << 5) #define CODA7_USE_ME_ENABLE (1 << 4) +#define CODA9_USE_OVL_ENABLE (1 << 4) #define CODA7_USE_OVL_ENABLE (1 << 3) +#define CODA9_USE_DBK_ENABLE (3 << 2) #define CODA7_USE_DBK_ENABLE (1 << 2) #define CODA7_USE_IP_ENABLE (1 << 1) #define CODA7_USE_BIT_ENABLE (1 << 0) @@ -93,6 +111,18 @@ #define CODA7_MODE_ENCODE_H264 8 #define CODA7_MODE_ENCODE_MP4 11 #define CODA7_MODE_ENCODE_MJPG 13 +#define CODA9_MODE_DECODE_H264 0 +#define CODA9_MODE_DECODE_VC1 1 +#define CODA9_MODE_DECODE_MP2 2 +#define CODA9_MODE_DECODE_MP4 3 +#define CODA9_MODE_DECODE_DV3 3 +#define CODA9_MODE_DECODE_RV 4 +#define CODA9_MODE_DECODE_AVS 5 +#define CODA9_MODE_DECODE_MJPG 6 +#define CODA9_MODE_DECODE_VPX 7 +#define CODA9_MODE_ENCODE_H264 8 +#define CODA9_MODE_ENCODE_MP4 11 +#define CODA9_MODE_ENCODE_MJPG 13 #define CODA_MODE_INVALID 0xffff #define CODA_REG_BIT_INT_ENABLE 0x170 #define CODA_INT_INTERRUPT_ENABLE (1 << 3) @@ -129,6 +159,7 @@ #define CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE 0x1a0 #define CODA7_RET_DEC_SEQ_ASPECT 0x1b0 +#define CODA9_RET_DEC_SEQ_BITRATE 0x1b4 #define CODA_RET_DEC_SEQ_SUCCESS 0x1c0 #define CODA_RET_DEC_SEQ_SRC_FMT 0x1c4 /* SRC_SIZE on CODA7 */ #define CODA_RET_DEC_SEQ_SRC_SIZE 0x1c4 @@ -145,13 +176,19 @@ #define CODA_RET_DEC_SEQ_FRATE_DR 0x1e8 #define CODA_RET_DEC_SEQ_JPG_PARA 0x1e4 #define CODA_RET_DEC_SEQ_JPG_THUMB_IND 0x1e8 +#define CODA9_RET_DEC_SEQ_HEADER_REPORT 0x1ec /* Decoder Picture Run */ #define CODA_CMD_DEC_PIC_ROT_MODE 0x180 #define CODA_CMD_DEC_PIC_ROT_ADDR_Y 0x184 +#define CODA9_CMD_DEC_PIC_ROT_INDEX 0x184 #define CODA_CMD_DEC_PIC_ROT_ADDR_CB 0x188 +#define CODA9_CMD_DEC_PIC_ROT_ADDR_Y 0x188 #define CODA_CMD_DEC_PIC_ROT_ADDR_CR 0x18c +#define CODA9_CMD_DEC_PIC_ROT_ADDR_CB 0x18c #define CODA_CMD_DEC_PIC_ROT_STRIDE 0x190 +#define CODA9_CMD_DEC_PIC_ROT_ADDR_CR 0x190 +#define CODA9_CMD_DEC_PIC_ROT_STRIDE 0x1b8 #define CODA_CMD_DEC_PIC_OPTION 0x194 #define CODA_PRE_SCAN_EN (1 << 0) @@ -183,25 +220,39 @@ #define CODA_RET_DEC_PIC_CROP_TOP_BOTTOM 0x1e4 #define CODA_RET_DEC_PIC_FRAME_NEED 0x1ec +#define CODA9_RET_DEC_PIC_VP8_PIC_REPORT 0x1e8 +#define CODA9_RET_DEC_PIC_ASPECT 0x1f0 +#define CODA9_RET_DEC_PIC_VP8_SCALE_INFO 0x1f0 +#define CODA9_RET_DEC_PIC_FRATE_NR 0x1f4 +#define CODA9_RET_DEC_PIC_FRATE_DR 0x1f8 + /* Encoder Sequence Initialization */ #define CODA_CMD_ENC_SEQ_BB_START 0x180 #define CODA_CMD_ENC_SEQ_BB_SIZE 0x184 #define CODA_CMD_ENC_SEQ_OPTION 0x188 #define CODA7_OPTION_AVCINTRA16X16ONLY_OFFSET 9 +#define CODA9_OPTION_MVC_PREFIX_NAL_OFFSET 9 #define CODA7_OPTION_GAMMA_OFFSET 8 +#define CODA9_OPTION_MVC_PARASET_REFRESH_OFFSET 8 #define CODA7_OPTION_RCQPMAX_OFFSET 7 +#define CODA9_OPTION_GAMMA_OFFSET 7 #define CODADX6_OPTION_GAMMA_OFFSET 7 #define CODA7_OPTION_RCQPMIN_OFFSET 6 +#define CODA9_OPTION_RCQPMAX_OFFSET 6 #define CODA_OPTION_LIMITQP_OFFSET 6 #define CODA_OPTION_RCINTRAQP_OFFSET 5 #define CODA_OPTION_FMO_OFFSET 4 +#define CODA9_OPTION_MVC_INTERVIEW_OFFSET 4 #define CODA_OPTION_AVC_AUD_OFFSET 2 #define CODA_OPTION_SLICEREPORT_OFFSET 1 #define CODA_CMD_ENC_SEQ_COD_STD 0x18c #define CODA_STD_MPEG4 0 +#define CODA9_STD_H264 0 #define CODA_STD_H263 1 #define CODA_STD_H264 2 #define CODA_STD_MJPG 3 +#define CODA9_STD_MPEG4 3 + #define CODA_CMD_ENC_SEQ_SRC_SIZE 0x190 #define CODA7_PICWIDTH_OFFSET 16 #define CODA7_PICWIDTH_MASK 0xffff @@ -268,15 +319,26 @@ #define CODA7_CMD_ENC_SEQ_SEARCH_BASE 0x1b8 #define CODA7_CMD_ENC_SEQ_SEARCH_SIZE 0x1bc #define CODA7_CMD_ENC_SEQ_INTRA_QP 0x1c4 -#define CODA_CMD_ENC_SEQ_RC_QP_MAX 0x1c8 +#define CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX 0x1c8 +#define CODA_QPMIN_OFFSET 8 +#define CODA_QPMIN_MASK 0x3f #define CODA_QPMAX_OFFSET 0 #define CODA_QPMAX_MASK 0x3f #define CODA_CMD_ENC_SEQ_RC_GAMMA 0x1cc #define CODA_GAMMA_OFFSET 0 #define CODA_GAMMA_MASK 0xffff +#define CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE 0x1d0 +#define CODA9_CMD_ENC_SEQ_INTRA_WEIGHT 0x1d4 +#define CODA9_CMD_ENC_SEQ_ME_OPTION 0x1d8 #define CODA_RET_ENC_SEQ_SUCCESS 0x1c0 /* Encoder Picture Run */ +#define CODA9_CMD_ENC_PIC_SRC_INDEX 0x180 +#define CODA9_CMD_ENC_PIC_SRC_STRIDE 0x184 +#define CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC 0x1a4 +#define CODA9_CMD_ENC_PIC_SRC_ADDR_Y 0x1a8 +#define CODA9_CMD_ENC_PIC_SRC_ADDR_CB 0x1ac +#define CODA9_CMD_ENC_PIC_SRC_ADDR_CR 0x1b0 #define CODA_CMD_ENC_PIC_SRC_ADDR_Y 0x180 #define CODA_CMD_ENC_PIC_SRC_ADDR_CB 0x184 #define CODA_CMD_ENC_PIC_SRC_ADDR_CR 0x188 @@ -291,7 +353,11 @@ #define CODA_MIR_VER (0x1 << 2) #define CODA_MIR_HOR (0x2 << 2) #define CODA_MIR_VER_HOR (0x3 << 2) -#define CODA_CMD_ENC_PIC_OPTION 0x194 +#define CODA_CMD_ENC_PIC_OPTION 0x194 +#define CODA_FORCE_IPICTURE BIT(1) +#define CODA_REPORT_MB_INFO BIT(3) +#define CODA_REPORT_MV_INFO BIT(4) +#define CODA_REPORT_SLICE_INFO BIT(5) #define CODA_CMD_ENC_PIC_BB_START 0x198 #define CODA_CMD_ENC_PIC_BB_SIZE 0x19c #define CODA_RET_ENC_FRAME_NUM 0x1c0 @@ -306,13 +372,30 @@ #define CODA_CMD_SET_FRAME_BUF_STRIDE 0x184 #define CODA_CMD_SET_FRAME_SLICE_BB_START 0x188 #define CODA_CMD_SET_FRAME_SLICE_BB_SIZE 0x18c +#define CODA9_CMD_SET_FRAME_SUBSAMP_A 0x188 +#define CODA9_CMD_SET_FRAME_SUBSAMP_B 0x18c #define CODA7_CMD_SET_FRAME_AXI_BIT_ADDR 0x190 #define CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR 0x194 #define CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR 0x198 #define CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR 0x19c #define CODA7_CMD_SET_FRAME_AXI_OVL_ADDR 0x1a0 #define CODA7_CMD_SET_FRAME_MAX_DEC_SIZE 0x1a4 +#define CODA9_CMD_SET_FRAME_AXI_BTP_ADDR 0x1a4 #define CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8 +#define CODA9_CMD_SET_FRAME_CACHE_SIZE 0x1a8 +#define CODA9_CMD_SET_FRAME_CACHE_CONFIG 0x1ac +#define CODA9_CACHE_BYPASS_OFFSET 28 +#define CODA9_CACHE_DUALCONF_OFFSET 26 +#define CODA9_CACHE_PAGEMERGE_OFFSET 24 +#define CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET 16 +#define CODA9_CACHE_CB_BUFFER_SIZE_OFFSET 8 +#define CODA9_CACHE_CR_BUFFER_SIZE_OFFSET 0 +#define CODA9_CMD_SET_FRAME_SUBSAMP_A_MVC 0x1b0 +#define CODA9_CMD_SET_FRAME_SUBSAMP_B_MVC 0x1b4 +#define CODA9_CMD_SET_FRAME_DP_BUF_BASE 0x1b0 +#define CODA9_CMD_SET_FRAME_DP_BUF_SIZE 0x1b4 +#define CODA9_CMD_SET_FRAME_MAX_DEC_SIZE 0x1b8 +#define CODA9_CMD_SET_FRAME_DELAY 0x1bc /* Encoder Header */ #define CODA_CMD_ENC_HEADER_CODE 0x180 @@ -322,8 +405,11 @@ #define CODA_HEADER_MP4V_VOL 0 #define CODA_HEADER_MP4V_VOS 1 #define CODA_HEADER_MP4V_VIS 2 +#define CODA9_HEADER_FRAME_CROP (1 << 3) #define CODA_CMD_ENC_HEADER_BB_START 0x184 #define CODA_CMD_ENC_HEADER_BB_SIZE 0x188 +#define CODA9_CMD_ENC_HEADER_FRAME_CROP_H 0x18c +#define CODA9_CMD_ENC_HEADER_FRAME_CROP_V 0x190 /* Get Version */ #define CODA_CMD_FIRMWARE_VERNUM 0x1c0 @@ -334,5 +420,28 @@ #define CODA_FIRMWARE_VERNUM(product, major, minor, release) \ ((product) << 16 | ((major) << 12) | \ ((minor) << 8) | (release)) +#define CODA9_CMD_FIRMWARE_CODE_REV 0x1c4 + +#define CODA9_GDMA_BASE 0x1000 +#define CODA9_GDI_WPROT_ERR_CLR (CODA9_GDMA_BASE + 0x0a0) +#define CODA9_GDI_WPROT_RGN_EN (CODA9_GDMA_BASE + 0x0ac) + +#define CODA9_GDI_BUS_CTRL (CODA9_GDMA_BASE + 0x0f0) +#define CODA9_GDI_BUS_STATUS (CODA9_GDMA_BASE + 0x0f4) + +#define CODA9_GDI_XY2_CAS_0 (CODA9_GDMA_BASE + 0x800) +#define CODA9_GDI_XY2_CAS_F (CODA9_GDMA_BASE + 0x83c) + +#define CODA9_GDI_XY2_BA_0 (CODA9_GDMA_BASE + 0x840) +#define CODA9_GDI_XY2_BA_1 (CODA9_GDMA_BASE + 0x844) +#define CODA9_GDI_XY2_BA_2 (CODA9_GDMA_BASE + 0x848) +#define CODA9_GDI_XY2_BA_3 (CODA9_GDMA_BASE + 0x84c) + +#define CODA9_GDI_XY2_RAS_0 (CODA9_GDMA_BASE + 0x850) +#define CODA9_GDI_XY2_RAS_F (CODA9_GDMA_BASE + 0x88c) + +#define CODA9_GDI_XY2_RBC_CONFIG (CODA9_GDMA_BASE + 0x890) +#define CODA9_GDI_RBC2_AXI_0 (CODA9_GDMA_BASE + 0x8a0) +#define CODA9_GDI_RBC2_AXI_1F (CODA9_GDMA_BASE + 0x91c) #endif -- cgit v0.10.2 From b9736299c427d5b3df7324e4469f9ce46793f943 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:18 -0300 Subject: [media] coda: remove BUG() in get_q_data This allows us to get rid of the now superfluous v4l2_m2m_get_vq check in G_FMT. Also, we can use this to check the buffer type in G_SELECTION later. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 6671494..f1b16df 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -326,9 +326,8 @@ static struct coda_q_data *get_q_data(struct coda_ctx *ctx, case V4L2_BUF_TYPE_VIDEO_CAPTURE: return &(ctx->q_data[V4L2_M2M_DST]); default: - BUG(); + return NULL; } - return NULL; } /* @@ -571,15 +570,12 @@ static int coda_enum_fmt_vid_out(struct file *file, void *priv, static int coda_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { - struct vb2_queue *vq; struct coda_q_data *q_data; struct coda_ctx *ctx = fh_to_ctx(priv); - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - if (!vq) - return -EINVAL; - q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.pixelformat = q_data->fourcc; @@ -628,6 +624,8 @@ static int coda_try_fmt(struct coda_ctx *ctx, struct coda_codec *codec, break; default: q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; f->fmt.pix.pixelformat = q_data->fourcc; } -- cgit v0.10.2 From 52c41676f8a317724fbd603646d512278cb00653 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:19 -0300 Subject: [media] coda: add selection API support for h.264 decoder The h.264 decoder produces capture frames that are a multiple of the macroblock size (16 pixels). To inform userspace about invalid pixel data at the edges, use the active and padded composing rectangles on the capture queue. The cropping information is obtained from the h.264 sequence parameter set. Reviewed-by: Hans Verkuil Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index f1b16df..0c77366 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -119,6 +119,7 @@ struct coda_q_data { unsigned int height; unsigned int sizeimage; unsigned int fourcc; + struct v4l2_rect rect; }; struct coda_aux_buf { @@ -733,6 +734,10 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) q_data->width = f->fmt.pix.width; q_data->height = f->fmt.pix.height; q_data->sizeimage = f->fmt.pix.sizeimage; + q_data->rect.left = 0; + q_data->rect.top = 0; + q_data->rect.width = f->fmt.pix.width; + q_data->rect.height = f->fmt.pix.height; v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Setting format for type %d, wxh: %dx%d, fmt: %d\n", @@ -869,6 +874,50 @@ static int coda_streamoff(struct file *file, void *priv, return ret; } +static int coda_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_q_data *q_data; + struct v4l2_rect r, *rsel; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + r.left = 0; + r.top = 0; + r.width = q_data->width; + r.height = q_data->height; + rsel = &q_data->rect; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + rsel = &r; + /* fallthrough */ + case V4L2_SEL_TGT_CROP: + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + rsel = &r; + /* fallthrough */ + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + default: + return -EINVAL; + } + + s->r = *rsel; + + return 0; +} + static int coda_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc) { @@ -947,6 +996,8 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_streamon = coda_streamon, .vidioc_streamoff = coda_streamoff, + .vidioc_g_selection = coda_g_selection, + .vidioc_try_decoder_cmd = coda_try_decoder_cmd, .vidioc_decoder_cmd = coda_decoder_cmd, @@ -1502,6 +1553,10 @@ static void set_default_params(struct coda_ctx *ctx) ctx->q_data[V4L2_M2M_DST].width = max_w; ctx->q_data[V4L2_M2M_DST].height = max_h; ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE; + ctx->q_data[V4L2_M2M_SRC].rect.width = max_w; + ctx->q_data[V4L2_M2M_SRC].rect.height = max_h; + ctx->q_data[V4L2_M2M_DST].rect.width = max_w; + ctx->q_data[V4L2_M2M_DST].rect.height = max_h; if (ctx->dev->devtype->product == CODA_960) coda_set_tiled_map_type(ctx, GDI_LINEAR_FRAME_MAP); @@ -2029,6 +2084,21 @@ static int coda_start_decoding(struct coda_ctx *ctx) return -EINVAL; } + if (src_fourcc == V4L2_PIX_FMT_H264) { + u32 left_right; + u32 top_bottom; + + left_right = coda_read(dev, CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT); + top_bottom = coda_read(dev, CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM); + + q_data_dst->rect.left = (left_right >> 10) & 0x3ff; + q_data_dst->rect.top = (top_bottom >> 10) & 0x3ff; + q_data_dst->rect.width = width - q_data_dst->rect.left - + (left_right & 0x3ff); + q_data_dst->rect.height = height - q_data_dst->rect.top - + (top_bottom & 0x3ff); + } + ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc); if (ret < 0) return ret; @@ -2938,6 +3008,30 @@ static void coda_finish_decode(struct coda_ctx *ctx) q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + /* frame crop information */ + if (src_fourcc == V4L2_PIX_FMT_H264) { + u32 left_right; + u32 top_bottom; + + left_right = coda_read(dev, CODA_RET_DEC_PIC_CROP_LEFT_RIGHT); + top_bottom = coda_read(dev, CODA_RET_DEC_PIC_CROP_TOP_BOTTOM); + + if (left_right == 0xffffffff && top_bottom == 0xffffffff) { + /* Keep current crop information */ + } else { + struct v4l2_rect *rect = &q_data_dst->rect; + + rect->left = left_right >> 16 & 0xffff; + rect->top = top_bottom >> 16 & 0xffff; + rect->width = width - rect->left - + (left_right & 0xffff); + rect->height = height - rect->top - + (top_bottom & 0xffff); + } + } else { + /* no cropping */ + } + val = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB); if (val > 0) v4l2_err(&dev->v4l2_dev, -- cgit v0.10.2 From 32b6f20603a10e8e7ebd1bb17586335fa804a6d5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:20 -0300 Subject: [media] coda: add workqueue to serialize hardware commands Using the coda_mutex lock to serialize hardware access would cause "INFO: possible circular locking dependency detected" lockdep warnings. Since the possible locking paths are hard to follow, serialize hardware access with a single workqueue thread. Ultimately the workqueue could be converted to only do register setup and readout for per-command work items. Using the initialized context property, SEQ_END is only queued in coda_release when needed. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 0c77366..45fd7c9 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -147,11 +147,11 @@ struct coda_dev { spinlock_t irqlock; struct mutex dev_mutex; struct mutex coda_mutex; + struct workqueue_struct *workqueue; struct v4l2_m2m_dev *m2m_dev; struct vb2_alloc_ctx *alloc_ctx; struct list_head instances; unsigned long instance_mask; - struct delayed_work timeout; }; struct coda_params { @@ -198,7 +198,9 @@ struct coda_ctx { struct coda_dev *dev; struct mutex buffer_mutex; struct list_head list; - struct work_struct skip_run; + struct work_struct pic_run_work; + struct work_struct seq_end_work; + struct completion completion; int aborting; int initialized; int streamon_out; @@ -1007,13 +1009,6 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { static int coda_start_decoding(struct coda_ctx *ctx); -static void coda_skip_run(struct work_struct *work) -{ - struct coda_ctx *ctx = container_of(work, struct coda_ctx, skip_run); - - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); -} - static inline int coda_get_bitstream_payload(struct coda_ctx *ctx) { return kfifo_len(&ctx->bitstream_fifo); @@ -1168,7 +1163,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "bitstream payload: %d, skipping\n", coda_get_bitstream_payload(ctx)); - schedule_work(&ctx->skip_run); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); return -EAGAIN; } @@ -1177,7 +1172,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) int ret = coda_start_decoding(ctx); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to start decoding\n"); - schedule_work(&ctx->skip_run); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); return -EAGAIN; } else { ctx->initialized = 1; @@ -1385,24 +1380,48 @@ static void coda_device_run(void *m2m_priv) { struct coda_ctx *ctx = m2m_priv; struct coda_dev *dev = ctx->dev; - int ret; + + queue_work(dev->workqueue, &ctx->pic_run_work); +} + +static void coda_free_framebuffers(struct coda_ctx *ctx); +static void coda_free_context_buffers(struct coda_ctx *ctx); + +static void coda_seq_end_work(struct work_struct *work) +{ + struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_end_work); + struct coda_dev *dev = ctx->dev; mutex_lock(&ctx->buffer_mutex); + mutex_lock(&dev->coda_mutex); - /* - * If streamoff dequeued all buffers before we could get the lock, - * just bail out immediately. - */ - if ((!v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) && - ctx->inst_type != CODA_INST_DECODER) || - !v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)) { - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "%d: device_run without buffers\n", ctx->idx); - mutex_unlock(&ctx->buffer_mutex); - schedule_work(&ctx->skip_run); - return; + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "%d: %s: sent command 'SEQ_END' to coda\n", ctx->idx, __func__); + if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) { + v4l2_err(&dev->v4l2_dev, + "CODA_COMMAND_SEQ_END failed\n"); } + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + + coda_free_framebuffers(ctx); + coda_free_context_buffers(ctx); + + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); +} + +static void coda_finish_decode(struct coda_ctx *ctx); +static void coda_finish_encode(struct coda_ctx *ctx); + +static void coda_pic_run_work(struct work_struct *work) +{ + struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work); + struct coda_dev *dev = ctx->dev; + int ret; + + mutex_lock(&ctx->buffer_mutex); mutex_lock(&dev->coda_mutex); if (ctx->inst_type == CODA_INST_DECODER) { @@ -1421,12 +1440,26 @@ static void coda_device_run(void *m2m_priv) coda_write(dev, ctx->iram_info.axi_sram_use, CODA7_REG_BIT_AXI_SRAM_USE); - /* 1 second timeout in case CODA locks up */ - schedule_delayed_work(&dev->timeout, HZ); - if (ctx->inst_type == CODA_INST_DECODER) coda_kfifo_sync_to_device_full(ctx); coda_command_async(ctx, CODA_COMMAND_PIC_RUN); + + if (!wait_for_completion_timeout(&ctx->completion, msecs_to_jiffies(1000))) { + dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout\n"); + } else if (!ctx->aborting) { + if (ctx->inst_type == CODA_INST_DECODER) + coda_finish_decode(ctx); + else + coda_finish_encode(ctx); + } + + if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) + queue_work(dev->workqueue, &ctx->seq_end_work); + + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); + + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); } static int coda_job_ready(void *m2m_priv) @@ -2786,7 +2819,9 @@ static int coda_open(struct file *file) } set_bit(idx, &dev->instance_mask); - INIT_WORK(&ctx->skip_run, coda_skip_run); + init_completion(&ctx->completion); + INIT_WORK(&ctx->pic_run_work, coda_pic_run_work); + INIT_WORK(&ctx->seq_end_work, coda_seq_end_work); v4l2_fh_init(&ctx->fh, video_devdata(file)); file->private_data = &ctx->fh; v4l2_fh_add(&ctx->fh); @@ -2889,16 +2924,10 @@ static int coda_release(struct file *file) v4l2_m2m_ctx_release(ctx->m2m_ctx); /* In case the instance was not running, we still need to call SEQ_END */ - mutex_lock(&dev->coda_mutex); - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "%s: sent command 'SEQ_END' to coda\n", __func__); - if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) { - v4l2_err(&dev->v4l2_dev, - "CODA_COMMAND_SEQ_END failed\n"); - mutex_unlock(&dev->coda_mutex); - return -ETIMEDOUT; + if (ctx->initialized) { + queue_work(dev->workqueue, &ctx->seq_end_work); + flush_work(&ctx->seq_end_work); } - mutex_unlock(&dev->coda_mutex); coda_free_framebuffers(ctx); @@ -3196,8 +3225,6 @@ static irqreturn_t coda_irq_handler(int irq, void *data) struct coda_dev *dev = data; struct coda_ctx *ctx; - cancel_delayed_work(&dev->timeout); - /* read status register to attend the IRQ */ coda_read(dev, CODA_REG_BIT_INT_STATUS); coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET, @@ -3213,7 +3240,6 @@ static irqreturn_t coda_irq_handler(int irq, void *data) if (ctx->aborting) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "task has been aborted\n"); - goto out; } if (coda_isbusy(ctx->dev)) { @@ -3222,57 +3248,11 @@ static irqreturn_t coda_irq_handler(int irq, void *data) return IRQ_NONE; } - if (ctx->inst_type == CODA_INST_DECODER) - coda_finish_decode(ctx); - else - coda_finish_encode(ctx); - -out: - if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) { - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, - "%s: sent command 'SEQ_END' to coda\n", __func__); - if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) { - v4l2_err(&dev->v4l2_dev, - "CODA_COMMAND_SEQ_END failed\n"); - } - - kfifo_init(&ctx->bitstream_fifo, - ctx->bitstream.vaddr, ctx->bitstream.size); - - coda_free_framebuffers(ctx); - coda_free_context_buffers(ctx); - } - - mutex_unlock(&dev->coda_mutex); - mutex_unlock(&ctx->buffer_mutex); - - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); + complete(&ctx->completion); return IRQ_HANDLED; } -static void coda_timeout(struct work_struct *work) -{ - struct coda_ctx *ctx; - struct coda_dev *dev = container_of(to_delayed_work(work), - struct coda_dev, timeout); - - dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout, stopping all streams\n"); - - mutex_lock(&dev->dev_mutex); - list_for_each_entry(ctx, &dev->instances, list) { - if (mutex_is_locked(&ctx->buffer_mutex)) - mutex_unlock(&ctx->buffer_mutex); - v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - } - mutex_unlock(&dev->dev_mutex); - - mutex_unlock(&dev->coda_mutex); - ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); -} - static u32 coda_supported_firmwares[] = { CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5), CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), @@ -3576,7 +3556,6 @@ static int coda_probe(struct platform_device *pdev) spin_lock_init(&dev->irqlock); INIT_LIST_HEAD(&dev->instances); - INIT_DELAYED_WORK(&dev->timeout, coda_timeout); dev->plat_dev = pdev; dev->clk_per = devm_clk_get(&pdev->dev, "per"); @@ -3683,6 +3662,12 @@ static int coda_probe(struct platform_device *pdev) return -ENOMEM; } + dev->workqueue = alloc_workqueue("coda", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!dev->workqueue) { + dev_err(&pdev->dev, "unable to alloc workqueue\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, dev); return coda_firmware_request(dev); @@ -3698,6 +3683,7 @@ static int coda_remove(struct platform_device *pdev) if (dev->alloc_ctx) vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); v4l2_device_unregister(&dev->v4l2_dev); + destroy_workqueue(dev->workqueue); if (dev->iram.vaddr) gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr, dev->iram.size); -- cgit v0.10.2 From 152ea1c81c5608cfe9e902b2dbe1539729bff9f2 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:21 -0300 Subject: [media] coda: Use mem-to-mem ioctl helpers Use the mem2mem helpers introduced to get rid of some duplicated code. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 45fd7c9..6811703 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -778,22 +778,6 @@ static int coda_s_fmt_vid_out(struct file *file, void *priv, return ret; } -static int coda_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int coda_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - static int coda_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { @@ -802,14 +786,6 @@ static int coda_qbuf(struct file *file, void *priv, return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); } -static int coda_expbuf(struct file *file, void *priv, - struct v4l2_exportbuffer *eb) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); -} - static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx, struct v4l2_buffer *buf) { @@ -842,40 +818,6 @@ static int coda_dqbuf(struct file *file, void *priv, return ret; } -static int coda_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *create) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_create_bufs(file, ctx->m2m_ctx, create); -} - -static int coda_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int coda_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct coda_ctx *ctx = fh_to_ctx(priv); - int ret; - - /* - * This indirectly calls __vb2_queue_cancel, which dequeues all buffers. - * We therefore have to lock it against running hardware in this context, - * which still needs the buffers. - */ - mutex_lock(&ctx->buffer_mutex); - ret = v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); - mutex_unlock(&ctx->buffer_mutex); - - return ret; -} - static int coda_g_selection(struct file *file, void *fh, struct v4l2_selection *s) { @@ -987,16 +929,16 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out, .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out, - .vidioc_reqbufs = coda_reqbufs, - .vidioc_querybuf = coda_querybuf, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, .vidioc_qbuf = coda_qbuf, - .vidioc_expbuf = coda_expbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, .vidioc_dqbuf = coda_dqbuf, - .vidioc_create_bufs = coda_create_bufs, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_streamon = coda_streamon, - .vidioc_streamoff = coda_streamoff, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, .vidioc_g_selection = coda_g_selection, @@ -1675,18 +1617,6 @@ static void coda_buf_queue(struct vb2_buffer *vb) } } -static void coda_wait_prepare(struct vb2_queue *q) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(q); - coda_unlock(ctx); -} - -static void coda_wait_finish(struct vb2_queue *q) -{ - struct coda_ctx *ctx = vb2_get_drv_priv(q); - coda_lock(ctx); -} - static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) { struct coda_dev *dev = ctx->dev; @@ -2648,10 +2578,10 @@ static struct vb2_ops coda_qops = { .queue_setup = coda_queue_setup, .buf_prepare = coda_buf_prepare, .buf_queue = coda_buf_queue, - .wait_prepare = coda_wait_prepare, - .wait_finish = coda_wait_finish, .start_streaming = coda_start_streaming, .stop_streaming = coda_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int coda_s_ctrl(struct v4l2_ctrl *ctrl) @@ -2774,6 +2704,7 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &coda_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->dev_mutex; ret = vb2_queue_init(src_vq); if (ret) @@ -2786,6 +2717,7 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &coda_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->dev_mutex; return vb2_queue_init(dst_vq); } @@ -2854,6 +2786,8 @@ static int coda_open(struct file *file) __func__, ret); goto err_ctx_init; } + ctx->fh.m2m_ctx = ctx->m2m_ctx; + ret = coda_ctrls_setup(ctx); if (ret) { v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n"); @@ -2953,32 +2887,13 @@ static int coda_release(struct file *file) return 0; } -static unsigned int coda_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct coda_ctx *ctx = fh_to_ctx(file->private_data); - int ret; - - coda_lock(ctx); - ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); - coda_unlock(ctx); - return ret; -} - -static int coda_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct coda_ctx *ctx = fh_to_ctx(file->private_data); - - return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); -} - static const struct v4l2_file_operations coda_fops = { .owner = THIS_MODULE, .open = coda_open, .release = coda_release, - .poll = coda_poll, + .poll = v4l2_m2m_fop_poll, .unlocked_ioctl = video_ioctl2, - .mmap = coda_mmap, + .mmap = v4l2_m2m_fop_mmap, }; static void coda_finish_decode(struct coda_ctx *ctx) -- cgit v0.10.2 From 14604e3a4d2c435ce4f1c95d952f464315c635f4 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:22 -0300 Subject: [media] coda: use ctx->fh.m2m_ctx instead of ctx->m2m_ctx v4l2_fh already contains a mem2mem context pointer. Use it. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 6811703..782b09e 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -213,7 +213,6 @@ struct coda_ctx { struct coda_codec *codec; enum v4l2_colorspace colorspace; struct coda_params params; - struct v4l2_m2m_ctx *m2m_ctx; struct v4l2_ctrl_handler ctrls; struct v4l2_fh fh; int gopcounter; @@ -553,7 +552,7 @@ static int coda_enum_fmt_vid_cap(struct file *file, void *priv, struct coda_q_data *q_data_src; /* If the source format is already fixed, only list matching formats */ - src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); if (vb2_is_streaming(src_vq)) { q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); @@ -665,7 +664,7 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv, * If the source format is already fixed, try to find a codec that * converts to the given destination format */ - src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); if (vb2_is_streaming(src_vq)) { struct coda_q_data *q_data_src; @@ -719,7 +718,7 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) struct coda_q_data *q_data; struct vb2_queue *vq; - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) return -EINVAL; @@ -783,7 +782,7 @@ static int coda_qbuf(struct file *file, void *priv, { struct coda_ctx *ctx = fh_to_ctx(priv); - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); + return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf); } static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx, @@ -791,7 +790,7 @@ static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx, { struct vb2_queue *src_vq; - src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) && (buf->sequence == (ctx->qsequence - 1))); @@ -803,7 +802,7 @@ static int coda_dqbuf(struct file *file, void *priv, struct coda_ctx *ctx = fh_to_ctx(priv); int ret; - ret = v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); + ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf); /* If this is the last capture buffer, emit an end-of-stream event */ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && @@ -1040,11 +1039,11 @@ static void coda_fill_bitstream(struct coda_ctx *ctx) { struct vb2_buffer *src_buf; - while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) { - src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); if (coda_bitstream_try_queue(ctx, src_buf)) { - src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); } else { break; @@ -1084,7 +1083,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) u32 stridey, height; u32 picture_y, picture_cb, picture_cr; - dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); if (ctx->params.rot_mode & CODA_ROT_90) { @@ -1105,7 +1104,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "bitstream payload: %d, skipping\n", coda_get_bitstream_payload(ctx)); - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); return -EAGAIN; } @@ -1114,7 +1113,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) int ret = coda_start_decoding(ctx); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to start decoding\n"); - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); return -EAGAIN; } else { ctx->initialized = 1; @@ -1187,8 +1186,8 @@ static void coda_prepare_encode(struct coda_ctx *ctx) u32 pic_stream_buffer_addr, pic_stream_buffer_size; u32 dst_fourcc; - src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); dst_fourcc = q_data_dst->fourcc; @@ -1401,7 +1400,7 @@ static void coda_pic_run_work(struct work_struct *work) mutex_unlock(&dev->coda_mutex); mutex_unlock(&ctx->buffer_mutex); - v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); } static int coda_job_ready(void *m2m_priv) @@ -1413,14 +1412,14 @@ static int coda_job_ready(void *m2m_priv) * and 1 frame are needed. In the decoder case, * the compressed frame can be in the bitstream. */ - if (!v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) && + if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) && ctx->inst_type != CODA_INST_DECODER) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "not ready: not enough video buffers.\n"); return 0; } - if (!v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)) { + if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "not ready: not enough video capture buffers.\n"); return 0; @@ -1609,11 +1608,11 @@ static void coda_buf_queue(struct vb2_buffer *vb) } } mutex_lock(&ctx->bitstream_mutex); - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); coda_fill_bitstream(ctx); mutex_unlock(&ctx->bitstream_mutex); } else { - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); } } @@ -2210,7 +2209,7 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) /* Allow decoder device_run with no new buffers queued */ if (ctx->inst_type == CODA_INST_DECODER) - v4l2_m2m_set_src_buffered(ctx->m2m_ctx, true); + v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true); ctx->gopcounter = ctx->params.gop_size - 1; q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); @@ -2258,7 +2257,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); dst_fourcc = q_data_dst->fourcc; - buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0); bitstream_size = q_data_dst->sizeimage; @@ -2469,7 +2468,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) } /* Save stream headers */ - buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); switch (dst_fourcc) { case V4L2_PIX_FMT_H264: /* @@ -2777,16 +2776,15 @@ static int coda_open(struct file *file) goto err_clk_ahb; set_default_params(ctx); - ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &coda_queue_init); - if (IS_ERR(ctx->m2m_ctx)) { - ret = PTR_ERR(ctx->m2m_ctx); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n", __func__, ret); goto err_ctx_init; } - ctx->fh.m2m_ctx = ctx->m2m_ctx; ret = coda_ctrls_setup(ctx); if (ret) { @@ -2832,7 +2830,7 @@ err_dma_writecombine: err_dma_alloc: v4l2_ctrl_handler_free(&ctx->ctrls); err_ctrls_setup: - v4l2_m2m_ctx_release(ctx->m2m_ctx); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); err_ctx_init: clk_disable_unprepare(dev->clk_ahb); err_clk_ahb: @@ -2855,7 +2853,7 @@ static int coda_release(struct file *file) ctx); /* If this instance is running, call .job_abort and wait for it to end */ - v4l2_m2m_ctx_release(ctx->m2m_ctx); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); /* In case the instance was not running, we still need to call SEQ_END */ if (ctx->initialized) { @@ -2909,7 +2907,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) int success; u32 val; - dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); /* Update kfifo out pointer from coda bitstream read pointer */ coda_kfifo_sync_from_device(ctx); @@ -3049,7 +3047,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) /* If a frame was copied out, return it */ if (ctx->display_idx >= 0 && ctx->display_idx < ctx->num_internal_frames) { - dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); dst_buf->v4l2_buf.sequence = ctx->osequence++; dst_buf->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME | @@ -3082,8 +3080,8 @@ static void coda_finish_encode(struct coda_ctx *ctx) struct coda_dev *dev = ctx->dev; u32 wr_ptr, start_ptr; - src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); /* Get results from the coda */ start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START); @@ -3121,7 +3119,7 @@ static void coda_finish_encode(struct coda_ctx *ctx) v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); ctx->gopcounter--; -- cgit v0.10.2 From 1e17273112d60ca93e1248715039f9bf1aa09733 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:23 -0300 Subject: [media] coda: Add runtime pm support This patch allows to use the runtime pm and generic pm domain frameworks to completely gate power to the VPU if it is unused. This functionality is available on i.MX6. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 782b09e..131cb5f 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -2767,6 +2768,13 @@ static int coda_open(struct file *file) ctx->reg_idx = idx; } + /* Power up and upload firmware if necessary */ + ret = pm_runtime_get_sync(&dev->plat_dev->dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret); + goto err_pm_get; + } + ret = clk_prepare_enable(dev->clk_per); if (ret) goto err_clk_per; @@ -2836,6 +2844,8 @@ err_ctx_init: err_clk_ahb: clk_disable_unprepare(dev->clk_per); err_clk_per: + pm_runtime_put_sync(&dev->plat_dev->dev); +err_pm_get: v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); clear_bit(ctx->idx, &dev->instance_mask); @@ -2877,6 +2887,7 @@ static int coda_release(struct file *file) v4l2_ctrl_handler_free(&ctx->ctrls); clk_disable_unprepare(dev->clk_ahb); clk_disable_unprepare(dev->clk_per); + pm_runtime_put_sync(&dev->plat_dev->dev); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); clear_bit(ctx->idx, &dev->instance_mask); @@ -3191,7 +3202,7 @@ static int coda_hw_init(struct coda_dev *dev) ret = clk_prepare_enable(dev->clk_per); if (ret) - return ret; + goto err_clk_per; ret = clk_prepare_enable(dev->clk_ahb); if (ret) @@ -3317,6 +3328,7 @@ static int coda_hw_init(struct coda_dev *dev) err_clk_ahb: clk_disable_unprepare(dev->clk_per); +err_clk_per: return ret; } @@ -3342,10 +3354,29 @@ static void coda_fw_callback(const struct firmware *fw, void *context) memcpy(dev->codebuf.vaddr, fw->data, fw->size); release_firmware(fw); - ret = coda_hw_init(dev); - if (ret) { - v4l2_err(&dev->v4l2_dev, "HW initialization failed\n"); - return; + if (pm_runtime_enabled(&pdev->dev) && pdev->dev.pm_domain) { + /* + * Enabling power temporarily will cause coda_hw_init to be + * called via coda_runtime_resume by the pm domain. + */ + ret = pm_runtime_get_sync(&dev->plat_dev->dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to power on: %d\n", + ret); + return; + } + + pm_runtime_put_sync(&dev->plat_dev->dev); + } else { + /* + * If runtime pm is disabled or pm_domain is not set, + * initialize once manually. + */ + ret = coda_hw_init(dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "HW initialization failed\n"); + return; + } } dev->vfd.fops = &coda_fops, @@ -3583,6 +3614,8 @@ static int coda_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); + pm_runtime_enable(&pdev->dev); + return coda_firmware_request(dev); } @@ -3593,6 +3626,7 @@ static int coda_remove(struct platform_device *pdev) video_unregister_device(&dev->vfd); if (dev->m2m_dev) v4l2_m2m_release(dev->m2m_dev); + pm_runtime_disable(&pdev->dev); if (dev->alloc_ctx) vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); v4l2_device_unregister(&dev->v4l2_dev); @@ -3606,6 +3640,26 @@ static int coda_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_RUNTIME +static int coda_runtime_resume(struct device *dev) +{ + struct coda_dev *cdev = dev_get_drvdata(dev); + int ret = 0; + + if (dev->pm_domain) { + ret = coda_hw_init(cdev); + if (ret) + v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n"); + } + + return ret; +} +#endif + +static const struct dev_pm_ops coda_pm_ops = { + SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL) +}; + static struct platform_driver coda_driver = { .probe = coda_probe, .remove = coda_remove, @@ -3613,6 +3667,7 @@ static struct platform_driver coda_driver = { .name = CODA_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(coda_dt_ids), + .pm = &coda_pm_ops, }, .id_table = coda_platform_ids, }; -- cgit v0.10.2 From fe7554e6210090d0d8a8dff6b7f0082fdf1ddbf1 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:24 -0300 Subject: [media] coda: split firmware version check out of coda_hw_init This adds a new function coda_check_firmware that does the firmware version checks so that this can be done only once from coda_probe instead of every time the runtime pm framework resumes the coda. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 131cb5f..87cc22606 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -3195,7 +3195,6 @@ static bool coda_firmware_supported(u32 vernum) static int coda_hw_init(struct coda_dev *dev) { - u16 product, major, minor, release; u32 data; u16 *p; int i, ret; @@ -3276,17 +3275,40 @@ static int coda_hw_init(struct coda_dev *dev) coda_write(dev, data, CODA_REG_BIT_CODE_RESET); coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); - /* Load firmware */ + clk_disable_unprepare(dev->clk_ahb); + clk_disable_unprepare(dev->clk_per); + + return 0; + +err_clk_ahb: + clk_disable_unprepare(dev->clk_per); +err_clk_per: + return ret; +} + +static int coda_check_firmware(struct coda_dev *dev) +{ + u16 product, major, minor, release; + u32 data; + int ret; + + ret = clk_prepare_enable(dev->clk_per); + if (ret) + goto err_clk_per; + + ret = clk_prepare_enable(dev->clk_ahb); + if (ret) + goto err_clk_ahb; + coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM); coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX); coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD); coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND); if (coda_wait_timeout(dev)) { - clk_disable_unprepare(dev->clk_per); - clk_disable_unprepare(dev->clk_ahb); v4l2_err(&dev->v4l2_dev, "firmware get command error\n"); - return -EIO; + ret = -EIO; + goto err_run_cmd; } if (dev->devtype->product == CODA_960) { @@ -3326,6 +3348,8 @@ static int coda_hw_init(struct coda_dev *dev) return 0; +err_run_cmd: + clk_disable_unprepare(dev->clk_ahb); err_clk_ahb: clk_disable_unprepare(dev->clk_per); err_clk_per: @@ -3366,6 +3390,10 @@ static void coda_fw_callback(const struct firmware *fw, void *context) return; } + ret = coda_check_firmware(dev); + if (ret < 0) + return; + pm_runtime_put_sync(&dev->plat_dev->dev); } else { /* @@ -3377,6 +3405,10 @@ static void coda_fw_callback(const struct firmware *fw, void *context) v4l2_err(&dev->v4l2_dev, "HW initialization failed\n"); return; } + + ret = coda_check_firmware(dev); + if (ret < 0) + return; } dev->vfd.fops = &coda_fops, -- cgit v0.10.2 From 39b06acc5f44a261af7cb88c3f7db4bf4da55d0e Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:25 -0300 Subject: [media] coda: select GENERIC_ALLOCATOR The driver uses the genalloc API, which doesn't have stubs in case GENERIC_ALLOCATOR is disabled. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 49e8c0a..433f0bf 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -143,6 +143,7 @@ config VIDEO_CODA select SRAM select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV + select GENERIC_ALLOCATOR ---help--- Coda is a range of video codec IPs that supports H.264, MPEG-4, and other video formats. -- cgit v0.10.2 From 1a5567e3e011ad7eaf3783431c2e14d6674d76ba Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:26 -0300 Subject: [media] coda: add h.264 min/max qp controls If the bitrate control is set, the encoder works in CBR mode, dynamically changing the quantization parameters to achieve a constant bitrate. With the min/max QP controls the quantization parameters can be limited to a given range. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 87cc22606..c4a6cab 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -159,6 +159,8 @@ struct coda_params { u8 rot_mode; u8 h264_intra_qp; u8 h264_inter_qp; + u8 h264_min_qp; + u8 h264_max_qp; u8 mpeg4_intra_qp; u8 mpeg4_inter_qp; u8 gop_size; @@ -2379,7 +2381,16 @@ static int coda_start_encoding(struct coda_ctx *ctx) coda_write(dev, (gamma & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET, CODA_CMD_ENC_SEQ_RC_GAMMA); } + + if (ctx->params.h264_min_qp || ctx->params.h264_max_qp) { + coda_write(dev, + ctx->params.h264_min_qp << CODA_QPMIN_OFFSET | + ctx->params.h264_max_qp << CODA_QPMAX_OFFSET, + CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX); + } if (dev->devtype->product == CODA_960) { + if (ctx->params.h264_max_qp) + value |= 1 << CODA9_OPTION_RCQPMAX_OFFSET; if (CODA_DEFAULT_GAMMA > 0) value |= 1 << CODA9_OPTION_GAMMA_OFFSET; } else { @@ -2389,6 +2400,10 @@ static int coda_start_encoding(struct coda_ctx *ctx) else value |= 1 << CODA7_OPTION_GAMMA_OFFSET; } + if (ctx->params.h264_min_qp) + value |= 1 << CODA7_OPTION_RCQPMIN_OFFSET; + if (ctx->params.h264_max_qp) + value |= 1 << CODA7_OPTION_RCQPMAX_OFFSET; } coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); @@ -2617,6 +2632,12 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: ctx->params.h264_inter_qp = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + ctx->params.h264_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + ctx->params.h264_max_qp = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: ctx->params.mpeg4_intra_qp = ctrl->val; break; @@ -2664,6 +2685,12 @@ static int coda_ctrls_setup(struct coda_ctx *ctx) V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25); + if (ctx->dev->devtype->product != CODA_960) { + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12); + } + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, -- cgit v0.10.2 From de23b1db6567e69cd2aa04ade029e50ffc1dc7e2 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:27 -0300 Subject: [media] coda: add h.264 deblocking filter controls This adds controls for the h.264 deblocking loop filter. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index c4a6cab..e675cd7 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -161,6 +161,9 @@ struct coda_params { u8 h264_inter_qp; u8 h264_min_qp; u8 h264_max_qp; + u8 h264_deblk_enabled; + u8 h264_deblk_alpha; + u8 h264_deblk_beta; u8 mpeg4_intra_qp; u8 mpeg4_inter_qp; u8 gop_size; @@ -2326,7 +2329,17 @@ static int coda_start_encoding(struct coda_ctx *ctx) coda_write(dev, CODA9_STD_H264, CODA_CMD_ENC_SEQ_COD_STD); else coda_write(dev, CODA_STD_H264, CODA_CMD_ENC_SEQ_COD_STD); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_264_PARA); + if (ctx->params.h264_deblk_enabled) { + value = ((ctx->params.h264_deblk_alpha & + CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK) << + CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) | + ((ctx->params.h264_deblk_beta & + CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) << + CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET); + } else { + value = 1 << CODA_264PARAM_DISABLEDEBLK_OFFSET; + } + coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA); break; default: v4l2_err(v4l2_dev, @@ -2638,6 +2651,16 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: ctx->params.h264_max_qp = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + ctx->params.h264_deblk_alpha = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + ctx->params.h264_deblk_beta = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + ctx->params.h264_deblk_enabled = (ctrl->val == + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); + break; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: ctx->params.mpeg4_intra_qp = ctrl->val; break; @@ -2692,6 +2715,14 @@ static int coda_ctrls_setup(struct coda_ctx *ctx) v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, 0, 15, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, 0, 15, 1, 0); + v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED, 0x0, + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2); -- cgit v0.10.2 From f38f79d5fd21ab7585c9bff21afeaa6eb9285ff2 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:28 -0300 Subject: [media] coda: add cyclic intra refresh control Allow userspace to enable cyclic intra refresh by setting the number of intra macroblocks per frame to a non-zero value. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index e675cd7..43349d9 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -167,6 +167,7 @@ struct coda_params { u8 mpeg4_intra_qp; u8 mpeg4_inter_qp; u8 gop_size; + int intra_refresh; int codec_mode; int codec_mode_aux; enum v4l2_mpeg_video_multi_slice_mode slice_mode; @@ -2379,7 +2380,8 @@ static int coda_start_encoding(struct coda_ctx *ctx) coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA); coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_INTRA_REFRESH); + coda_write(dev, ctx->params.intra_refresh, + CODA_CMD_ENC_SEQ_INTRA_REFRESH); coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START); coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE); @@ -2678,6 +2680,9 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_MPEG_VIDEO_HEADER_MODE: break; + case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: + ctx->params.intra_refresh = ctrl->val; + break; default: v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", @@ -2739,6 +2744,8 @@ static int coda_ctrls_setup(struct coda_ctx *ctx) V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE), V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, 1920 * 1088 / 256, 1, 0); if (ctx->ctrls.error) { v4l2_err(&ctx->dev->v4l2_dev, "control initialization error (%d)", -- cgit v0.10.2 From 1190a419e0f89254b831fb2928e1d5d6efe6abe4 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Tue, 22 Jul 2014 09:36:04 -0300 Subject: [media] v4l2-mem2mem: export v4l2_m2m_try_schedule Some drivers might allow to decode remaining frames from an internal ringbuffer after a decoder stop command. Allow those to call v4l2_m2m_try_schedule directly. Signed-off-by: Michael Olbrich Signed-off-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 97defed..80c588f 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -208,7 +208,7 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) * An example of the above could be an instance that requires more than one * src/dst buffer per transaction. */ -static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) +void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) { struct v4l2_m2m_dev *m2m_dev; unsigned long flags_job, flags_out, flags_cap; @@ -274,6 +274,7 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) v4l2_m2m_try_run(m2m_dev); } +EXPORT_SYMBOL_GPL(v4l2_m2m_try_schedule); /** * v4l2_m2m_cancel_job() - cancel pending jobs for the context diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 12ea5a6..c5f3914 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -95,6 +95,8 @@ void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev); struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type); +void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx); + void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, struct v4l2_m2m_ctx *m2m_ctx); -- cgit v0.10.2 From f3497dad92cf2bf713b31b05c6b504a1135cc97c Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Fri, 11 Jul 2014 06:36:30 -0300 Subject: [media] coda: try to schedule a decode run after a stop command In case no further buffers are queued after the stop command, restart job scheduling explicitly. Signed-off-by: Michael Olbrich Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 43349d9..4ce17ac 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -907,6 +907,8 @@ static int coda_decoder_cmd(struct file *file, void *fh, /* If this context is currently running, update the hardware flag */ coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM); } + ctx->prescan_failed = false; + v4l2_m2m_try_schedule(ctx->fh.m2m_ctx); return 0; } -- cgit v0.10.2 From 846ced9c0ebfcb4d42718fcbb487d6e0e52142ec Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:31 -0300 Subject: [media] coda: add decoder timestamp queue The coda driver advertises timestamp_type V4L2_BUF_FLAG_TIMESTAMP_COPY on both queues, so we have to copy timestamps from input v4l2 buffers to the corresponding destination v4l2 buffers. Since the h.264 decoder can reorder frames, a timestamp queue is needed to keep track of and assign the correct timestamp to destination buffers. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 4ce17ac..c7e557a 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -201,6 +201,13 @@ struct gdi_tiled_map { #define GDI_LINEAR_FRAME_MAP 0 }; +struct coda_timestamp { + struct list_head list; + u32 sequence; + struct v4l2_timecode timecode; + struct timeval timestamp; +}; + struct coda_ctx { struct coda_dev *dev; struct mutex buffer_mutex; @@ -235,6 +242,8 @@ struct coda_ctx { struct coda_aux_buf slicebuf; struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS]; u32 frame_types[CODA_MAX_FRAMEBUFFERS]; + struct coda_timestamp frame_timestamps[CODA_MAX_FRAMEBUFFERS]; + struct list_head timestamp_list; struct coda_aux_buf workbuf; int num_internal_frames; int idx; @@ -1011,7 +1020,7 @@ static int coda_bitstream_queue(struct coda_ctx *ctx, struct vb2_buffer *src_buf dma_sync_single_for_device(&ctx->dev->plat_dev->dev, ctx->bitstream.paddr, ctx->bitstream.size, DMA_TO_DEVICE); - ctx->qsequence++; + src_buf->v4l2_buf.sequence = ctx->qsequence++; return 0; } @@ -1047,12 +1056,26 @@ static bool coda_bitstream_try_queue(struct coda_ctx *ctx, static void coda_fill_bitstream(struct coda_ctx *ctx) { struct vb2_buffer *src_buf; + struct coda_timestamp *ts; while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); if (coda_bitstream_try_queue(ctx, src_buf)) { + /* + * Source buffer is queued in the bitstream ringbuffer; + * queue the timestamp and mark source buffer as done + */ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + + ts = kmalloc(sizeof(*ts), GFP_KERNEL); + if (ts) { + ts->sequence = src_buf->v4l2_buf.sequence; + ts->timecode = src_buf->v4l2_buf.timecode; + ts->timestamp = src_buf->v4l2_buf.timestamp; + list_add_tail(&ts->list, &ctx->timestamp_list); + } + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); } else { break; @@ -2600,6 +2623,14 @@ static void coda_stop_streaming(struct vb2_queue *q) } if (!ctx->streamon_out && !ctx->streamon_cap) { + struct coda_timestamp *ts; + + while (!list_empty(&ctx->timestamp_list)) { + ts = list_first_entry(&ctx->timestamp_list, + struct coda_timestamp, list); + list_del(&ts->list); + kfree(ts); + } kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, ctx->bitstream.size); ctx->runcounter = 0; @@ -2887,6 +2918,7 @@ static int coda_open(struct file *file) ctx->bitstream.vaddr, ctx->bitstream.size); mutex_init(&ctx->bitstream_mutex); mutex_init(&ctx->buffer_mutex); + INIT_LIST_HEAD(&ctx->timestamp_list); coda_lock(ctx); list_add(&ctx->list, &dev->instances); @@ -2978,6 +3010,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) struct coda_q_data *q_data_src; struct coda_q_data *q_data_dst; struct vb2_buffer *dst_buf; + struct coda_timestamp *ts; int width, height; int decoded_idx; int display_idx; @@ -3099,6 +3132,18 @@ static void coda_finish_decode(struct coda_ctx *ctx) v4l2_err(&dev->v4l2_dev, "decoded frame index out of range: %d\n", decoded_idx); } else { + ts = list_first_entry(&ctx->timestamp_list, + struct coda_timestamp, list); + list_del(&ts->list); + val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1; + if (val != ts->sequence) { + v4l2_err(&dev->v4l2_dev, + "sequence number mismatch (%d != %d)\n", + val, ts->sequence); + } + ctx->frame_timestamps[decoded_idx] = *ts; + kfree(ts); + val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7; if (val == 0) ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_KEYFRAME; @@ -3132,6 +3177,9 @@ static void coda_finish_decode(struct coda_ctx *ctx) V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME); dst_buf->v4l2_buf.flags |= ctx->frame_types[ctx->display_idx]; + ts = &ctx->frame_timestamps[ctx->display_idx]; + dst_buf->v4l2_buf.timecode = ts->timecode; + dst_buf->v4l2_buf.timestamp = ts->timestamp; vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2); -- cgit v0.10.2 From 410e5e493895ad9018f19dfbca92d65e1eadcbbe Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:32 -0300 Subject: [media] coda: alert userspace about macroblock errors If the CODA reports macroblock errors, also set the VB2_BUF_STATE_ERROR flag to alert userspace. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index c7e557a..d4e7003 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -243,6 +243,7 @@ struct coda_ctx { struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS]; u32 frame_types[CODA_MAX_FRAMEBUFFERS]; struct coda_timestamp frame_timestamps[CODA_MAX_FRAMEBUFFERS]; + u32 frame_errors[CODA_MAX_FRAMEBUFFERS]; struct list_head timestamp_list; struct coda_aux_buf workbuf; int num_internal_frames; @@ -3016,6 +3017,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) int display_idx; u32 src_fourcc; int success; + u32 err_mb; u32 val; dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); @@ -3085,10 +3087,10 @@ static void coda_finish_decode(struct coda_ctx *ctx) /* no cropping */ } - val = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB); - if (val > 0) + err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB); + if (err_mb > 0) v4l2_err(&dev->v4l2_dev, - "errors in %d macroblocks\n", val); + "errors in %d macroblocks\n", err_mb); if (dev->devtype->product == CODA_7541) { val = coda_read(dev, CODA_RET_DEC_PIC_OPTION); @@ -3151,6 +3153,8 @@ static void coda_finish_decode(struct coda_ctx *ctx) ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_PFRAME; else ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_BFRAME; + + ctx->frame_errors[decoded_idx] = err_mb; } if (display_idx == -1) { @@ -3183,8 +3187,8 @@ static void coda_finish_decode(struct coda_ctx *ctx) vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2); - v4l2_m2m_buf_done(dst_buf, success ? VB2_BUF_STATE_DONE : - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, ctx->frame_errors[display_idx] ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "job finished: decoding frame (%d) (%s)\n", -- cgit v0.10.2 From cb2c02821c44522f7284fcd57a7f11e11657255b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:33 -0300 Subject: [media] coda: add sequence counter offset The coda h.264 decoder also counts PIC_RUNs where no frame was decoded but a frame was rotated out / marked as ready to be displayed. This causes an offset between the incoming encoded frame's sequence number and the decode sequence number returned by the coda. This patch introduces a sequence counter offset variable to keep track of the difference. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index d4e7003..d20343b 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -222,6 +222,7 @@ struct coda_ctx { u32 isequence; u32 qsequence; u32 osequence; + u32 sequence_offset; struct coda_q_data q_data[2]; enum coda_inst_type inst_type; struct coda_codec *codec; @@ -2621,6 +2622,7 @@ static void coda_stop_streaming(struct vb2_queue *q) ctx->streamon_cap = 0; ctx->osequence = 0; + ctx->sequence_offset = 0; } if (!ctx->streamon_out && !ctx->streamon_cap) { @@ -3126,7 +3128,9 @@ static void coda_finish_decode(struct coda_ctx *ctx) if (decoded_idx == -1) { /* no frame was decoded, but we might have a display frame */ - if (display_idx < 0 && ctx->display_idx < 0) + if (display_idx >= 0 && display_idx < ctx->num_internal_frames) + ctx->sequence_offset++; + else if (ctx->display_idx < 0) ctx->prescan_failed = true; } else if (decoded_idx == -2) { /* no frame was decoded, we still return the remaining buffers */ @@ -3138,10 +3142,11 @@ static void coda_finish_decode(struct coda_ctx *ctx) struct coda_timestamp, list); list_del(&ts->list); val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1; - if (val != ts->sequence) { + val -= ctx->sequence_offset; + if (val != (ts->sequence & 0xffff)) { v4l2_err(&dev->v4l2_dev, - "sequence number mismatch (%d != %d)\n", - val, ts->sequence); + "sequence number mismatch (%d(%d) != %d)\n", + val, ctx->sequence_offset, ts->sequence); } ctx->frame_timestamps[decoded_idx] = *ts; kfree(ts); -- cgit v0.10.2 From 84e2365d7dcc3e6414be613d7fefa9f28d66859b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:34 -0300 Subject: [media] coda: rename prescan_failed to hold and stop stream after timeout Rename the per-context prescan_failed variable to hold, as this is what the flag does: it temporarily keeps the coda from running until new data is fed into the bitstream buffer or stop_streaming is called on the input side. A prescan failure on i.MX5 is one possible reason to enter this state, another one is a picture run timeout on i.MX6. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index d20343b..2379379 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -237,7 +237,7 @@ struct coda_ctx { struct kfifo bitstream_fifo; struct mutex bitstream_mutex; struct coda_aux_buf bitstream; - bool prescan_failed; + bool hold; struct coda_aux_buf parabuf; struct coda_aux_buf psbuf; struct coda_aux_buf slicebuf; @@ -918,7 +918,7 @@ static int coda_decoder_cmd(struct file *file, void *fh, /* If this context is currently running, update the hardware flag */ coda_write(dev, ctx->bit_stream_param, CODA_REG_BIT_BIT_STREAM_PARAM); } - ctx->prescan_failed = false; + ctx->hold = false; v4l2_m2m_try_schedule(ctx->fh.m2m_ctx); return 0; @@ -1050,7 +1050,7 @@ static bool coda_bitstream_try_queue(struct coda_ctx *ctx, if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev)) coda_kfifo_sync_to_device_write(ctx); - ctx->prescan_failed = false; + ctx->hold = false; return true; } @@ -1421,6 +1421,8 @@ static void coda_pic_run_work(struct work_struct *work) if (!wait_for_completion_timeout(&ctx->completion, msecs_to_jiffies(1000))) { dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout\n"); + + ctx->hold = true; } else if (!ctx->aborting) { if (ctx->inst_type == CODA_INST_DECODER) coda_finish_decode(ctx); @@ -1459,7 +1461,7 @@ static int coda_job_ready(void *m2m_priv) return 0; } - if (ctx->prescan_failed || + if (ctx->hold || ((ctx->inst_type == CODA_INST_DECODER) && (coda_get_bitstream_payload(ctx) < 512) && !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { @@ -3100,7 +3102,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) /* not enough bitstream data */ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "prescan failed: %d\n", val); - ctx->prescan_failed = true; + ctx->hold = true; return; } } @@ -3131,7 +3133,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) if (display_idx >= 0 && display_idx < ctx->num_internal_frames) ctx->sequence_offset++; else if (ctx->display_idx < 0) - ctx->prescan_failed = true; + ctx->hold = true; } else if (decoded_idx == -2) { /* no frame was decoded, we still return the remaining buffers */ } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) { @@ -3167,7 +3169,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) * no more frames to be decoded, but there could still * be rotator output to dequeue */ - ctx->prescan_failed = true; + ctx->hold = true; } else if (display_idx == -3) { /* possibly prescan failure */ } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) { -- cgit v0.10.2 From 8f45284c4ed758174d22342aca1bb7299f76b012 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:35 -0300 Subject: [media] coda: add reset control support On i.MX53 and i.MX6, the CODA VPU can be reset by the System Reset Controller. We can use this to get out of dire situations, for example after a picture run timeout. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 2379379..b8c3d61 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -138,6 +139,7 @@ struct coda_dev { void __iomem *regs_base; struct clk *clk_per; struct clk *clk_ahb; + struct reset_control *rstc; struct coda_aux_buf codebuf; struct coda_aux_buf tempbuf; @@ -337,6 +339,39 @@ static int coda_command_sync(struct coda_ctx *ctx, int cmd) return coda_wait_timeout(dev); } +static int coda_hw_reset(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + unsigned long timeout; + unsigned int idx; + int ret; + + if (!dev->rstc) + return -ENOENT; + + idx = coda_read(dev, CODA_REG_BIT_RUN_INDEX); + + timeout = jiffies + msecs_to_jiffies(100); + coda_write(dev, 0x11, CODA9_GDI_BUS_CTRL); + while (coda_read(dev, CODA9_GDI_BUS_STATUS) != 0x77) { + if (time_after(jiffies, timeout)) + return -ETIME; + cpu_relax(); + } + + ret = reset_control_reset(dev->rstc); + if (ret < 0) + return ret; + + coda_write(dev, 0x00, CODA9_GDI_BUS_CTRL); + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); + coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); + ret = coda_wait_timeout(dev); + coda_write(dev, idx, CODA_REG_BIT_RUN_INDEX); + + return ret; +} + static struct coda_q_data *get_q_data(struct coda_ctx *ctx, enum v4l2_buf_type type) { @@ -1423,6 +1458,8 @@ static void coda_pic_run_work(struct work_struct *work) dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout\n"); ctx->hold = true; + + coda_hw_reset(ctx); } else if (!ctx->aborting) { if (ctx->inst_type == CODA_INST_DECODER) coda_finish_decode(ctx); @@ -3333,6 +3370,9 @@ static int coda_hw_init(struct coda_dev *dev) if (ret) goto err_clk_ahb; + if (dev->rstc) + reset_control_reset(dev->rstc); + /* * Copy the first CODA_ISRAM_SIZE in the internal SRAM. * The 16-bit chars in the code buffer are in memory access @@ -3691,6 +3731,17 @@ static int coda_probe(struct platform_device *pdev) return -ENOENT; } + dev->rstc = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(dev->rstc)) { + ret = PTR_ERR(dev->rstc); + if (ret == -ENOENT) { + dev->rstc = NULL; + } else { + dev_err(&pdev->dev, "failed get reset control: %d\n", ret); + return ret; + } + } + /* Get IRAM pool from device tree or platform data */ pool = of_get_named_gen_pool(np, "iram", 0); if (!pool && pdata) -- cgit v0.10.2 From 2fd6a3709ee6644e7404c02c1d480a9b73a6c858 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:36 -0300 Subject: [media] coda: add bytesperline to queue data bytesperline is calculated in multiple places, store it in the coda_q_data structure. This will be more useful later when adding JPEG support. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index b8c3d61..5ac455f 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -119,6 +119,7 @@ struct coda_devtype { struct coda_q_data { unsigned int width; unsigned int height; + unsigned int bytesperline; unsigned int sizeimage; unsigned int fourcc; struct v4l2_rect rect; @@ -636,10 +637,7 @@ static int coda_g_fmt(struct file *file, void *priv, f->fmt.pix.pixelformat = q_data->fourcc; f->fmt.pix.width = q_data->width; f->fmt.pix.height = q_data->height; - if (coda_format_is_yuv(f->fmt.pix.pixelformat)) - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 2); - else /* encoded formats h.264/mpeg4 */ - f->fmt.pix.bytesperline = 0; + f->fmt.pix.bytesperline = q_data->bytesperline; f->fmt.pix.sizeimage = q_data->sizeimage; f->fmt.pix.colorspace = ctx->colorspace; @@ -787,6 +785,7 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) q_data->fourcc = f->fmt.pix.pixelformat; q_data->width = f->fmt.pix.width; q_data->height = f->fmt.pix.height; + q_data->bytesperline = f->fmt.pix.bytesperline; q_data->sizeimage = f->fmt.pix.sizeimage; q_data->rect.left = 0; q_data->rect.top = 0; @@ -1347,14 +1346,16 @@ static void coda_prepare_encode(struct coda_ctx *ctx) switch (q_data_src->fourcc) { case V4L2_PIX_FMT_YVU420: /* Switch Cb and Cr for YVU420 format */ - picture_cr = picture_y + q_data_src->width * q_data_src->height; - picture_cb = picture_cr + q_data_src->width / 2 * + picture_cr = picture_y + q_data_src->bytesperline * + q_data_src->height; + picture_cb = picture_cr + q_data_src->bytesperline / 2 * q_data_src->height / 2; break; case V4L2_PIX_FMT_YUV420: default: - picture_cb = picture_y + q_data_src->width * q_data_src->height; - picture_cr = picture_cb + q_data_src->width / 2 * + picture_cb = picture_y + q_data_src->bytesperline * + q_data_src->height; + picture_cr = picture_cb + q_data_src->bytesperline / 2 * q_data_src->height / 2; break; } @@ -1596,9 +1597,11 @@ static void set_default_params(struct coda_ctx *ctx) ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc; ctx->q_data[V4L2_M2M_SRC].width = max_w; ctx->q_data[V4L2_M2M_SRC].height = max_h; + ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w; ctx->q_data[V4L2_M2M_SRC].sizeimage = (max_w * max_h * 3) / 2; ctx->q_data[V4L2_M2M_DST].width = max_w; ctx->q_data[V4L2_M2M_DST].height = max_h; + ctx->q_data[V4L2_M2M_DST].bytesperline = 0; ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE; ctx->q_data[V4L2_M2M_SRC].rect.width = max_w; ctx->q_data[V4L2_M2M_SRC].rect.height = max_h; @@ -2533,10 +2536,12 @@ static int coda_start_encoding(struct coda_ctx *ctx) } coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); - coda_write(dev, round_up(q_data_src->width, 8), CODA_CMD_SET_FRAME_BUF_STRIDE); - if (dev->devtype->product == CODA_7541) - coda_write(dev, round_up(q_data_src->width, 8), + coda_write(dev, q_data_src->bytesperline, + CODA_CMD_SET_FRAME_BUF_STRIDE); + if (dev->devtype->product == CODA_7541) { + coda_write(dev, q_data_src->bytesperline, CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE); + } if (dev->devtype->product != CODA_DX6) { coda_write(dev, ctx->iram_info.buf_bit_use, CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); -- cgit v0.10.2 From 1b0ed7be67f84b5381efb1b97740b2f66e0ecbfb Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:37 -0300 Subject: [media] coda: allow odd width, but still round up bytesperline Even though the CODA h.264 decoder always decodes complete macroblocks, we can set the stride to the corresponding multiple of 16 and use a value smaller than that as real width. Unfortunately the same doesn't work for height, as there is no vertical linesperframe stride for discontiguous planar YUV frames. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 5ac455f..05419f4 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -738,9 +738,9 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv, /* The h.264 decoder only returns complete 16x16 macroblocks */ if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) { - f->fmt.pix.width = round_up(f->fmt.pix.width, 16); + f->fmt.pix.width = f->fmt.pix.width; f->fmt.pix.height = round_up(f->fmt.pix.height, 16); - f->fmt.pix.bytesperline = f->fmt.pix.width; + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height * 3 / 2; } -- cgit v0.10.2 From 7c3df78953ca62c0713eae3d6b7cbc55b58cd9d5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:38 -0300 Subject: [media] coda: round up internal frames to multiples of macroblock size for h.264 CODA7541 only supports encoding h.264 frames with width and height that are multiples of the macroblock size. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 05419f4..aa80020 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -1744,15 +1744,21 @@ static void coda_free_framebuffers(struct coda_ctx *ctx) static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 fourcc) { struct coda_dev *dev = ctx->dev; - int height = q_data->height; + int width, height; dma_addr_t paddr; int ysize; int ret; int i; - if (ctx->codec && ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) - height = round_up(height, 16); - ysize = round_up(q_data->width, 8) * height; + if (ctx->codec && (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || + ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264)) { + width = round_up(q_data->width, 16); + height = round_up(q_data->height, 16); + } else { + width = round_up(q_data->width, 8); + height = q_data->height; + } + ysize = width * height; /* Allocate frame buffers */ for (i = 0; i < ctx->num_internal_frames; i++) { @@ -2377,7 +2383,16 @@ static int coda_start_encoding(struct coda_ctx *ctx) value = (q_data_src->width & CODADX6_PICWIDTH_MASK) << CODADX6_PICWIDTH_OFFSET; value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; break; - default: + case CODA_7541: + if (dst_fourcc == V4L2_PIX_FMT_H264) { + value = (round_up(q_data_src->width, 16) & + CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; + value |= (round_up(q_data_src->height, 16) & + CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; + break; + } + /* fallthrough */ + case CODA_960: value = (q_data_src->width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; value |= (q_data_src->height & CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; } -- cgit v0.10.2 From 451dfe5d85cc5f5c6cb196dc9ebae4458a59dc21 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:39 -0300 Subject: [media] coda: increase frame stride to 16 for h.264 When encoding into h.264, the input frame stride needs to be a multiple of 16. During allocation of the input buffers, it may not be known yet whether the encoder should create h.264 or not. Assume the worst and always use a frame stride that is a multiple of 16. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index aa80020..5ec6819 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -685,8 +685,8 @@ static int coda_try_fmt(struct coda_ctx *ctx, struct coda_codec *codec, switch (f->fmt.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - /* Frame stride must be multiple of 8 */ - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 8); + /* Frame stride must be multiple of 8, but 16 for h.264 */ + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height * 3 / 2; break; -- cgit v0.10.2 From f61c0b148cb406b0313fdb2a4ad8a43cc664313f Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:40 -0300 Subject: [media] coda: export auxiliary buffers via debugfs This patch exports all auxiliary buffers, including SRAM, as debugfs binary blobs for debugging purposes. It shows, for example, that psbuf currently doesn't seem to be used at all on CODA7541, and that slicebuf and workbuf usage is far from the maximum. It can also be used to validate SRAM size allocation. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 5ec6819..04a2d8b 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -129,6 +130,8 @@ struct coda_aux_buf { void *vaddr; dma_addr_t paddr; u32 size; + struct debugfs_blob_wrapper blob; + struct dentry *dentry; }; struct coda_dev { @@ -156,6 +159,7 @@ struct coda_dev { struct vb2_alloc_ctx *alloc_ctx; struct list_head instances; unsigned long instance_mask; + struct dentry *debugfs_root; }; struct coda_params { @@ -259,6 +263,7 @@ struct coda_ctx { u32 frm_dis_flg; u32 frame_mem_ctrl; int display_idx; + struct dentry *debugfs_entry; }; static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff, @@ -1704,7 +1709,8 @@ static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) } static int coda_alloc_aux_buf(struct coda_dev *dev, - struct coda_aux_buf *buf, size_t size) + struct coda_aux_buf *buf, size_t size, + const char *name, struct dentry *parent) { buf->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, &buf->paddr, GFP_KERNEL); @@ -1713,13 +1719,23 @@ static int coda_alloc_aux_buf(struct coda_dev *dev, buf->size = size; + if (name && parent) { + buf->blob.data = buf->vaddr; + buf->blob.size = size; + buf->dentry = debugfs_create_blob(name, 0644, parent, &buf->blob); + if (!buf->dentry) + dev_warn(&dev->plat_dev->dev, + "failed to create debugfs entry %s\n", name); + } + return 0; } static inline int coda_alloc_context_buf(struct coda_ctx *ctx, - struct coda_aux_buf *buf, size_t size) + struct coda_aux_buf *buf, size_t size, + const char *name) { - return coda_alloc_aux_buf(ctx->dev, buf, size); + return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry); } static void coda_free_aux_buf(struct coda_dev *dev, @@ -1731,6 +1747,7 @@ static void coda_free_aux_buf(struct coda_dev *dev, buf->vaddr = NULL; buf->size = 0; } + debugfs_remove(buf->dentry); } static void coda_free_framebuffers(struct coda_ctx *ctx) @@ -1763,12 +1780,16 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d /* Allocate frame buffers */ for (i = 0; i < ctx->num_internal_frames; i++) { size_t size; + char *name; size = ysize + ysize / 2; if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && dev->devtype->product != CODA_DX6) size += ysize / 4; - ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i], size); + name = kasprintf(GFP_KERNEL, "fb%d", i); + ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i], + size, name); + kfree(name); if (ret < 0) { coda_free_framebuffers(ctx); return ret; @@ -1992,7 +2013,7 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, /* worst case slice size */ size = (DIV_ROUND_UP(q_data->width, 16) * DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512; - ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size); + ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, "slicebuf"); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte slice buffer", ctx->slicebuf.size); @@ -2001,14 +2022,14 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, } if (dev->devtype->product == CODA_7541) { - ret = coda_alloc_context_buf(ctx, &ctx->psbuf, CODA7_PS_BUF_SIZE); + ret = coda_alloc_context_buf(ctx, &ctx->psbuf, CODA7_PS_BUF_SIZE, "psbuf"); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to allocate psmem buffer"); goto err; } } - ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size); + ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, "workbuf"); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte context buffer", ctx->workbuf.size); @@ -2897,6 +2918,7 @@ static int coda_open(struct file *file) { struct coda_dev *dev = video_drvdata(file); struct coda_ctx *ctx = NULL; + char *name; int ret; int idx; @@ -2911,6 +2933,10 @@ static int coda_open(struct file *file) } set_bit(idx, &dev->instance_mask); + name = kasprintf(GFP_KERNEL, "context%d", idx); + ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root); + kfree(name); + init_completion(&ctx->completion); INIT_WORK(&ctx->pic_run_work, coda_pic_run_work); INIT_WORK(&ctx->seq_end_work, coda_seq_end_work); @@ -2962,7 +2988,8 @@ static int coda_open(struct file *file) ctx->fh.ctrl_handler = &ctx->ctrls; - ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE); + ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE, + "parabuf"); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf"); goto err_dma_alloc; @@ -3023,6 +3050,8 @@ static int coda_release(struct file *file) v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n", ctx); + debugfs_remove_recursive(ctx->debugfs_entry); + /* If this instance is running, call .job_abort and wait for it to end */ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); @@ -3554,7 +3583,8 @@ static void coda_fw_callback(const struct firmware *fw, void *context) } /* allocate auxiliary per-device code buffer for the BIT processor */ - ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size); + ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf", + dev->debugfs_root); if (ret < 0) { dev_err(&pdev->dev, "failed to allocate code buffer\n"); return; @@ -3790,11 +3820,16 @@ static int coda_probe(struct platform_device *pdev) return -EINVAL; } + dev->debugfs_root = debugfs_create_dir("coda", NULL); + if (!dev->debugfs_root) + dev_warn(&pdev->dev, "failed to create debugfs root\n"); + /* allocate auxiliary per-device buffers for the BIT processor */ switch (dev->devtype->product) { case CODA_DX6: ret = coda_alloc_aux_buf(dev, &dev->workbuf, - CODADX6_WORK_BUF_SIZE); + CODADX6_WORK_BUF_SIZE, "workbuf", + dev->debugfs_root); if (ret < 0) { dev_err(&pdev->dev, "failed to allocate work buffer\n"); v4l2_device_unregister(&dev->v4l2_dev); @@ -3810,7 +3845,8 @@ static int coda_probe(struct platform_device *pdev) } if (dev->tempbuf.size) { ret = coda_alloc_aux_buf(dev, &dev->tempbuf, - dev->tempbuf.size); + dev->tempbuf.size, "tempbuf", + dev->debugfs_root); if (ret < 0) { dev_err(&pdev->dev, "failed to allocate temp buffer\n"); v4l2_device_unregister(&dev->v4l2_dev); @@ -3835,6 +3871,11 @@ static int coda_probe(struct platform_device *pdev) return -ENOMEM; } + dev->iram.blob.data = dev->iram.vaddr; + dev->iram.blob.size = dev->iram.size; + dev->iram.dentry = debugfs_create_blob("iram", 0644, dev->debugfs_root, + &dev->iram.blob); + dev->workqueue = alloc_workqueue("coda", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); if (!dev->workqueue) { dev_err(&pdev->dev, "unable to alloc workqueue\n"); @@ -3866,6 +3907,7 @@ static int coda_remove(struct platform_device *pdev) coda_free_aux_buf(dev, &dev->codebuf); coda_free_aux_buf(dev, &dev->tempbuf); coda_free_aux_buf(dev, &dev->workbuf); + debugfs_remove_recursive(dev->debugfs_root); return 0; } -- cgit v0.10.2 From e5b0d1c60fff0b3420a97f3687906df04afc8b66 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:41 -0300 Subject: [media] coda: store per-context work buffer size in struct coda_devtype We had the workbuf_size field since the beginning. Use it to tighten the code a little bit. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 04a2d8b..23c2111 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -44,10 +44,6 @@ #define CODADX6_MAX_INSTANCES 4 -#define CODA_FMO_BUF_SIZE 32 -#define CODADX6_WORK_BUF_SIZE (288 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024) -#define CODA7_WORK_BUF_SIZE (128 * 1024) -#define CODA9_WORK_BUF_SIZE (80 * 1024) #define CODA7_TEMP_BUF_SIZE (304 * 1024) #define CODA9_TEMP_BUF_SIZE (204 * 1024) #define CODA_PARA_BUF_SIZE (10 * 1024) @@ -1982,18 +1978,8 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, size_t size; int ret; - switch (dev->devtype->product) { - case CODA_7541: - size = CODA7_WORK_BUF_SIZE; - break; - case CODA_960: - size = CODA9_WORK_BUF_SIZE; - if (q_data->fourcc == V4L2_PIX_FMT_H264) - size += CODA9_PS_SAVE_SIZE; - break; - default: + if (dev->devtype->product == CODA_DX6) return 0; - } if (ctx->psbuf.vaddr) { v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n"); @@ -2029,6 +2015,10 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, } } + size = dev->devtype->workbuf_size; + if (dev->devtype->product == CODA_960 && + q_data->fourcc == V4L2_PIX_FMT_H264) + size += CODA9_PS_SAVE_SIZE; ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size, "workbuf"); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte context buffer", @@ -3684,28 +3674,32 @@ enum coda_platform { static const struct coda_devtype coda_devdata[] = { [CODA_IMX27] = { - .firmware = "v4l-codadx6-imx27.bin", - .product = CODA_DX6, - .codecs = codadx6_codecs, - .num_codecs = ARRAY_SIZE(codadx6_codecs), + .firmware = "v4l-codadx6-imx27.bin", + .product = CODA_DX6, + .codecs = codadx6_codecs, + .num_codecs = ARRAY_SIZE(codadx6_codecs), + .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024, }, [CODA_IMX53] = { - .firmware = "v4l-coda7541-imx53.bin", - .product = CODA_7541, - .codecs = coda7_codecs, - .num_codecs = ARRAY_SIZE(coda7_codecs), + .firmware = "v4l-coda7541-imx53.bin", + .product = CODA_7541, + .codecs = coda7_codecs, + .num_codecs = ARRAY_SIZE(coda7_codecs), + .workbuf_size = 128 * 1024, }, [CODA_IMX6Q] = { - .firmware = "v4l-coda960-imx6q.bin", - .product = CODA_960, - .codecs = coda9_codecs, - .num_codecs = ARRAY_SIZE(coda9_codecs), + .firmware = "v4l-coda960-imx6q.bin", + .product = CODA_960, + .codecs = coda9_codecs, + .num_codecs = ARRAY_SIZE(coda9_codecs), + .workbuf_size = 80 * 1024, }, [CODA_IMX6DL] = { - .firmware = "v4l-coda960-imx6dl.bin", - .product = CODA_960, - .codecs = coda9_codecs, - .num_codecs = ARRAY_SIZE(coda9_codecs), + .firmware = "v4l-coda960-imx6dl.bin", + .product = CODA_960, + .codecs = coda9_codecs, + .num_codecs = ARRAY_SIZE(coda9_codecs), + .workbuf_size = 80 * 1024, }, }; @@ -3828,7 +3822,7 @@ static int coda_probe(struct platform_device *pdev) switch (dev->devtype->product) { case CODA_DX6: ret = coda_alloc_aux_buf(dev, &dev->workbuf, - CODADX6_WORK_BUF_SIZE, "workbuf", + dev->devtype->workbuf_size, "workbuf", dev->debugfs_root); if (ret < 0) { dev_err(&pdev->dev, "failed to allocate work buffer\n"); -- cgit v0.10.2 From 5d73fad1eba7a62d13687ded6db3a5d72323c4db Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:42 -0300 Subject: [media] coda: store global temporary buffer size in struct coda_devtype Similarly to the work buffer size, store the temporary buffer size in the coda_devtype structure. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 23c2111..c1e950a 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -44,8 +44,6 @@ #define CODADX6_MAX_INSTANCES 4 -#define CODA7_TEMP_BUF_SIZE (304 * 1024) -#define CODA9_TEMP_BUF_SIZE (204 * 1024) #define CODA_PARA_BUF_SIZE (10 * 1024) #define CODA_ISRAM_SIZE (2048 * 2) #define CODADX6_IRAM_SIZE 0xb000 @@ -110,6 +108,7 @@ struct coda_devtype { struct coda_codec *codecs; unsigned int num_codecs; size_t workbuf_size; + size_t tempbuf_size; }; /* Per-queue, driver-specific private data */ @@ -3686,6 +3685,7 @@ static const struct coda_devtype coda_devdata[] = { .codecs = coda7_codecs, .num_codecs = ARRAY_SIZE(coda7_codecs), .workbuf_size = 128 * 1024, + .tempbuf_size = 304 * 1024, }, [CODA_IMX6Q] = { .firmware = "v4l-coda960-imx6q.bin", @@ -3693,6 +3693,7 @@ static const struct coda_devtype coda_devdata[] = { .codecs = coda9_codecs, .num_codecs = ARRAY_SIZE(coda9_codecs), .workbuf_size = 80 * 1024, + .tempbuf_size = 204 * 1024, }, [CODA_IMX6DL] = { .firmware = "v4l-coda960-imx6dl.bin", @@ -3700,6 +3701,7 @@ static const struct coda_devtype coda_devdata[] = { .codecs = coda9_codecs, .num_codecs = ARRAY_SIZE(coda9_codecs), .workbuf_size = 80 * 1024, + .tempbuf_size = 204 * 1024, }, }; @@ -3819,8 +3821,7 @@ static int coda_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "failed to create debugfs root\n"); /* allocate auxiliary per-device buffers for the BIT processor */ - switch (dev->devtype->product) { - case CODA_DX6: + if (dev->devtype->product == CODA_DX6) { ret = coda_alloc_aux_buf(dev, &dev->workbuf, dev->devtype->workbuf_size, "workbuf", dev->debugfs_root); @@ -3829,17 +3830,11 @@ static int coda_probe(struct platform_device *pdev) v4l2_device_unregister(&dev->v4l2_dev); return ret; } - break; - case CODA_7541: - dev->tempbuf.size = CODA7_TEMP_BUF_SIZE; - break; - case CODA_960: - dev->tempbuf.size = CODA9_TEMP_BUF_SIZE; - break; } - if (dev->tempbuf.size) { + + if (dev->devtype->tempbuf_size) { ret = coda_alloc_aux_buf(dev, &dev->tempbuf, - dev->tempbuf.size, "tempbuf", + dev->devtype->tempbuf_size, "tempbuf", dev->debugfs_root); if (ret < 0) { dev_err(&pdev->dev, "failed to allocate temp buffer\n"); -- cgit v0.10.2 From 401e972fae7d3da80c70e615aa0463f9c7c619ec Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2014 06:36:43 -0300 Subject: [media] coda: store IRAM size in struct coda_devtype Similarly to workbuf_size and tempbuf_size, store iram_size in the coda_devtype structure. This also decreases the IRAM used on i.MX6DL to 128 KiB. Signed-off-by: Philipp Zabel Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index c1e950a..18758e2 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -46,9 +46,6 @@ #define CODA_PARA_BUF_SIZE (10 * 1024) #define CODA_ISRAM_SIZE (2048 * 2) -#define CODADX6_IRAM_SIZE 0xb000 -#define CODA7_IRAM_SIZE 0x14000 -#define CODA9_IRAM_SIZE 0x21000 #define CODA7_PS_BUF_SIZE 0x28000 #define CODA9_PS_SAVE_SIZE (512 * 1024) @@ -109,6 +106,7 @@ struct coda_devtype { unsigned int num_codecs; size_t workbuf_size; size_t tempbuf_size; + size_t iram_size; }; /* Per-queue, driver-specific private data */ @@ -3678,6 +3676,7 @@ static const struct coda_devtype coda_devdata[] = { .codecs = codadx6_codecs, .num_codecs = ARRAY_SIZE(codadx6_codecs), .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024, + .iram_size = 0xb000, }, [CODA_IMX53] = { .firmware = "v4l-coda7541-imx53.bin", @@ -3686,6 +3685,7 @@ static const struct coda_devtype coda_devdata[] = { .num_codecs = ARRAY_SIZE(coda7_codecs), .workbuf_size = 128 * 1024, .tempbuf_size = 304 * 1024, + .iram_size = 0x14000, }, [CODA_IMX6Q] = { .firmware = "v4l-coda960-imx6q.bin", @@ -3694,6 +3694,7 @@ static const struct coda_devtype coda_devdata[] = { .num_codecs = ARRAY_SIZE(coda9_codecs), .workbuf_size = 80 * 1024, .tempbuf_size = 204 * 1024, + .iram_size = 0x21000, }, [CODA_IMX6DL] = { .firmware = "v4l-coda960-imx6dl.bin", @@ -3702,6 +3703,7 @@ static const struct coda_devtype coda_devdata[] = { .num_codecs = ARRAY_SIZE(coda9_codecs), .workbuf_size = 80 * 1024, .tempbuf_size = 204 * 1024, + .iram_size = 0x20000, }, }; @@ -3843,16 +3845,7 @@ static int coda_probe(struct platform_device *pdev) } } - switch (dev->devtype->product) { - case CODA_DX6: - dev->iram.size = CODADX6_IRAM_SIZE; - break; - case CODA_7541: - dev->iram.size = CODA7_IRAM_SIZE; - break; - case CODA_960: - dev->iram.size = CODA9_IRAM_SIZE; - } + dev->iram.size = dev->devtype->iram_size; dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size, &dev->iram.paddr); if (!dev->iram.vaddr) { -- cgit v0.10.2 From 28cae868cd245b6bb2f27bce807e9d78afcf8ea2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Jul 2014 06:13:07 +0200 Subject: [media] solo6x10: move out of staging into drivers/media/pci. Now that the custom motion detection API has been replaced with a standard API there is no reason anymore to keep it in staging. So (finally!) move it to drivers/media/pci. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 53196f1..5c16c9c 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -19,6 +19,7 @@ if MEDIA_ANALOG_TV_SUPPORT source "drivers/media/pci/ivtv/Kconfig" source "drivers/media/pci/zoran/Kconfig" source "drivers/media/pci/saa7146/Kconfig" +source "drivers/media/pci/solo6x10/Kconfig" endif if MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 35cc578..e5b53fb 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ obj-$(CONFIG_VIDEO_MEYE) += meye/ obj-$(CONFIG_STA2X11_VIP) += sta2x11/ +obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/ diff --git a/drivers/media/pci/solo6x10/Kconfig b/drivers/media/pci/solo6x10/Kconfig new file mode 100644 index 0000000..d9e06a6 --- /dev/null +++ b/drivers/media/pci/solo6x10/Kconfig @@ -0,0 +1,19 @@ +config VIDEO_SOLO6X10 + tristate "Bluecherry / Softlogic 6x10 capture cards (MPEG-4/H.264)" + depends on PCI && VIDEO_DEV && SND && I2C + select BITREVERSE + select FONT_SUPPORT + select FONT_8x16 + select VIDEOBUF2_DMA_SG + select VIDEOBUF2_DMA_CONTIG + select SND_PCM + select FONT_8x16 + ---help--- + This driver supports the Bluecherry H.264 and MPEG-4 hardware + compression capture cards and other Softlogic-based ones. + + Following cards have been tested: + * Bluecherry BC-H16480A (PCIe, 16 port, H.264) + * Bluecherry BC-H04120A (PCIe, 4 port, H.264) + * Bluecherry BC-H04120A-MPCI (Mini-PCI, 4 port, H.264) + * Bluecherry BC-04120A (PCIe, 4 port, MPEG-4) diff --git a/drivers/media/pci/solo6x10/Makefile b/drivers/media/pci/solo6x10/Makefile new file mode 100644 index 0000000..f474226 --- /dev/null +++ b/drivers/media/pci/solo6x10/Makefile @@ -0,0 +1,5 @@ +solo6x10-y := solo6x10-core.o solo6x10-i2c.o solo6x10-p2m.o solo6x10-v4l2.o \ + solo6x10-tw28.o solo6x10-gpio.o solo6x10-disp.o solo6x10-enc.o \ + solo6x10-v4l2-enc.o solo6x10-g723.o solo6x10-eeprom.o + +obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10.o diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c new file mode 100644 index 0000000..172583d --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -0,0 +1,705 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "solo6x10.h" +#include "solo6x10-tw28.h" + +MODULE_DESCRIPTION("Softlogic 6x10 MPEG4/H.264/G.723 CODEC V4L2/ALSA Driver"); +MODULE_AUTHOR("Bluecherry "); +MODULE_VERSION(SOLO6X10_VERSION); +MODULE_LICENSE("GPL"); + +static unsigned video_nr = -1; +module_param(video_nr, uint, 0644); +MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)"); + +static int full_eeprom; /* default is only top 64B */ +module_param(full_eeprom, uint, 0644); +MODULE_PARM_DESC(full_eeprom, "Allow access to full 128B EEPROM (dangerous)"); + + +static void solo_set_time(struct solo_dev *solo_dev) +{ + struct timespec ts; + + ktime_get_ts(&ts); + + solo_reg_write(solo_dev, SOLO_TIMER_SEC, ts.tv_sec); + solo_reg_write(solo_dev, SOLO_TIMER_USEC, ts.tv_nsec / NSEC_PER_USEC); +} + +static void solo_timer_sync(struct solo_dev *solo_dev) +{ + u32 sec, usec; + struct timespec ts; + long diff; + + if (solo_dev->type != SOLO_DEV_6110) + return; + + if (++solo_dev->time_sync < 60) + return; + + solo_dev->time_sync = 0; + + sec = solo_reg_read(solo_dev, SOLO_TIMER_SEC); + usec = solo_reg_read(solo_dev, SOLO_TIMER_USEC); + + ktime_get_ts(&ts); + + diff = (long)ts.tv_sec - (long)sec; + diff = (diff * 1000000) + + ((long)(ts.tv_nsec / NSEC_PER_USEC) - (long)usec); + + if (diff > 1000 || diff < -1000) { + solo_set_time(solo_dev); + } else if (diff) { + long usec_lsb = solo_dev->usec_lsb; + + usec_lsb -= diff / 4; + if (usec_lsb < 0) + usec_lsb = 0; + else if (usec_lsb > 255) + usec_lsb = 255; + + solo_dev->usec_lsb = usec_lsb; + solo_reg_write(solo_dev, SOLO_TIMER_USEC_LSB, + solo_dev->usec_lsb); + } +} + +static irqreturn_t solo_isr(int irq, void *data) +{ + struct solo_dev *solo_dev = data; + u32 status; + int i; + + status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); + if (!status) + return IRQ_NONE; + + if (status & ~solo_dev->irq_mask) { + solo_reg_write(solo_dev, SOLO_IRQ_STAT, + status & ~solo_dev->irq_mask); + status &= solo_dev->irq_mask; + } + + if (status & SOLO_IRQ_PCI_ERR) + solo_p2m_error_isr(solo_dev); + + for (i = 0; i < SOLO_NR_P2M; i++) + if (status & SOLO_IRQ_P2M(i)) + solo_p2m_isr(solo_dev, i); + + if (status & SOLO_IRQ_IIC) + solo_i2c_isr(solo_dev); + + if (status & SOLO_IRQ_VIDEO_IN) { + solo_video_in_isr(solo_dev); + solo_timer_sync(solo_dev); + } + + if (status & SOLO_IRQ_ENCODER) + solo_enc_v4l2_isr(solo_dev); + + if (status & SOLO_IRQ_G723) + solo_g723_isr(solo_dev); + + /* Clear all interrupts handled */ + solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); + + return IRQ_HANDLED; +} + +static void free_solo_dev(struct solo_dev *solo_dev) +{ + struct pci_dev *pdev; + + if (!solo_dev) + return; + + if (solo_dev->dev.parent) + device_unregister(&solo_dev->dev); + + pdev = solo_dev->pdev; + + /* If we never initialized the PCI device, then nothing else + * below here needs cleanup */ + if (!pdev) { + kfree(solo_dev); + return; + } + + if (solo_dev->reg_base) { + /* Bring down the sub-devices first */ + solo_g723_exit(solo_dev); + solo_enc_v4l2_exit(solo_dev); + solo_enc_exit(solo_dev); + solo_v4l2_exit(solo_dev); + solo_disp_exit(solo_dev); + solo_gpio_exit(solo_dev); + solo_p2m_exit(solo_dev); + solo_i2c_exit(solo_dev); + + /* Now cleanup the PCI device */ + solo_irq_off(solo_dev, ~0); + pci_iounmap(pdev, solo_dev->reg_base); + if (pdev->irq) + free_irq(pdev->irq, solo_dev); + } + + pci_release_regions(pdev); + pci_disable_device(pdev); + v4l2_device_unregister(&solo_dev->v4l2_dev); + pci_set_drvdata(pdev, NULL); + + kfree(solo_dev); +} + +static ssize_t eeprom_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned short *p = (unsigned short *)buf; + int i; + + if (count & 0x1) + dev_warn(dev, "EEPROM Write not aligned (truncating)\n"); + + if (!full_eeprom && count > 64) { + dev_warn(dev, "EEPROM Write truncated to 64 bytes\n"); + count = 64; + } else if (full_eeprom && count > 128) { + dev_warn(dev, "EEPROM Write truncated to 128 bytes\n"); + count = 128; + } + + solo_eeprom_ewen(solo_dev, 1); + + for (i = full_eeprom ? 0 : 32; i < min((int)(full_eeprom ? 64 : 32), + (int)(count / 2)); i++) + solo_eeprom_write(solo_dev, i, cpu_to_be16(p[i])); + + solo_eeprom_ewen(solo_dev, 0); + + return count; +} + +static ssize_t eeprom_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned short *p = (unsigned short *)buf; + int count = (full_eeprom ? 128 : 64); + int i; + + for (i = (full_eeprom ? 0 : 32); i < (count / 2); i++) + p[i] = be16_to_cpu(solo_eeprom_read(solo_dev, i)); + + return count; +} + +static ssize_t p2m_timeouts_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "%d\n", solo_dev->p2m_timeouts); +} + +static ssize_t sdram_size_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "%dMegs\n", solo_dev->sdram_size >> 20); +} + +static ssize_t tw28xx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "tw2815[%d] tw2864[%d] tw2865[%d]\n", + hweight32(solo_dev->tw2815), + hweight32(solo_dev->tw2864), + hweight32(solo_dev->tw2865)); +} + +static ssize_t input_map_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned int val; + char *out = buf; + + val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_0); + out += sprintf(out, "Channel 0 => Input %d\n", val & 0x1f); + out += sprintf(out, "Channel 1 => Input %d\n", (val >> 5) & 0x1f); + out += sprintf(out, "Channel 2 => Input %d\n", (val >> 10) & 0x1f); + out += sprintf(out, "Channel 3 => Input %d\n", (val >> 15) & 0x1f); + out += sprintf(out, "Channel 4 => Input %d\n", (val >> 20) & 0x1f); + out += sprintf(out, "Channel 5 => Input %d\n", (val >> 25) & 0x1f); + + val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_1); + out += sprintf(out, "Channel 6 => Input %d\n", val & 0x1f); + out += sprintf(out, "Channel 7 => Input %d\n", (val >> 5) & 0x1f); + out += sprintf(out, "Channel 8 => Input %d\n", (val >> 10) & 0x1f); + out += sprintf(out, "Channel 9 => Input %d\n", (val >> 15) & 0x1f); + out += sprintf(out, "Channel 10 => Input %d\n", (val >> 20) & 0x1f); + out += sprintf(out, "Channel 11 => Input %d\n", (val >> 25) & 0x1f); + + val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_2); + out += sprintf(out, "Channel 12 => Input %d\n", val & 0x1f); + out += sprintf(out, "Channel 13 => Input %d\n", (val >> 5) & 0x1f); + out += sprintf(out, "Channel 14 => Input %d\n", (val >> 10) & 0x1f); + out += sprintf(out, "Channel 15 => Input %d\n", (val >> 15) & 0x1f); + out += sprintf(out, "Spot Output => Input %d\n", (val >> 20) & 0x1f); + + return out - buf; +} + +static ssize_t p2m_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned long ms; + int ret = kstrtoul(buf, 10, &ms); + + if (ret < 0 || ms > 200) + return -EINVAL; + solo_dev->p2m_jiffies = msecs_to_jiffies(ms); + + return count; +} + +static ssize_t p2m_timeout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "%ums\n", jiffies_to_msecs(solo_dev->p2m_jiffies)); +} + +static ssize_t intervals_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + char *out = buf; + int fps = solo_dev->fps; + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) { + out += sprintf(out, "Channel %d: %d/%d (0x%08x)\n", + i, solo_dev->v4l2_enc[i]->interval, fps, + solo_reg_read(solo_dev, SOLO_CAP_CH_INTV(i))); + } + + return out - buf; +} + +static ssize_t sdram_offsets_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + char *out = buf; + + out += sprintf(out, "DISP: 0x%08x @ 0x%08x\n", + SOLO_DISP_EXT_ADDR, + SOLO_DISP_EXT_SIZE); + + out += sprintf(out, "EOSD: 0x%08x @ 0x%08x (0x%08x * %d)\n", + SOLO_EOSD_EXT_ADDR, + SOLO_EOSD_EXT_AREA(solo_dev), + SOLO_EOSD_EXT_SIZE(solo_dev), + SOLO_EOSD_EXT_AREA(solo_dev) / + SOLO_EOSD_EXT_SIZE(solo_dev)); + + out += sprintf(out, "MOTI: 0x%08x @ 0x%08x\n", + SOLO_MOTION_EXT_ADDR(solo_dev), + SOLO_MOTION_EXT_SIZE); + + out += sprintf(out, "G723: 0x%08x @ 0x%08x\n", + SOLO_G723_EXT_ADDR(solo_dev), + SOLO_G723_EXT_SIZE); + + out += sprintf(out, "CAPT: 0x%08x @ 0x%08x (0x%08x * %d)\n", + SOLO_CAP_EXT_ADDR(solo_dev), + SOLO_CAP_EXT_SIZE(solo_dev), + SOLO_CAP_PAGE_SIZE, + SOLO_CAP_EXT_SIZE(solo_dev) / SOLO_CAP_PAGE_SIZE); + + out += sprintf(out, "EREF: 0x%08x @ 0x%08x (0x%08x * %d)\n", + SOLO_EREF_EXT_ADDR(solo_dev), + SOLO_EREF_EXT_AREA(solo_dev), + SOLO_EREF_EXT_SIZE, + SOLO_EREF_EXT_AREA(solo_dev) / SOLO_EREF_EXT_SIZE); + + out += sprintf(out, "MPEG: 0x%08x @ 0x%08x\n", + SOLO_MP4E_EXT_ADDR(solo_dev), + SOLO_MP4E_EXT_SIZE(solo_dev)); + + out += sprintf(out, "JPEG: 0x%08x @ 0x%08x\n", + SOLO_JPEG_EXT_ADDR(solo_dev), + SOLO_JPEG_EXT_SIZE(solo_dev)); + + return out - buf; +} + +static ssize_t sdram_show(struct file *file, struct kobject *kobj, + struct bin_attribute *a, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + const int size = solo_dev->sdram_size; + + if (off >= size) + return 0; + + if (off + count > size) + count = size - off; + + if (solo_p2m_dma(solo_dev, 0, buf, off, count, 0, 0)) + return -EIO; + + return count; +} + +static const struct device_attribute solo_dev_attrs[] = { + __ATTR(eeprom, 0640, eeprom_show, eeprom_store), + __ATTR(p2m_timeout, 0644, p2m_timeout_show, p2m_timeout_store), + __ATTR_RO(p2m_timeouts), + __ATTR_RO(sdram_size), + __ATTR_RO(tw28xx), + __ATTR_RO(input_map), + __ATTR_RO(intervals), + __ATTR_RO(sdram_offsets), +}; + +static void solo_device_release(struct device *dev) +{ + /* Do nothing */ +} + +static int solo_sysfs_init(struct solo_dev *solo_dev) +{ + struct bin_attribute *sdram_attr = &solo_dev->sdram_attr; + struct device *dev = &solo_dev->dev; + const char *driver; + int i; + + if (solo_dev->type == SOLO_DEV_6110) + driver = "solo6110"; + else + driver = "solo6010"; + + dev->release = solo_device_release; + dev->parent = &solo_dev->pdev->dev; + set_dev_node(dev, dev_to_node(&solo_dev->pdev->dev)); + dev_set_name(dev, "%s-%d-%d", driver, solo_dev->vfd->num, + solo_dev->nr_chans); + + if (device_register(dev)) { + dev->parent = NULL; + return -ENOMEM; + } + + for (i = 0; i < ARRAY_SIZE(solo_dev_attrs); i++) { + if (device_create_file(dev, &solo_dev_attrs[i])) { + device_unregister(dev); + return -ENOMEM; + } + } + + sysfs_attr_init(&sdram_attr->attr); + sdram_attr->attr.name = "sdram"; + sdram_attr->attr.mode = 0440; + sdram_attr->read = sdram_show; + sdram_attr->size = solo_dev->sdram_size; + + if (device_create_bin_file(dev, sdram_attr)) { + device_unregister(dev); + return -ENOMEM; + } + + return 0; +} + +static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct solo_dev *solo_dev; + int ret; + u8 chip_id; + + solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); + if (solo_dev == NULL) + return -ENOMEM; + + if (id->driver_data == SOLO_DEV_6010) + dev_info(&pdev->dev, "Probing Softlogic 6010\n"); + else + dev_info(&pdev->dev, "Probing Softlogic 6110\n"); + + solo_dev->type = id->driver_data; + solo_dev->pdev = pdev; + spin_lock_init(&solo_dev->reg_io_lock); + ret = v4l2_device_register(&pdev->dev, &solo_dev->v4l2_dev); + if (ret) + goto fail_probe; + + /* Only for during init */ + solo_dev->p2m_jiffies = msecs_to_jiffies(100); + + ret = pci_enable_device(pdev); + if (ret) + goto fail_probe; + + pci_set_master(pdev); + + /* RETRY/TRDY Timeout disabled */ + pci_write_config_byte(pdev, 0x40, 0x00); + pci_write_config_byte(pdev, 0x41, 0x00); + + ret = pci_request_regions(pdev, SOLO6X10_NAME); + if (ret) + goto fail_probe; + + solo_dev->reg_base = pci_ioremap_bar(pdev, 0); + if (solo_dev->reg_base == NULL) { + ret = -ENOMEM; + goto fail_probe; + } + + chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & + SOLO_CHIP_ID_MASK; + switch (chip_id) { + case 7: + solo_dev->nr_chans = 16; + solo_dev->nr_ext = 5; + break; + case 6: + solo_dev->nr_chans = 8; + solo_dev->nr_ext = 2; + break; + default: + dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n", + chip_id); + case 5: + solo_dev->nr_chans = 4; + solo_dev->nr_ext = 1; + } + + /* Disable all interrupts to start */ + solo_irq_off(solo_dev, ~0); + + /* Initial global settings */ + if (solo_dev->type == SOLO_DEV_6010) { + solo_dev->clock_mhz = 108; + solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT + | SOLO_SYS_CFG_INPUTDIV(25) + | SOLO_SYS_CFG_FEEDBACKDIV(solo_dev->clock_mhz * 2 - 2) + | SOLO_SYS_CFG_OUTDIV(3); + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); + } else { + u32 divq, divf; + + solo_dev->clock_mhz = 135; + + if (solo_dev->clock_mhz < 125) { + divq = 3; + divf = (solo_dev->clock_mhz * 4) / 3 - 1; + } else { + divq = 2; + divf = (solo_dev->clock_mhz * 2) / 3 - 1; + } + + solo_reg_write(solo_dev, SOLO_PLL_CONFIG, + (1 << 20) | /* PLL_RANGE */ + (8 << 15) | /* PLL_DIVR */ + (divq << 12) | + (divf << 4) | + (1 << 1) /* PLL_FSEN */); + + solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT; + } + + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); + solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, + solo_dev->clock_mhz - 1); + + /* PLL locking time of 1ms */ + mdelay(1); + + ret = request_irq(pdev->irq, solo_isr, IRQF_SHARED, SOLO6X10_NAME, + solo_dev); + if (ret) + goto fail_probe; + + /* Handle this from the start */ + solo_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); + + ret = solo_i2c_init(solo_dev); + if (ret) + goto fail_probe; + + /* Setup the DMA engine */ + solo_reg_write(solo_dev, SOLO_DMA_CTRL, + SOLO_DMA_CTRL_REFRESH_CYCLE(1) | + SOLO_DMA_CTRL_SDRAM_SIZE(2) | + SOLO_DMA_CTRL_SDRAM_CLK_INVERT | + SOLO_DMA_CTRL_READ_CLK_SELECT | + SOLO_DMA_CTRL_LATENCY(1)); + + /* Undocumented crap */ + solo_reg_write(solo_dev, SOLO_DMA_CTRL1, + solo_dev->type == SOLO_DEV_6010 ? 0x100 : 0x300); + + if (solo_dev->type != SOLO_DEV_6010) { + solo_dev->usec_lsb = 0x3f; + solo_set_time(solo_dev); + } + + /* Disable watchdog */ + solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); + + /* Initialize sub components */ + + ret = solo_p2m_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_disp_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_gpio_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_tw28_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_v4l2_init(solo_dev, video_nr); + if (ret) + goto fail_probe; + + ret = solo_enc_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_enc_v4l2_init(solo_dev, video_nr); + if (ret) + goto fail_probe; + + ret = solo_g723_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_sysfs_init(solo_dev); + if (ret) + goto fail_probe; + + /* Now that init is over, set this lower */ + solo_dev->p2m_jiffies = msecs_to_jiffies(20); + + return 0; + +fail_probe: + free_solo_dev(solo_dev); + return ret; +} + +static void solo_pci_remove(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct solo_dev *solo_dev = container_of(v4l2_dev, struct solo_dev, v4l2_dev); + + free_solo_dev(solo_dev); +} + +static const struct pci_device_id solo_id_table[] = { + /* 6010 based cards */ + { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16), + .driver_data = SOLO_DEV_6010 }, + /* 6110 based cards */ + { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), + .driver_data = SOLO_DEV_6110 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4), + .driver_data = SOLO_DEV_6110 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8), + .driver_data = SOLO_DEV_6110 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16), + .driver_data = SOLO_DEV_6110 }, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, solo_id_table); + +static struct pci_driver solo_pci_driver = { + .name = SOLO6X10_NAME, + .id_table = solo_id_table, + .probe = solo_pci_probe, + .remove = solo_pci_remove, +}; + +module_pci_driver(solo_pci_driver); diff --git a/drivers/media/pci/solo6x10/solo6x10-disp.c b/drivers/media/pci/solo6x10/solo6x10-disp.c new file mode 100644 index 0000000..ed88ab4 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-disp.c @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 +#include +#include +#include + +#include "solo6x10.h" + +#define SOLO_VCLK_DELAY 3 +#define SOLO_PROGRESSIVE_VSIZE 1024 + +#define SOLO_MOT_THRESH_W 64 +#define SOLO_MOT_THRESH_H 64 +#define SOLO_MOT_THRESH_SIZE 8192 +#define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) +#define SOLO_MOT_FLAG_SIZE 1024 +#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 16) + +static void solo_vin_config(struct solo_dev *solo_dev) +{ + solo_dev->vin_hstart = 8; + solo_dev->vin_vstart = 2; + + solo_reg_write(solo_dev, SOLO_SYS_VCLK, + SOLO_VCLK_SELECT(2) | + SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_I_P, + SOLO_VI_H_START(solo_dev->vin_hstart) | + SOLO_VI_V_START(solo_dev->vin_vstart) | + SOLO_VI_V_STOP(solo_dev->vin_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_I_S, + SOLO_VI_H_START(solo_dev->vout_hstart) | + SOLO_VI_V_START(solo_dev->vout_vstart) | + SOLO_VI_V_STOP(solo_dev->vout_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_P, + SOLO_VI_H_START(0) | + SOLO_VI_V_START(1) | + SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE)); + + solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, + SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); + + /* On 6110, initialize mozaic darkness stength */ + if (solo_dev->type == SOLO_DEV_6010) + solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0); + else + solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 16 << 22); + + solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2); + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, + SOLO_VI_PB_USER_MODE); + solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, + SOLO_VI_PB_HSIZE(858) | SOLO_VI_PB_VSIZE(246)); + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, + SOLO_VI_PB_VSTART(4) | + SOLO_VI_PB_VSTOP(4 + 240)); + } else { + solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, + SOLO_VI_PB_USER_MODE | SOLO_VI_PB_PAL); + solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, + SOLO_VI_PB_HSIZE(864) | SOLO_VI_PB_VSIZE(294)); + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, + SOLO_VI_PB_VSTART(4) | + SOLO_VI_PB_VSTOP(4 + 288)); + } + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_H, SOLO_VI_PB_HSTART(16) | + SOLO_VI_PB_HSTOP(16 + 720)); +} + +static void solo_vout_config_cursor(struct solo_dev *dev) +{ + int i; + + /* Load (blank) cursor bitmap mask (2bpp) */ + for (i = 0; i < 20; i++) + solo_reg_write(dev, SOLO_VO_CURSOR_MASK(i), 0); + + solo_reg_write(dev, SOLO_VO_CURSOR_POS, 0); + + solo_reg_write(dev, SOLO_VO_CURSOR_CLR, + (0x80 << 24) | (0x80 << 16) | (0x10 << 8) | 0x80); + solo_reg_write(dev, SOLO_VO_CURSOR_CLR2, (0xe0 << 8) | 0x80); +} + +static void solo_vout_config(struct solo_dev *solo_dev) +{ + solo_dev->vout_hstart = 6; + solo_dev->vout_vstart = 8; + + solo_reg_write(solo_dev, SOLO_VO_FMT_ENC, + solo_dev->video_type | + SOLO_VO_USER_COLOR_SET_NAV | + SOLO_VO_USER_COLOR_SET_NAH | + SOLO_VO_NA_COLOR_Y(0) | + SOLO_VO_NA_COLOR_CB(0) | + SOLO_VO_NA_COLOR_CR(0)); + + solo_reg_write(solo_dev, SOLO_VO_ACT_H, + SOLO_VO_H_START(solo_dev->vout_hstart) | + SOLO_VO_H_STOP(solo_dev->vout_hstart + + solo_dev->video_hsize)); + + solo_reg_write(solo_dev, SOLO_VO_ACT_V, + SOLO_VO_V_START(solo_dev->vout_vstart) | + SOLO_VO_V_STOP(solo_dev->vout_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VO_RANGE_HV, + SOLO_VO_H_LEN(solo_dev->video_hsize) | + SOLO_VO_V_LEN(solo_dev->video_vsize)); + + /* Border & background colors */ + solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, + (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); + solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, + (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); + solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, + (16 << 24) | (128 << 16) | (16 << 8) | 128); + + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); + + solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 0); + + solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); + solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); + + solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON | + SOLO_VO_DISP_ERASE_COUNT(8) | + SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); + + + solo_vout_config_cursor(solo_dev); + + /* Enable channels we support */ + solo_reg_write(solo_dev, SOLO_VI_CH_ENA, + (1 << solo_dev->nr_chans) - 1); +} + +static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, + u16 val, int reg_size) +{ + u16 *buf; + const int n = 64, size = n * sizeof(*buf); + int i, ret = 0; + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < n; i++) + buf[i] = cpu_to_le16(val); + + for (i = 0; i < reg_size; i += size) { + ret = solo_p2m_dma(solo_dev, 1, buf, + SOLO_MOTION_EXT_ADDR(solo_dev) + off + i, + size, 0, 0); + + if (ret) + break; + } + + kfree(buf); + return ret; +} + +int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) +{ + if (ch > solo_dev->nr_chans) + return -EINVAL; + + return solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + + (ch * SOLO_MOT_THRESH_SIZE * 2), + val, SOLO_MOT_THRESH_SIZE); +} + +int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, + const u16 *thresholds) +{ + const unsigned size = sizeof(u16) * 64; + u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2; + u16 *buf; + int x, y; + int ret = 0; + + buf = kzalloc(size, GFP_KERNEL); + for (y = 0; y < SOLO_MOTION_SZ; y++) { + for (x = 0; x < SOLO_MOTION_SZ; x++) + buf[x] = cpu_to_le16(thresholds[y * SOLO_MOTION_SZ + x]); + ret |= solo_p2m_dma(solo_dev, 1, buf, + SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * size, + size, 0, 0); + } + kfree(buf); + return ret; +} + +/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k + * threshold and working table for each channel. Atleast that's what the + * spec says. However, this code (taken from rdk) has some mystery 8k + * block right after the flag area, before the first thresh table. */ +static void solo_motion_config(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) { + /* Clear motion flag area */ + solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000, + SOLO_MOT_FLAG_SIZE); + + /* Clear working cache table */ + solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + + (i * SOLO_MOT_THRESH_SIZE * 2) + + SOLO_MOT_THRESH_SIZE, 0x0000, + SOLO_MOT_THRESH_SIZE); + + /* Set default threshold table */ + solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH); + } + + /* Default motion settings */ + solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) | + (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); + solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, + SOLO_VI_MOTION_FRAME_COUNT(3) | + SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) + /* | SOLO_VI_MOTION_INTR_START_STOP */ + | SOLO_VI_MOTION_SAMPLE_COUNT(10)); + + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); +} + +int solo_disp_init(struct solo_dev *solo_dev) +{ + int i; + + solo_dev->video_hsize = 704; + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + solo_dev->video_vsize = 240; + solo_dev->fps = 30; + } else { + solo_dev->video_vsize = 288; + solo_dev->fps = 25; + } + + solo_vin_config(solo_dev); + solo_motion_config(solo_dev); + solo_vout_config(solo_dev); + + for (i = 0; i < solo_dev->nr_chans; i++) + solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1); + + return 0; +} + +void solo_disp_exit(struct solo_dev *solo_dev) +{ + int i; + + solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0); + solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); + solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(i), 0); + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(i), 0); + solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 0); + } + + /* Set default border */ + for (i = 0; i < 5; i++) + solo_reg_write(solo_dev, SOLO_VO_BORDER_X(i), 0); + + for (i = 0; i < 5; i++) + solo_reg_write(solo_dev, SOLO_VO_BORDER_Y(i), 0); + + solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_MASK, 0); + solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_MASK, 0); + + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(0), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(0), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(0), 0); + + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(1), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(1), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(1), 0); +} diff --git a/drivers/media/pci/solo6x10/solo6x10-eeprom.c b/drivers/media/pci/solo6x10/solo6x10-eeprom.c new file mode 100644 index 0000000..af40b3a --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-eeprom.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 +#include + +#include "solo6x10.h" + +/* Control */ +#define EE_SHIFT_CLK 0x04 +#define EE_CS 0x08 +#define EE_DATA_WRITE 0x02 +#define EE_DATA_READ 0x01 +#define EE_ENB (0x80 | EE_CS) + +#define eeprom_delay() udelay(100) +#if 0 +#define eeprom_delay() solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) +#define eeprom_delay() ({ \ + int i, ret; \ + udelay(100); \ + for (i = ret = 0; i < 1000 && !ret; i++) \ + ret = solo_eeprom_reg_read(solo_dev); \ +}) +#endif +#define ADDR_LEN 6 + +/* Commands */ +#define EE_EWEN_CMD 4 +#define EE_EWDS_CMD 4 +#define EE_WRITE_CMD 5 +#define EE_READ_CMD 6 +#define EE_ERASE_CMD 7 + +static unsigned int solo_eeprom_reg_read(struct solo_dev *solo_dev) +{ + return solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) & EE_DATA_READ; +} + +static void solo_eeprom_reg_write(struct solo_dev *solo_dev, u32 data) +{ + solo_reg_write(solo_dev, SOLO_EEPROM_CTRL, data); + eeprom_delay(); +} + +static void solo_eeprom_cmd(struct solo_dev *solo_dev, int cmd) +{ + int i; + + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ACCESS_EN); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); + + for (i = 4 + ADDR_LEN; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0; + + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | dataval); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | + EE_SHIFT_CLK | dataval); + } + + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); +} + +unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en) +{ + int ewen_cmd = (w_en ? 0x3f : 0) | (EE_EWEN_CMD << ADDR_LEN); + unsigned int retval = 0; + int i; + + solo_eeprom_cmd(solo_dev, ewen_cmd); + + for (i = 0; i < 16; i++) { + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | + EE_SHIFT_CLK); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + } + + solo_eeprom_reg_write(solo_dev, ~EE_CS); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + + return retval; +} + +unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc) +{ + int read_cmd = loc | (EE_READ_CMD << ADDR_LEN); + unsigned short retval = 0; + int i; + + solo_eeprom_cmd(solo_dev, read_cmd); + + for (i = 0; i < 16; i++) { + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | + EE_SHIFT_CLK); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); + } + + solo_eeprom_reg_write(solo_dev, ~EE_CS); + + return retval; +} + +int solo_eeprom_write(struct solo_dev *solo_dev, int loc, + unsigned short data) +{ + int write_cmd = loc | (EE_WRITE_CMD << ADDR_LEN); + unsigned int retval; + int i; + + solo_eeprom_cmd(solo_dev, write_cmd); + + for (i = 15; i >= 0; i--) { + unsigned int dataval = (data >> i) & 1; + + solo_eeprom_reg_write(solo_dev, EE_ENB); + solo_eeprom_reg_write(solo_dev, + EE_ENB | (dataval << 1) | EE_SHIFT_CLK); + } + + solo_eeprom_reg_write(solo_dev, EE_ENB); + solo_eeprom_reg_write(solo_dev, ~EE_CS); + solo_eeprom_reg_write(solo_dev, EE_ENB); + + for (i = retval = 0; i < 10000 && !retval; i++) + retval = solo_eeprom_reg_read(solo_dev); + + solo_eeprom_reg_write(solo_dev, ~EE_CS); + + return !retval; +} diff --git a/drivers/media/pci/solo6x10/solo6x10-enc.c b/drivers/media/pci/solo6x10/solo6x10-enc.c new file mode 100644 index 0000000..d19c0ae --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-enc.c @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 +#include +#include +#include + +#include "solo6x10.h" + +#define VI_PROG_HSIZE (1280 - 16) +#define VI_PROG_VSIZE (1024 - 16) + +#define IRQ_LEVEL 2 + +static void solo_capture_config(struct solo_dev *solo_dev) +{ + unsigned long height; + unsigned long width; + void *buf; + int i; + + solo_reg_write(solo_dev, SOLO_CAP_BASE, + SOLO_CAP_MAX_PAGE((SOLO_CAP_EXT_SIZE(solo_dev) + - SOLO_CAP_PAGE_SIZE) >> 16) + | SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); + + /* XXX: Undocumented bits at b17 and b24 */ + if (solo_dev->type == SOLO_DEV_6110) { + /* NOTE: Ref driver has (62 << 24) here as well, but it causes + * wacked out frame timing on 4-port 6110. */ + solo_reg_write(solo_dev, SOLO_CAP_BTW, + (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | + SOLO_CAP_MAX_BANDWIDTH(36)); + } else { + solo_reg_write(solo_dev, SOLO_CAP_BTW, + (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | + SOLO_CAP_MAX_BANDWIDTH(32)); + } + + /* Set scale 1, 9 dimension */ + width = solo_dev->video_hsize; + height = solo_dev->video_vsize; + solo_reg_write(solo_dev, SOLO_DIM_SCALE1, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 2, 10 dimension */ + width = solo_dev->video_hsize / 2; + height = solo_dev->video_vsize; + solo_reg_write(solo_dev, SOLO_DIM_SCALE2, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 3, 11 dimension */ + width = solo_dev->video_hsize / 2; + height = solo_dev->video_vsize / 2; + solo_reg_write(solo_dev, SOLO_DIM_SCALE3, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 4, 12 dimension */ + width = solo_dev->video_hsize / 3; + height = solo_dev->video_vsize / 3; + solo_reg_write(solo_dev, SOLO_DIM_SCALE4, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 5, 13 dimension */ + width = solo_dev->video_hsize / 4; + height = solo_dev->video_vsize / 2; + solo_reg_write(solo_dev, SOLO_DIM_SCALE5, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Progressive */ + width = VI_PROG_HSIZE; + height = VI_PROG_VSIZE; + solo_reg_write(solo_dev, SOLO_DIM_PROG, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 16) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Clear OSD */ + solo_reg_write(solo_dev, SOLO_VE_OSD_CH, 0); + solo_reg_write(solo_dev, SOLO_VE_OSD_BASE, SOLO_EOSD_EXT_ADDR >> 16); + solo_reg_write(solo_dev, SOLO_VE_OSD_CLR, + 0xF0 << 16 | 0x80 << 8 | 0x80); + + if (solo_dev->type == SOLO_DEV_6010) + solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, + SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); + else + solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, SOLO_VE_OSD_V_DOUBLE + | SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); + + /* Clear OSG buffer */ + buf = kzalloc(SOLO_EOSD_EXT_SIZE(solo_dev), GFP_KERNEL); + if (!buf) + return; + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_p2m_dma(solo_dev, 1, buf, + SOLO_EOSD_EXT_ADDR + + (SOLO_EOSD_EXT_SIZE(solo_dev) * i), + SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0); + } + kfree(buf); +} + +#define SOLO_OSD_WRITE_SIZE (16 * OSD_TEXT_MAX) + +/* Should be called with enable_lock held */ +int solo_osd_print(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + unsigned char *str = solo_enc->osd_text; + u8 *buf = solo_enc->osd_buf; + u32 reg; + const struct font_desc *vga = find_font("VGA8x16"); + const unsigned char *vga_data; + int i, j; + + if (WARN_ON_ONCE(!vga)) + return -ENODEV; + + reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH); + if (!*str) { + /* Disable OSD on this channel */ + reg &= ~(1 << solo_enc->ch); + goto out; + } + + memset(buf, 0, SOLO_OSD_WRITE_SIZE); + vga_data = (const unsigned char *)vga->data; + + for (i = 0; *str; i++, str++) { + for (j = 0; j < 16; j++) { + buf[(j << 1) | (i & 1) | ((i & ~1) << 4)] = + bitrev8(vga_data[(*str << 4) | j]); + } + } + + solo_p2m_dma(solo_dev, 1, buf, + SOLO_EOSD_EXT_ADDR_CHAN(solo_dev, solo_enc->ch), + SOLO_OSD_WRITE_SIZE, 0, 0); + + /* Enable OSD on this channel */ + reg |= (1 << solo_enc->ch); + +out: + solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); + return 0; +} + +/** + * Set channel Quality Profile (0-3). + */ +void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch, + unsigned int qp) +{ + unsigned long flags; + unsigned int idx, reg; + + if ((ch > 31) || (qp > 3)) + return; + + if (solo_dev->type == SOLO_DEV_6010) + return; + + if (ch < 16) { + idx = 0; + reg = SOLO_VE_JPEG_QP_CH_L; + } else { + ch -= 16; + idx = 1; + reg = SOLO_VE_JPEG_QP_CH_H; + } + ch *= 2; + + spin_lock_irqsave(&solo_dev->jpeg_qp_lock, flags); + + solo_dev->jpeg_qp[idx] &= ~(3 << ch); + solo_dev->jpeg_qp[idx] |= (qp & 3) << ch; + + solo_reg_write(solo_dev, reg, solo_dev->jpeg_qp[idx]); + + spin_unlock_irqrestore(&solo_dev->jpeg_qp_lock, flags); +} + +int solo_g_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch) +{ + int idx; + + if (solo_dev->type == SOLO_DEV_6010) + return 2; + + if (WARN_ON_ONCE(ch > 31)) + return 2; + + if (ch < 16) { + idx = 0; + } else { + ch -= 16; + idx = 1; + } + ch *= 2; + + return (solo_dev->jpeg_qp[idx] >> ch) & 3; +} + +#define SOLO_QP_INIT 0xaaaaaaaa + +static void solo_jpeg_config(struct solo_dev *solo_dev) +{ + if (solo_dev->type == SOLO_DEV_6010) { + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, + (2 << 24) | (2 << 16) | (2 << 8) | 2); + } else { + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, + (4 << 24) | (3 << 16) | (2 << 8) | 1); + } + + spin_lock_init(&solo_dev->jpeg_qp_lock); + + /* Initialize Quality Profile for all channels */ + solo_dev->jpeg_qp[0] = solo_dev->jpeg_qp[1] = SOLO_QP_INIT; + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, SOLO_QP_INIT); + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, SOLO_QP_INIT); + + solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG, + (SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) | + ((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff)); + solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff); + if (solo_dev->type == SOLO_DEV_6110) { + solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG1, + (0 << 16) | (30 << 8) | 60); + } +} + +static void solo_mp4e_config(struct solo_dev *solo_dev) +{ + int i; + u32 cfg; + + solo_reg_write(solo_dev, SOLO_VE_CFG0, + SOLO_VE_INTR_CTRL(IRQ_LEVEL) | + SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) | + SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16)); + + + cfg = SOLO_VE_BYTE_ALIGN(2) | SOLO_VE_INSERT_INDEX + | SOLO_VE_MOTION_MODE(0); + if (solo_dev->type != SOLO_DEV_6010) { + cfg |= SOLO_VE_MPEG_SIZE_H( + (SOLO_MP4E_EXT_SIZE(solo_dev) >> 24) & 0x0f); + cfg |= SOLO_VE_JPEG_SIZE_H( + (SOLO_JPEG_EXT_SIZE(solo_dev) >> 24) & 0x0f); + } + solo_reg_write(solo_dev, SOLO_VE_CFG1, cfg); + + solo_reg_write(solo_dev, SOLO_VE_WMRK_POLY, 0); + solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0); + solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, 0); + if (solo_dev->type == SOLO_DEV_6110) + solo_reg_write(solo_dev, SOLO_VE_WMRK_ENABLE, 0); + solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0); + solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0); + + solo_reg_write(solo_dev, SOLO_VE_ATTR, + SOLO_VE_LITTLE_ENDIAN | + SOLO_COMP_ATTR_FCODE(1) | + SOLO_COMP_TIME_INC(0) | + SOLO_COMP_TIME_WIDTH(15) | + SOLO_DCT_INTERVAL(solo_dev->type == SOLO_DEV_6010 ? 9 : 10)); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i), + (SOLO_EREF_EXT_ADDR(solo_dev) + + (i * SOLO_EREF_EXT_SIZE)) >> 16); + solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE_E(i), + (SOLO_EREF_EXT_ADDR(solo_dev) + + ((i + 16) * SOLO_EREF_EXT_SIZE)) >> 16); + } + + if (solo_dev->type == SOLO_DEV_6110) { + solo_reg_write(solo_dev, SOLO_VE_COMPT_MOT, 0x00040008); + } else { + for (i = 0; i < solo_dev->nr_chans; i++) + solo_reg_write(solo_dev, SOLO_VE_CH_MOT(i), 0x100); + } +} + +int solo_enc_init(struct solo_dev *solo_dev) +{ + int i; + + solo_capture_config(solo_dev); + solo_mp4e_config(solo_dev); + solo_jpeg_config(solo_dev); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); + } + + return 0; +} + +void solo_enc_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); + } +} diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c new file mode 100644 index 0000000..c7141f2 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "solo6x10.h" +#include "solo6x10-tw28.h" + +#define G723_FDMA_PAGES 32 +#define G723_PERIOD_BYTES 48 +#define G723_PERIOD_BLOCK 1024 +#define G723_FRAMES_PER_PAGE 48 + +/* Sets up channels 16-19 for decoding and 0-15 for encoding */ +#define OUTMODE_MASK 0x300 + +#define SAMPLERATE 8000 +#define BITRATE 25 + +/* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page + * is broken down to 20 * 48 byte regions (one for each channel possible) + * with the rest of the page being dummy data. */ +#define G723_MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) +#define G723_INTR_ORDER 4 /* 0 - 4 */ +#define PERIODS_MIN (1 << G723_INTR_ORDER) +#define PERIODS_MAX G723_FDMA_PAGES + +struct solo_snd_pcm { + int on; + spinlock_t lock; + struct solo_dev *solo_dev; + unsigned char *g723_buf; + dma_addr_t g723_dma; +}; + +static void solo_g723_config(struct solo_dev *solo_dev) +{ + int clk_div; + + clk_div = (solo_dev->clock_mhz * 1000000) + / (SAMPLERATE * (BITRATE * 2) * 2); + + solo_reg_write(solo_dev, SOLO_AUDIO_SAMPLE, + SOLO_AUDIO_BITRATE(BITRATE) + | SOLO_AUDIO_CLK_DIV(clk_div)); + + solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR, + SOLO_AUDIO_FDMA_INTERVAL(1) + | SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) + | SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); + + solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, + SOLO_AUDIO_ENABLE + | SOLO_AUDIO_I2S_MODE + | SOLO_AUDIO_I2S_MULTI(3) + | SOLO_AUDIO_MODE(OUTMODE_MASK)); +} + +void solo_g723_isr(struct solo_dev *solo_dev) +{ + struct snd_pcm_str *pstr = + &solo_dev->snd_pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; + struct snd_pcm_substream *ss; + struct solo_snd_pcm *solo_pcm; + + for (ss = pstr->substream; ss != NULL; ss = ss->next) { + if (snd_pcm_substream_chip(ss) == NULL) + continue; + + /* This means open() hasn't been called on this one */ + if (snd_pcm_substream_chip(ss) == solo_dev) + continue; + + /* Haven't triggered a start yet */ + solo_pcm = snd_pcm_substream_chip(ss); + if (!solo_pcm->on) + continue; + + snd_pcm_period_elapsed(ss); + } +} + +static int snd_solo_hw_params(struct snd_pcm_substream *ss, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); +} + +static int snd_solo_hw_free(struct snd_pcm_substream *ss) +{ + return snd_pcm_lib_free_pages(ss); +} + +static const struct snd_pcm_hardware snd_solo_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8, + .rates = SNDRV_PCM_RATE_8000, + .rate_min = SAMPLERATE, + .rate_max = SAMPLERATE, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = G723_MAX_BUFFER, + .period_bytes_min = G723_PERIOD_BYTES, + .period_bytes_max = G723_PERIOD_BYTES, + .periods_min = PERIODS_MIN, + .periods_max = PERIODS_MAX, +}; + +static int snd_solo_pcm_open(struct snd_pcm_substream *ss) +{ + struct solo_dev *solo_dev = snd_pcm_substream_chip(ss); + struct solo_snd_pcm *solo_pcm; + + solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL); + if (solo_pcm == NULL) + goto oom; + + solo_pcm->g723_buf = pci_alloc_consistent(solo_dev->pdev, + G723_PERIOD_BYTES, + &solo_pcm->g723_dma); + if (solo_pcm->g723_buf == NULL) + goto oom; + + spin_lock_init(&solo_pcm->lock); + solo_pcm->solo_dev = solo_dev; + ss->runtime->hw = snd_solo_pcm_hw; + + snd_pcm_substream_chip(ss) = solo_pcm; + + return 0; + +oom: + kfree(solo_pcm); + return -ENOMEM; +} + +static int snd_solo_pcm_close(struct snd_pcm_substream *ss) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + + snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; + pci_free_consistent(solo_pcm->solo_dev->pdev, G723_PERIOD_BYTES, + solo_pcm->g723_buf, solo_pcm->g723_dma); + kfree(solo_pcm); + + return 0; +} + +static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo_dev *solo_dev = solo_pcm->solo_dev; + int ret = 0; + + spin_lock(&solo_pcm->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (solo_pcm->on == 0) { + /* If this is the first user, switch on interrupts */ + if (atomic_inc_return(&solo_dev->snd_users) == 1) + solo_irq_on(solo_dev, SOLO_IRQ_G723); + solo_pcm->on = 1; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (solo_pcm->on) { + /* If this was our last user, switch them off */ + if (atomic_dec_return(&solo_dev->snd_users) == 0) + solo_irq_off(solo_dev, SOLO_IRQ_G723); + solo_pcm->on = 0; + } + break; + default: + ret = -EINVAL; + } + + spin_unlock(&solo_pcm->lock); + + return ret; +} + +static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss) +{ + return 0; +} + +static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo_dev *solo_dev = solo_pcm->solo_dev; + snd_pcm_uframes_t idx = solo_reg_read(solo_dev, SOLO_AUDIO_STA) & 0x1f; + + return idx * G723_FRAMES_PER_PAGE; +} + +static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, + snd_pcm_uframes_t pos, void __user *dst, + snd_pcm_uframes_t count) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo_dev *solo_dev = solo_pcm->solo_dev; + int err, i; + + for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { + int page = (pos / G723_FRAMES_PER_PAGE) + i; + + err = solo_p2m_dma_t(solo_dev, 0, solo_pcm->g723_dma, + SOLO_G723_EXT_ADDR(solo_dev) + + (page * G723_PERIOD_BLOCK) + + (ss->number * G723_PERIOD_BYTES), + G723_PERIOD_BYTES, 0, 0); + if (err) + return err; + + err = copy_to_user(dst + (i * G723_PERIOD_BYTES), + solo_pcm->g723_buf, G723_PERIOD_BYTES); + + if (err) + return -EFAULT; + } + + return 0; +} + +static struct snd_pcm_ops snd_solo_pcm_ops = { + .open = snd_solo_pcm_open, + .close = snd_solo_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_solo_hw_params, + .hw_free = snd_solo_hw_free, + .prepare = snd_solo_pcm_prepare, + .trigger = snd_solo_pcm_trigger, + .pointer = snd_solo_pcm_pointer, + .copy = snd_solo_pcm_copy, +}; + +static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 1; + info->value.integer.min = 0; + info->value.integer.max = 15; + info->value.integer.step = 1; + + return 0; +} + +static int snd_solo_capture_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); + u8 ch = value->id.numid - 1; + + value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch); + + return 0; +} + +static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); + u8 ch = value->id.numid - 1; + u8 old_val; + + old_val = tw28_get_audio_gain(solo_dev, ch); + if (old_val == value->value.integer.value[0]) + return 0; + + tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]); + + return 1; +} + +static struct snd_kcontrol_new snd_solo_capture_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .info = snd_solo_capture_volume_info, + .get = snd_solo_capture_volume_get, + .put = snd_solo_capture_volume_put, +}; + +static int solo_snd_pcm_init(struct solo_dev *solo_dev) +{ + struct snd_card *card = solo_dev->snd_card; + struct snd_pcm *pcm; + struct snd_pcm_substream *ss; + int ret; + int i; + + ret = snd_pcm_new(card, card->driver, 0, 0, solo_dev->nr_chans, + &pcm); + if (ret < 0) + return ret; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_solo_pcm_ops); + + snd_pcm_chip(pcm) = solo_dev; + pcm->info_flags = 0; + strcpy(pcm->name, card->shortname); + + for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + ss; ss = ss->next, i++) + sprintf(ss->name, "Camera #%d Audio", i); + + ret = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + G723_MAX_BUFFER, G723_MAX_BUFFER); + if (ret < 0) + return ret; + + solo_dev->snd_pcm = pcm; + + return 0; +} + +int solo_g723_init(struct solo_dev *solo_dev) +{ + static struct snd_device_ops ops = { NULL }; + struct snd_card *card; + struct snd_kcontrol_new kctl; + char name[32]; + int ret; + + atomic_set(&solo_dev->snd_users, 0); + + /* Allows for easier mapping between video and audio */ + sprintf(name, "Softlogic%d", solo_dev->vfd->num); + + ret = snd_card_new(&solo_dev->pdev->dev, + SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0, + &solo_dev->snd_card); + if (ret < 0) + return ret; + + card = solo_dev->snd_card; + + strcpy(card->driver, SOLO6X10_NAME); + strcpy(card->shortname, "SOLO-6x10 Audio"); + sprintf(card->longname, "%s on %s IRQ %d", card->shortname, + pci_name(solo_dev->pdev), solo_dev->pdev->irq); + + ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops); + if (ret < 0) + goto snd_error; + + /* Mixer controls */ + strcpy(card->mixername, "SOLO-6x10"); + kctl = snd_solo_capture_volume; + kctl.count = solo_dev->nr_chans; + + ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); + if (ret < 0) + return ret; + + ret = solo_snd_pcm_init(solo_dev); + if (ret < 0) + goto snd_error; + + ret = snd_card_register(card); + if (ret < 0) + goto snd_error; + + solo_g723_config(solo_dev); + + dev_info(&solo_dev->pdev->dev, "Alsa sound card as %s\n", name); + + return 0; + +snd_error: + snd_card_free(card); + return ret; +} + +void solo_g723_exit(struct solo_dev *solo_dev) +{ + if (!solo_dev->snd_card) + return; + + solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0); + solo_irq_off(solo_dev, SOLO_IRQ_G723); + + snd_card_free(solo_dev->snd_card); + solo_dev->snd_card = NULL; +} diff --git a/drivers/media/pci/solo6x10/solo6x10-gpio.c b/drivers/media/pci/solo6x10/solo6x10-gpio.c new file mode 100644 index 0000000..6d3b4a3 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-gpio.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 +#include +#include +#include + +#include "solo6x10.h" + +static void solo_gpio_mode(struct solo_dev *solo_dev, + unsigned int port_mask, unsigned int mode) +{ + int port; + unsigned int ret; + + ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0); + + /* To set gpio */ + for (port = 0; port < 16; port++) { + if (!((1 << port) & port_mask)) + continue; + + ret &= (~(3 << (port << 1))); + ret |= ((mode & 3) << (port << 1)); + } + + solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_0, ret); + + /* To set extended gpio - sensor */ + ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1); + + for (port = 0; port < 16; port++) { + if (!((1 << (port + 16)) & port_mask)) + continue; + + if (!mode) + ret &= ~(1 << port); + else + ret |= 1 << port; + } + + solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_1, ret); +} + +static void solo_gpio_set(struct solo_dev *solo_dev, unsigned int value) +{ + solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, + solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) | value); +} + +static void solo_gpio_clear(struct solo_dev *solo_dev, unsigned int value) +{ + solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, + solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) & ~value); +} + +static void solo_gpio_config(struct solo_dev *solo_dev) +{ + /* Video reset */ + solo_gpio_mode(solo_dev, 0x30, 1); + solo_gpio_clear(solo_dev, 0x30); + udelay(100); + solo_gpio_set(solo_dev, 0x30); + udelay(100); + + /* Warning: Don't touch the next line unless you're sure of what + * you're doing: first four gpio [0-3] are used for video. */ + solo_gpio_mode(solo_dev, 0x0f, 2); + + /* We use bit 8-15 of SOLO_GPIO_CONFIG_0 for relay purposes */ + solo_gpio_mode(solo_dev, 0xff00, 1); + + /* Initially set relay status to 0 */ + solo_gpio_clear(solo_dev, 0xff00); +} + +int solo_gpio_init(struct solo_dev *solo_dev) +{ + solo_gpio_config(solo_dev); + return 0; +} + +void solo_gpio_exit(struct solo_dev *solo_dev) +{ + solo_gpio_clear(solo_dev, 0x30); + solo_gpio_config(solo_dev); +} diff --git a/drivers/media/pci/solo6x10/solo6x10-i2c.c b/drivers/media/pci/solo6x10/solo6x10-i2c.c new file mode 100644 index 0000000..c908672 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-i2c.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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. + */ + +/* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c + * channel. The bus can only handle one i2c event at a time. The below handles + * this all wrong. We should be using the status registers to see if the bus + * is in use, and have a global lock to check the status register. Also, + * the bulk of the work should be handled out-of-interrupt. The ugly loops + * that occur during interrupt scare me. The ISR should merely signal + * thread context, ACK the interrupt, and move on. -- BenC */ + +#include + +#include "solo6x10.h" + +u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) +{ + struct i2c_msg msgs[2]; + u8 data; + + msgs[0].flags = 0; + msgs[0].addr = addr; + msgs[0].len = 1; + msgs[0].buf = &off; + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = addr; + msgs[1].len = 1; + msgs[1].buf = &data; + + i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2); + + return data; +} + +void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, + u8 off, u8 data) +{ + struct i2c_msg msgs; + u8 buf[2]; + + buf[0] = off; + buf[1] = data; + msgs.flags = 0; + msgs.addr = addr; + msgs.len = 2; + msgs.buf = buf; + + i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1); +} + +static void solo_i2c_flush(struct solo_dev *solo_dev, int wr) +{ + u32 ctrl; + + ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); + + if (solo_dev->i2c_state == IIC_STATE_START) + ctrl |= SOLO_IIC_START; + + if (wr) { + ctrl |= SOLO_IIC_WRITE; + } else { + ctrl |= SOLO_IIC_READ; + if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) + ctrl |= SOLO_IIC_ACK_EN; + } + + if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) + ctrl |= SOLO_IIC_STOP; + + solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl); +} + +static void solo_i2c_start(struct solo_dev *solo_dev) +{ + u32 addr = solo_dev->i2c_msg->addr << 1; + + if (solo_dev->i2c_msg->flags & I2C_M_RD) + addr |= 1; + + solo_dev->i2c_state = IIC_STATE_START; + solo_reg_write(solo_dev, SOLO_IIC_TXD, addr); + solo_i2c_flush(solo_dev, 1); +} + +static void solo_i2c_stop(struct solo_dev *solo_dev) +{ + solo_irq_off(solo_dev, SOLO_IRQ_IIC); + solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); + solo_dev->i2c_state = IIC_STATE_STOP; + wake_up(&solo_dev->i2c_wait); +} + +static int solo_i2c_handle_read(struct solo_dev *solo_dev) +{ +prepare_read: + if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { + solo_i2c_flush(solo_dev, 0); + return 0; + } + + solo_dev->i2c_msg_ptr = 0; + solo_dev->i2c_msg++; + solo_dev->i2c_msg_num--; + + if (solo_dev->i2c_msg_num == 0) { + solo_i2c_stop(solo_dev); + return 0; + } + + if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { + solo_i2c_start(solo_dev); + } else { + if (solo_dev->i2c_msg->flags & I2C_M_RD) + goto prepare_read; + else + solo_i2c_stop(solo_dev); + } + + return 0; +} + +static int solo_i2c_handle_write(struct solo_dev *solo_dev) +{ +retry_write: + if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { + solo_reg_write(solo_dev, SOLO_IIC_TXD, + solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); + solo_dev->i2c_msg_ptr++; + solo_i2c_flush(solo_dev, 1); + return 0; + } + + solo_dev->i2c_msg_ptr = 0; + solo_dev->i2c_msg++; + solo_dev->i2c_msg_num--; + + if (solo_dev->i2c_msg_num == 0) { + solo_i2c_stop(solo_dev); + return 0; + } + + if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { + solo_i2c_start(solo_dev); + } else { + if (solo_dev->i2c_msg->flags & I2C_M_RD) + solo_i2c_stop(solo_dev); + else + goto retry_write; + } + + return 0; +} + +int solo_i2c_isr(struct solo_dev *solo_dev) +{ + u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); + int ret = -EINVAL; + + + if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) + || solo_dev->i2c_id < 0) { + solo_i2c_stop(solo_dev); + return -ENXIO; + } + + switch (solo_dev->i2c_state) { + case IIC_STATE_START: + if (solo_dev->i2c_msg->flags & I2C_M_RD) { + solo_dev->i2c_state = IIC_STATE_READ; + ret = solo_i2c_handle_read(solo_dev); + break; + } + + solo_dev->i2c_state = IIC_STATE_WRITE; + case IIC_STATE_WRITE: + ret = solo_i2c_handle_write(solo_dev); + break; + + case IIC_STATE_READ: + solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = + solo_reg_read(solo_dev, SOLO_IIC_RXD); + solo_dev->i2c_msg_ptr++; + + ret = solo_i2c_handle_read(solo_dev); + break; + + default: + solo_i2c_stop(solo_dev); + } + + return ret; +} + +static int solo_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct solo_dev *solo_dev = adap->algo_data; + unsigned long timeout; + int ret; + int i; + DEFINE_WAIT(wait); + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (&solo_dev->i2c_adap[i] == adap) + break; + } + + if (i == SOLO_I2C_ADAPTERS) + return num; /* XXX Right return value for failure? */ + + mutex_lock(&solo_dev->i2c_mutex); + solo_dev->i2c_id = i; + solo_dev->i2c_msg = msgs; + solo_dev->i2c_msg_num = num; + solo_dev->i2c_msg_ptr = 0; + + solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); + solo_irq_on(solo_dev, SOLO_IRQ_IIC); + solo_i2c_start(solo_dev); + + timeout = HZ / 2; + + for (;;) { + prepare_to_wait(&solo_dev->i2c_wait, &wait, + TASK_INTERRUPTIBLE); + + if (solo_dev->i2c_state == IIC_STATE_STOP) + break; + + timeout = schedule_timeout(timeout); + if (!timeout) + break; + + if (signal_pending(current)) + break; + } + + finish_wait(&solo_dev->i2c_wait, &wait); + ret = num - solo_dev->i2c_msg_num; + solo_dev->i2c_state = IIC_STATE_IDLE; + solo_dev->i2c_id = -1; + + mutex_unlock(&solo_dev->i2c_mutex); + + return ret; +} + +static u32 solo_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm solo_i2c_algo = { + .master_xfer = solo_i2c_master_xfer, + .functionality = solo_i2c_functionality, +}; + +int solo_i2c_init(struct solo_dev *solo_dev) +{ + int i; + int ret; + + solo_reg_write(solo_dev, SOLO_IIC_CFG, + SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); + + solo_dev->i2c_id = -1; + solo_dev->i2c_state = IIC_STATE_IDLE; + init_waitqueue_head(&solo_dev->i2c_wait); + mutex_init(&solo_dev->i2c_mutex); + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; + + snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", + SOLO6X10_NAME, i); + adap->algo = &solo_i2c_algo; + adap->algo_data = solo_dev; + adap->retries = 1; + adap->dev.parent = &solo_dev->pdev->dev; + + ret = i2c_add_adapter(adap); + if (ret) { + adap->algo_data = NULL; + break; + } + } + + if (ret) { + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (!solo_dev->i2c_adap[i].algo_data) + break; + i2c_del_adapter(&solo_dev->i2c_adap[i]); + solo_dev->i2c_adap[i].algo_data = NULL; + } + return ret; + } + + return 0; +} + +void solo_i2c_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (!solo_dev->i2c_adap[i].algo_data) + continue; + i2c_del_adapter(&solo_dev->i2c_adap[i]); + solo_dev->i2c_adap[i].algo_data = NULL; + } +} diff --git a/drivers/media/pci/solo6x10/solo6x10-jpeg.h b/drivers/media/pci/solo6x10/solo6x10-jpeg.h new file mode 100644 index 0000000..1c66a46 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-jpeg.h @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 __SOLO6X10_JPEG_H +#define __SOLO6X10_JPEG_H + +static const unsigned char jpeg_header[] = { + 0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c, + 0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79, + 0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16, + 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c, + 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, + 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a, 0x3a, 0x50, + 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, 0x70, 0x6e, + 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, + 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, + 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2, 0xf2, 0xe0, + 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0xff, 0xdb, + 0x00, 0x43, 0x01, 0x22, 0x24, 0x24, 0x30, 0x2a, + 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6, 0x84, 0x70, + 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xff, 0xc4, 0x01, 0xa2, 0x00, + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, + 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, + 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, + 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, + 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, + 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, + 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, + 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, + 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, + 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, + 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, + 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, + 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, + 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, + 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, + 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, + 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, + 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, + 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, + 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, + 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, + 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, + 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, + 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, + 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, + 0xc0, 0x00, 0x11, 0x08, 0x00, 0xf0, 0x02, 0xc0, + 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, + 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, + 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +}; + +/* This is the byte marker for the start of SOF0: 0xffc0 marker */ +#define SOF0_START 575 + +/* This is the byte marker for the start of the DQT */ +#define DQT_START 17 +#define DQT_LEN 138 +static const unsigned char jpeg_dqt[4][DQT_LEN] = { + { + 0xff, 0xdb, 0x00, 0x43, 0x00, + 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, + 0x07, 0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, + 0x0d, 0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13, + 0x0f, 0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, + 0x1c, 0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22, + 0x2c, 0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, + 0x30, 0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39, + 0x3d, 0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, + 0xff, 0xdb, 0x00, 0x43, 0x01, + 0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0d, + 0x0d, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32 + }, { + 0xff, 0xdb, 0x00, 0x43, 0x00, + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + 0xff, 0xdb, 0x00, 0x43, 0x01, + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 + }, { + 0xff, 0xdb, 0x00, 0x43, 0x00, + 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, + 0x1a, 0x1c, 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, + 0x34, 0x30, 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a, + 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, + 0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, + 0xae, 0x8a, 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae, + 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2, + 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, + 0xff, 0xdb, 0x00, 0x43, 0x01, + 0x22, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, + 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6 + }, { + 0xff, 0xdb, 0x00, 0x43, 0x00, + 0x30, 0x21, 0x24, 0x2a, 0x24, 0x1e, 0x30, 0x2a, + 0x27, 0x2a, 0x36, 0x33, 0x30, 0x39, 0x48, 0x78, + 0x4e, 0x48, 0x42, 0x42, 0x48, 0x93, 0x69, 0x6f, + 0x57, 0x78, 0xae, 0x99, 0xb7, 0xb4, 0xab, 0x99, + 0xa8, 0xa5, 0xc0, 0xd8, 0xff, 0xea, 0xc0, 0xcc, + 0xff, 0xcf, 0xa5, 0xa8, 0xf0, 0xff, 0xf3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xba, 0xe7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xdb, 0x00, 0x43, 0x01, + 0x33, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e, + 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + } +}; + +#endif /* __SOLO6X10_JPEG_H */ diff --git a/drivers/media/pci/solo6x10/solo6x10-offsets.h b/drivers/media/pci/solo6x10/solo6x10-offsets.h new file mode 100644 index 0000000..d6aea7c --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-offsets.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 __SOLO6X10_OFFSETS_H +#define __SOLO6X10_OFFSETS_H + +#define SOLO_DISP_EXT_ADDR 0x00000000 +#define SOLO_DISP_EXT_SIZE 0x00480000 + +#define SOLO_EOSD_EXT_ADDR \ + (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE) +#define SOLO_EOSD_EXT_SIZE(__solo) \ + (__solo->type == SOLO_DEV_6010 ? 0x10000 : 0x20000) +#define SOLO_EOSD_EXT_SIZE_MAX 0x20000 +#define SOLO_EOSD_EXT_AREA(__solo) \ + (SOLO_EOSD_EXT_SIZE(__solo) * 32) +#define SOLO_EOSD_EXT_ADDR_CHAN(__solo, ch) \ + (SOLO_EOSD_EXT_ADDR + SOLO_EOSD_EXT_SIZE(__solo) * (ch)) + +#define SOLO_MOTION_EXT_ADDR(__solo) \ + (SOLO_EOSD_EXT_ADDR + SOLO_EOSD_EXT_AREA(__solo)) +#define SOLO_MOTION_EXT_SIZE 0x00080000 + +#define SOLO_G723_EXT_ADDR(__solo) \ + (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE) +#define SOLO_G723_EXT_SIZE 0x00010000 + +#define SOLO_CAP_EXT_ADDR(__solo) \ + (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE) + +/* 18 is the maximum number of pages required for PAL@D1, the largest frame + * possible */ +#define SOLO_CAP_PAGE_SIZE (18 << 16) + +/* Always allow the encoder enough for 16 channels, even if we have less. The + * exception is if we have card with only 32Megs of memory. */ +#define SOLO_CAP_EXT_SIZE(__solo) \ + ((((__solo->sdram_size <= (32 << 20)) ? 4 : 16) + 1) \ + * SOLO_CAP_PAGE_SIZE) + +#define SOLO_EREF_EXT_ADDR(__solo) \ + (SOLO_CAP_EXT_ADDR(__solo) + SOLO_CAP_EXT_SIZE(__solo)) +#define SOLO_EREF_EXT_SIZE 0x00140000 +#define SOLO_EREF_EXT_AREA(__solo) \ + (SOLO_EREF_EXT_SIZE * __solo->nr_chans * 2) + +#define __SOLO_JPEG_MIN_SIZE(__solo) (__solo->nr_chans * 0x00080000) + +#define SOLO_MP4E_EXT_ADDR(__solo) \ + (SOLO_EREF_EXT_ADDR(__solo) + SOLO_EREF_EXT_AREA(__solo)) +#define SOLO_MP4E_EXT_SIZE(__solo) \ + max((__solo->nr_chans * 0x00080000), \ + min(((__solo->sdram_size - SOLO_MP4E_EXT_ADDR(__solo)) - \ + __SOLO_JPEG_MIN_SIZE(__solo)), 0x00ff0000)) + +#define __SOLO_JPEG_MIN_SIZE(__solo) (__solo->nr_chans * 0x00080000) +#define SOLO_JPEG_EXT_ADDR(__solo) \ + (SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo)) +#define SOLO_JPEG_EXT_SIZE(__solo) \ + max(__SOLO_JPEG_MIN_SIZE(__solo), \ + min((__solo->sdram_size - SOLO_JPEG_EXT_ADDR(__solo)), 0x00ff0000)) + +#define SOLO_SDRAM_END(__solo) \ + (SOLO_JPEG_EXT_ADDR(__solo) + SOLO_JPEG_EXT_SIZE(__solo)) + +#endif /* __SOLO6X10_OFFSETS_H */ diff --git a/drivers/media/pci/solo6x10/solo6x10-p2m.c b/drivers/media/pci/solo6x10/solo6x10-p2m.c new file mode 100644 index 0000000..8c84846 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-p2m.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 +#include +#include + +#include "solo6x10.h" + +static int multi_p2m; +module_param(multi_p2m, uint, 0644); +MODULE_PARM_DESC(multi_p2m, + "Use multiple P2M DMA channels (default: no, 6010-only)"); + +static int desc_mode; +module_param(desc_mode, uint, 0644); +MODULE_PARM_DESC(desc_mode, + "Allow use of descriptor mode DMA (default: no, 6010-only)"); + +int solo_p2m_dma(struct solo_dev *solo_dev, int wr, + void *sys_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size) +{ + dma_addr_t dma_addr; + int ret; + + if (WARN_ON_ONCE((unsigned long)sys_addr & 0x03)) + return -EINVAL; + if (WARN_ON_ONCE(!size)) + return -EINVAL; + + dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, + wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(solo_dev->pdev, dma_addr)) + return -ENOMEM; + + ret = solo_p2m_dma_t(solo_dev, wr, dma_addr, ext_addr, size, + repeat, ext_size); + + pci_unmap_single(solo_dev->pdev, dma_addr, size, + wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + return ret; +} + +/* Mutex must be held for p2m_id before calling this!! */ +int solo_p2m_dma_desc(struct solo_dev *solo_dev, + struct solo_p2m_desc *desc, dma_addr_t desc_dma, + int desc_cnt) +{ + struct solo_p2m_dev *p2m_dev; + unsigned int timeout; + unsigned int config = 0; + int ret = 0; + int p2m_id = 0; + + /* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */ + if (solo_dev->type != SOLO_DEV_6110 && multi_p2m) { + p2m_id = atomic_inc_return(&solo_dev->p2m_count) % SOLO_NR_P2M; + if (p2m_id < 0) + p2m_id = -p2m_id; + } + + p2m_dev = &solo_dev->p2m_dev[p2m_id]; + + if (mutex_lock_interruptible(&p2m_dev->mutex)) + return -EINTR; + + reinit_completion(&p2m_dev->completion); + p2m_dev->error = 0; + + if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && desc_mode) { + /* For 6010 with more than one desc, we can do a one-shot */ + p2m_dev->desc_count = p2m_dev->desc_idx = 0; + config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(p2m_id)); + + solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(p2m_id), desc_dma); + solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(p2m_id), desc_cnt); + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config | + SOLO_P2M_DESC_MODE); + } else { + /* For single descriptors and 6110, we need to run each desc */ + p2m_dev->desc_count = desc_cnt; + p2m_dev->desc_idx = 1; + p2m_dev->descs = desc; + + solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(p2m_id), + desc[1].dma_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(p2m_id), + desc[1].ext_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(p2m_id), + desc[1].cfg); + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), + desc[1].ctrl); + } + + timeout = wait_for_completion_timeout(&p2m_dev->completion, + solo_dev->p2m_jiffies); + + if (WARN_ON_ONCE(p2m_dev->error)) + ret = -EIO; + else if (timeout == 0) { + solo_dev->p2m_timeouts++; + ret = -EAGAIN; + } + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), 0); + + /* Don't write here for the no_desc_mode case, because config is 0. + * We can't test no_desc_mode again, it might race. */ + if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && config) + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config); + + mutex_unlock(&p2m_dev->mutex); + + return ret; +} + +void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size) +{ + WARN_ON_ONCE(dma_addr & 0x03); + WARN_ON_ONCE(!size); + + desc->cfg = SOLO_P2M_COPY_SIZE(size >> 2); + desc->ctrl = SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | + (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON; + + if (repeat) { + desc->cfg |= SOLO_P2M_EXT_INC(ext_size >> 2); + desc->ctrl |= SOLO_P2M_PCI_INC(size >> 2) | + SOLO_P2M_REPEAT(repeat); + } + + desc->dma_addr = dma_addr; + desc->ext_addr = ext_addr; +} + +int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size) +{ + struct solo_p2m_desc desc[2]; + + solo_p2m_fill_desc(&desc[1], wr, dma_addr, ext_addr, size, repeat, + ext_size); + + /* No need for desc_dma since we know it is a single-shot */ + return solo_p2m_dma_desc(solo_dev, desc, 0, 1); +} + +void solo_p2m_isr(struct solo_dev *solo_dev, int id) +{ + struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id]; + struct solo_p2m_desc *desc; + + if (p2m_dev->desc_count <= p2m_dev->desc_idx) { + complete(&p2m_dev->completion); + return; + } + + /* Setup next descriptor */ + p2m_dev->desc_idx++; + desc = &p2m_dev->descs[p2m_dev->desc_idx]; + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); + solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), desc->dma_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), desc->ext_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id), desc->cfg); + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), desc->ctrl); +} + +void solo_p2m_error_isr(struct solo_dev *solo_dev) +{ + unsigned int err = solo_reg_read(solo_dev, SOLO_PCI_ERR); + struct solo_p2m_dev *p2m_dev; + int i; + + if (!(err & (SOLO_PCI_ERR_P2M | SOLO_PCI_ERR_P2M_DESC))) + return; + + for (i = 0; i < SOLO_NR_P2M; i++) { + p2m_dev = &solo_dev->p2m_dev[i]; + p2m_dev->error = 1; + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); + complete(&p2m_dev->completion); + } +} + +void solo_p2m_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < SOLO_NR_P2M; i++) + solo_irq_off(solo_dev, SOLO_IRQ_P2M(i)); +} + +static int solo_p2m_test(struct solo_dev *solo_dev, int base, int size) +{ + u32 *wr_buf; + u32 *rd_buf; + int i; + int ret = -EIO; + int order = get_order(size); + + wr_buf = (u32 *)__get_free_pages(GFP_KERNEL, order); + if (wr_buf == NULL) + return -1; + + rd_buf = (u32 *)__get_free_pages(GFP_KERNEL, order); + if (rd_buf == NULL) { + free_pages((unsigned long)wr_buf, order); + return -1; + } + + for (i = 0; i < (size >> 3); i++) + *(wr_buf + i) = (i << 16) | (i + 1); + + for (i = (size >> 3); i < (size >> 2); i++) + *(wr_buf + i) = ~((i << 16) | (i + 1)); + + memset(rd_buf, 0x55, size); + + if (solo_p2m_dma(solo_dev, 1, wr_buf, base, size, 0, 0)) + goto test_fail; + + if (solo_p2m_dma(solo_dev, 0, rd_buf, base, size, 0, 0)) + goto test_fail; + + for (i = 0; i < (size >> 2); i++) { + if (*(wr_buf + i) != *(rd_buf + i)) + goto test_fail; + } + + ret = 0; + +test_fail: + free_pages((unsigned long)wr_buf, order); + free_pages((unsigned long)rd_buf, order); + + return ret; +} + +int solo_p2m_init(struct solo_dev *solo_dev) +{ + struct solo_p2m_dev *p2m_dev; + int i; + + for (i = 0; i < SOLO_NR_P2M; i++) { + p2m_dev = &solo_dev->p2m_dev[i]; + + mutex_init(&p2m_dev->mutex); + init_completion(&p2m_dev->completion); + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), + SOLO_P2M_CSC_16BIT_565 | + SOLO_P2M_DESC_INTR_OPT | + SOLO_P2M_DMA_INTERVAL(0) | + SOLO_P2M_PCI_MASTER_MODE); + solo_irq_on(solo_dev, SOLO_IRQ_P2M(i)); + } + + /* Find correct SDRAM size */ + for (solo_dev->sdram_size = 0, i = 2; i >= 0; i--) { + solo_reg_write(solo_dev, SOLO_DMA_CTRL, + SOLO_DMA_CTRL_REFRESH_CYCLE(1) | + SOLO_DMA_CTRL_SDRAM_SIZE(i) | + SOLO_DMA_CTRL_SDRAM_CLK_INVERT | + SOLO_DMA_CTRL_READ_CLK_SELECT | + SOLO_DMA_CTRL_LATENCY(1)); + + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config | + SOLO_SYS_CFG_RESET); + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); + + switch (i) { + case 2: + if (solo_p2m_test(solo_dev, 0x07ff0000, 0x00010000) || + solo_p2m_test(solo_dev, 0x05ff0000, 0x00010000)) + continue; + break; + + case 1: + if (solo_p2m_test(solo_dev, 0x03ff0000, 0x00010000)) + continue; + break; + + default: + if (solo_p2m_test(solo_dev, 0x01ff0000, 0x00010000)) + continue; + } + + solo_dev->sdram_size = (32 << 20) << i; + break; + } + + if (!solo_dev->sdram_size) { + dev_err(&solo_dev->pdev->dev, "Error detecting SDRAM size\n"); + return -EIO; + } + + if (SOLO_SDRAM_END(solo_dev) > solo_dev->sdram_size) { + dev_err(&solo_dev->pdev->dev, + "SDRAM is not large enough (%u < %u)\n", + solo_dev->sdram_size, SOLO_SDRAM_END(solo_dev)); + return -EIO; + } + + return 0; +} diff --git a/drivers/media/pci/solo6x10/solo6x10-regs.h b/drivers/media/pci/solo6x10/solo6x10-regs.h new file mode 100644 index 0000000..e34ac56 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-regs.h @@ -0,0 +1,635 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 __SOLO6X10_REGISTERS_H +#define __SOLO6X10_REGISTERS_H + +#include "solo6x10-offsets.h" + +/* Global 6010 system configuration */ +#define SOLO_SYS_CFG 0x0000 +#define SOLO_SYS_CFG_FOUT_EN 0x00000001 +#define SOLO_SYS_CFG_PLL_BYPASS 0x00000002 +#define SOLO_SYS_CFG_PLL_PWDN 0x00000004 +#define SOLO_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) +#define SOLO_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) +#define SOLO_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) +#define SOLO_SYS_CFG_CLOCK_DIV 0x00080000 +#define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24) +#define SOLO_SYS_CFG_PCLK_DELAY(__n) (((__n) & 0x00f) << 26) +#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000 +#define SOLO_SYS_CFG_RESET 0x80000000 + +#define SOLO_DMA_CTRL 0x0004 +#define SOLO_DMA_CTRL_REFRESH_CYCLE(n) ((n)<<8) +/* 0=16/32MB, 1=32/64MB, 2=64/128MB, 3=128/256MB */ +#define SOLO_DMA_CTRL_SDRAM_SIZE(n) ((n)<<6) +#define SOLO_DMA_CTRL_SDRAM_CLK_INVERT (1<<5) +#define SOLO_DMA_CTRL_STROBE_SELECT (1<<4) +#define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3) +#define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2) +#define SOLO_DMA_CTRL_LATENCY(n) ((n)<<0) + +/* Some things we set in this are undocumented. Why Softlogic?!?! */ +#define SOLO_DMA_CTRL1 0x0008 + +#define SOLO_SYS_VCLK 0x000C +#define SOLO_VCLK_INVERT (1<<22) +/* 0=sys_clk/4, 1=sys_clk/2, 2=clk_in/2 of system input */ +#define SOLO_VCLK_SELECT(n) ((n)<<20) +#define SOLO_VCLK_VIN1415_DELAY(n) ((n)<<14) +#define SOLO_VCLK_VIN1213_DELAY(n) ((n)<<12) +#define SOLO_VCLK_VIN1011_DELAY(n) ((n)<<10) +#define SOLO_VCLK_VIN0809_DELAY(n) ((n)<<8) +#define SOLO_VCLK_VIN0607_DELAY(n) ((n)<<6) +#define SOLO_VCLK_VIN0405_DELAY(n) ((n)<<4) +#define SOLO_VCLK_VIN0203_DELAY(n) ((n)<<2) +#define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0) + +#define SOLO_IRQ_STAT 0x0010 +#define SOLO_IRQ_MASK 0x0014 +#define SOLO_IRQ_P2M(n) (1<<((n)+17)) +#define SOLO_IRQ_GPIO (1<<16) +#define SOLO_IRQ_VIDEO_LOSS (1<<15) +#define SOLO_IRQ_VIDEO_IN (1<<14) +#define SOLO_IRQ_MOTION (1<<13) +#define SOLO_IRQ_ATA_CMD (1<<12) +#define SOLO_IRQ_ATA_DIR (1<<11) +#define SOLO_IRQ_PCI_ERR (1<<10) +#define SOLO_IRQ_PS2_1 (1<<9) +#define SOLO_IRQ_PS2_0 (1<<8) +#define SOLO_IRQ_SPI (1<<7) +#define SOLO_IRQ_IIC (1<<6) +#define SOLO_IRQ_UART(n) (1<<((n) + 4)) +#define SOLO_IRQ_G723 (1<<3) +#define SOLO_IRQ_DECODER (1<<1) +#define SOLO_IRQ_ENCODER (1<<0) + +#define SOLO_CHIP_OPTION 0x001C +#define SOLO_CHIP_ID_MASK 0x00000007 + +#define SOLO_PLL_CONFIG 0x0020 /* 6110 Only */ + +#define SOLO_EEPROM_CTRL 0x0060 +#define SOLO_EEPROM_ACCESS_EN (1<<7) +#define SOLO_EEPROM_CS (1<<3) +#define SOLO_EEPROM_CLK (1<<2) +#define SOLO_EEPROM_DO (1<<1) +#define SOLO_EEPROM_DI (1<<0) +#define SOLO_EEPROM_ENABLE (SOLO_EEPROM_ACCESS_EN | SOLO_EEPROM_CS) + +#define SOLO_PCI_ERR 0x0070 +#define SOLO_PCI_ERR_FATAL 0x00000001 +#define SOLO_PCI_ERR_PARITY 0x00000002 +#define SOLO_PCI_ERR_TARGET 0x00000004 +#define SOLO_PCI_ERR_TIMEOUT 0x00000008 +#define SOLO_PCI_ERR_P2M 0x00000010 +#define SOLO_PCI_ERR_ATA 0x00000020 +#define SOLO_PCI_ERR_P2M_DESC 0x00000040 +#define SOLO_PCI_ERR_FSM0(__s) (((__s) >> 16) & 0x0f) +#define SOLO_PCI_ERR_FSM1(__s) (((__s) >> 20) & 0x0f) +#define SOLO_PCI_ERR_FSM2(__s) (((__s) >> 24) & 0x1f) + +#define SOLO_P2M_BASE 0x0080 + +#define SOLO_P2M_CONFIG(n) (0x0080 + ((n)*0x20)) +#define SOLO_P2M_DMA_INTERVAL(n) ((n)<<6)/* N*32 clocks */ +#define SOLO_P2M_CSC_BYTE_REORDER (1<<5) /* BGR -> RGB */ +/* 0:r=[14:10] g=[9:5] b=[4:0], 1:r=[15:11] g=[10:5] b=[4:0] */ +#define SOLO_P2M_CSC_16BIT_565 (1<<4) +#define SOLO_P2M_UV_SWAP (1<<3) +#define SOLO_P2M_PCI_MASTER_MODE (1<<2) +#define SOLO_P2M_DESC_INTR_OPT (1<<1) /* 1:Empty, 0:Each */ +#define SOLO_P2M_DESC_MODE (1<<0) + +#define SOLO_P2M_DES_ADR(n) (0x0084 + ((n)*0x20)) + +#define SOLO_P2M_DESC_ID(n) (0x0088 + ((n)*0x20)) +#define SOLO_P2M_UPDATE_ID(n) ((n)<<0) + +#define SOLO_P2M_STATUS(n) (0x008C + ((n)*0x20)) +#define SOLO_P2M_COMMAND_DONE (1<<8) +#define SOLO_P2M_CURRENT_ID(stat) (0xff & (stat)) + +#define SOLO_P2M_CONTROL(n) (0x0090 + ((n)*0x20)) +#define SOLO_P2M_PCI_INC(n) ((n)<<20) +#define SOLO_P2M_REPEAT(n) ((n)<<10) +/* 0:512, 1:256, 2:128, 3:64, 4:32, 5:128(2page) */ +#define SOLO_P2M_BURST_SIZE(n) ((n)<<7) +#define SOLO_P2M_BURST_512 0 +#define SOLO_P2M_BURST_256 1 +#define SOLO_P2M_BURST_128 2 +#define SOLO_P2M_BURST_64 3 +#define SOLO_P2M_BURST_32 4 +#define SOLO_P2M_CSC_16BIT (1<<6) /* 0:24bit, 1:16bit */ +/* 0:Y[0]<-0(OFF), 1:Y[0]<-1(ON), 2:Y[0]<-G[0], 3:Y[0]<-Bit[15] */ +#define SOLO_P2M_ALPHA_MODE(n) ((n)<<4) +#define SOLO_P2M_CSC_ON (1<<3) +#define SOLO_P2M_INTERRUPT_REQ (1<<2) +#define SOLO_P2M_WRITE (1<<1) +#define SOLO_P2M_TRANS_ON (1<<0) + +#define SOLO_P2M_EXT_CFG(n) (0x0094 + ((n)*0x20)) +#define SOLO_P2M_EXT_INC(n) ((n)<<20) +#define SOLO_P2M_COPY_SIZE(n) ((n)<<0) + +#define SOLO_P2M_TAR_ADR(n) (0x0098 + ((n)*0x20)) + +#define SOLO_P2M_EXT_ADR(n) (0x009C + ((n)*0x20)) + +#define SOLO_P2M_BUFFER(i) (0x2000 + ((i)*4)) + +#define SOLO_VI_CH_SWITCH_0 0x0100 +#define SOLO_VI_CH_SWITCH_1 0x0104 +#define SOLO_VI_CH_SWITCH_2 0x0108 + +#define SOLO_VI_CH_ENA 0x010C +#define SOLO_VI_CH_FORMAT 0x0110 +#define SOLO_VI_FD_SEL_MASK(n) ((n)<<16) +#define SOLO_VI_PROG_MASK(n) ((n)<<0) + +#define SOLO_VI_FMT_CFG 0x0114 +#define SOLO_VI_FMT_CHECK_VCOUNT (1<<31) +#define SOLO_VI_FMT_CHECK_HCOUNT (1<<30) +#define SOLO_VI_FMT_TEST_SIGNAL (1<<28) + +#define SOLO_VI_PAGE_SW 0x0118 +#define SOLO_FI_INV_DISP_LIVE(n) ((n)<<8) +#define SOLO_FI_INV_DISP_OUT(n) ((n)<<7) +#define SOLO_DISP_SYNC_FI(n) ((n)<<6) +#define SOLO_PIP_PAGE_ADD(n) ((n)<<3) +#define SOLO_NORMAL_PAGE_ADD(n) ((n)<<0) + +#define SOLO_VI_ACT_I_P 0x011C +#define SOLO_VI_ACT_I_S 0x0120 +#define SOLO_VI_ACT_P 0x0124 +#define SOLO_VI_FI_INVERT (1<<31) +#define SOLO_VI_H_START(n) ((n)<<21) +#define SOLO_VI_V_START(n) ((n)<<11) +#define SOLO_VI_V_STOP(n) ((n)<<0) + +#define SOLO_VI_STATUS0 0x0128 +#define SOLO_VI_STATUS0_PAGE(__n) ((__n) & 0x07) +#define SOLO_VI_STATUS1 0x012C + +/* XXX: Might be better off in kernel level disp.h */ +#define DISP_PAGE(stat) ((stat) & 0x07) + +#define SOLO_VI_PB_CONFIG 0x0130 +#define SOLO_VI_PB_USER_MODE (1<<1) +#define SOLO_VI_PB_PAL (1<<0) +#define SOLO_VI_PB_RANGE_HV 0x0134 +#define SOLO_VI_PB_HSIZE(h) ((h)<<12) +#define SOLO_VI_PB_VSIZE(v) ((v)<<0) +#define SOLO_VI_PB_ACT_H 0x0138 +#define SOLO_VI_PB_HSTART(n) ((n)<<12) +#define SOLO_VI_PB_HSTOP(n) ((n)<<0) +#define SOLO_VI_PB_ACT_V 0x013C +#define SOLO_VI_PB_VSTART(n) ((n)<<12) +#define SOLO_VI_PB_VSTOP(n) ((n)<<0) + +#define SOLO_VI_MOSAIC(ch) (0x0140 + ((ch)*4)) +#define SOLO_VI_MOSAIC_SX(x) ((x)<<24) +#define SOLO_VI_MOSAIC_EX(x) ((x)<<16) +#define SOLO_VI_MOSAIC_SY(x) ((x)<<8) +#define SOLO_VI_MOSAIC_EY(x) ((x)<<0) + +#define SOLO_VI_WIN_CTRL0(ch) (0x0180 + ((ch)*4)) +#define SOLO_VI_WIN_CTRL1(ch) (0x01C0 + ((ch)*4)) + +#define SOLO_VI_WIN_CHANNEL(n) ((n)<<28) + +#define SOLO_VI_WIN_PIP(n) ((n)<<27) +#define SOLO_VI_WIN_SCALE(n) ((n)<<24) + +#define SOLO_VI_WIN_SX(x) ((x)<<12) +#define SOLO_VI_WIN_EX(x) ((x)<<0) + +#define SOLO_VI_WIN_SY(x) ((x)<<12) +#define SOLO_VI_WIN_EY(x) ((x)<<0) + +#define SOLO_VI_WIN_ON(ch) (0x0200 + ((ch)*4)) + +#define SOLO_VI_WIN_SW 0x0240 +#define SOLO_VI_WIN_LIVE_AUTO_MUTE 0x0244 + +#define SOLO_VI_MOT_ADR 0x0260 +#define SOLO_VI_MOTION_EN(mask) ((mask)<<16) +#define SOLO_VI_MOT_CTRL 0x0264 +#define SOLO_VI_MOTION_FRAME_COUNT(n) ((n)<<24) +#define SOLO_VI_MOTION_SAMPLE_LENGTH(n) ((n)<<16) +#define SOLO_VI_MOTION_INTR_START_STOP (1<<15) +#define SOLO_VI_MOTION_FREEZE_DATA (1<<14) +#define SOLO_VI_MOTION_SAMPLE_COUNT(n) ((n)<<0) +#define SOLO_VI_MOT_CLEAR 0x0268 +#define SOLO_VI_MOT_STATUS 0x026C +#define SOLO_VI_MOTION_CNT(n) ((n)<<0) +#define SOLO_VI_MOTION_BORDER 0x0270 +#define SOLO_VI_MOTION_BAR 0x0274 +#define SOLO_VI_MOTION_Y_SET (1<<29) +#define SOLO_VI_MOTION_Y_ADD (1<<28) +#define SOLO_VI_MOTION_CB_SET (1<<27) +#define SOLO_VI_MOTION_CB_ADD (1<<26) +#define SOLO_VI_MOTION_CR_SET (1<<25) +#define SOLO_VI_MOTION_CR_ADD (1<<24) +#define SOLO_VI_MOTION_Y_VALUE(v) ((v)<<16) +#define SOLO_VI_MOTION_CB_VALUE(v) ((v)<<8) +#define SOLO_VI_MOTION_CR_VALUE(v) ((v)<<0) + +#define SOLO_VO_FMT_ENC 0x0300 +#define SOLO_VO_SCAN_MODE_PROGRESSIVE (1<<31) +#define SOLO_VO_FMT_TYPE_PAL (1<<30) +#define SOLO_VO_FMT_TYPE_NTSC 0 +#define SOLO_VO_USER_SET (1<<29) + +#define SOLO_VO_FI_CHANGE (1<<20) +#define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19) +#define SOLO_VO_USER_COLOR_SET_HSYNC (1<<18) +#define SOLO_VO_USER_COLOR_SET_NAH (1<<17) +#define SOLO_VO_USER_COLOR_SET_NAV (1<<16) +#define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8) +#define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4) +#define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0) + +#define SOLO_VO_ACT_H 0x0304 +#define SOLO_VO_H_BLANK(n) ((n)<<22) +#define SOLO_VO_H_START(n) ((n)<<11) +#define SOLO_VO_H_STOP(n) ((n)<<0) + +#define SOLO_VO_ACT_V 0x0308 +#define SOLO_VO_V_BLANK(n) ((n)<<22) +#define SOLO_VO_V_START(n) ((n)<<11) +#define SOLO_VO_V_STOP(n) ((n)<<0) + +#define SOLO_VO_RANGE_HV 0x030C +#define SOLO_VO_SYNC_INVERT (1<<24) +#define SOLO_VO_HSYNC_INVERT (1<<23) +#define SOLO_VO_VSYNC_INVERT (1<<22) +#define SOLO_VO_H_LEN(n) ((n)<<11) +#define SOLO_VO_V_LEN(n) ((n)<<0) + +#define SOLO_VO_DISP_CTRL 0x0310 +#define SOLO_VO_DISP_ON (1<<31) +#define SOLO_VO_DISP_ERASE_COUNT(n) ((n&0xf)<<24) +#define SOLO_VO_DISP_DOUBLE_SCAN (1<<22) +#define SOLO_VO_DISP_SINGLE_PAGE (1<<21) +#define SOLO_VO_DISP_BASE(n) (((n)>>16) & 0xffff) + +#define SOLO_VO_DISP_ERASE 0x0314 +#define SOLO_VO_DISP_ERASE_ON (1<<0) + +#define SOLO_VO_ZOOM_CTRL 0x0318 +#define SOLO_VO_ZOOM_VER_ON (1<<24) +#define SOLO_VO_ZOOM_HOR_ON (1<<23) +#define SOLO_VO_ZOOM_V_COMP (1<<22) +#define SOLO_VO_ZOOM_SX(h) (((h)/2)<<11) +#define SOLO_VO_ZOOM_SY(v) (((v)/2)<<0) + +#define SOLO_VO_FREEZE_CTRL 0x031C +#define SOLO_VO_FREEZE_ON (1<<1) +#define SOLO_VO_FREEZE_INTERPOLATION (1<<0) + +#define SOLO_VO_BKG_COLOR 0x0320 +#define SOLO_BG_Y(y) ((y)<<16) +#define SOLO_BG_U(u) ((u)<<8) +#define SOLO_BG_V(v) ((v)<<0) + +#define SOLO_VO_DEINTERLACE 0x0324 +#define SOLO_VO_DEINTERLACE_THRESHOLD(n) ((n)<<8) +#define SOLO_VO_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) + +#define SOLO_VO_BORDER_LINE_COLOR 0x0330 +#define SOLO_VO_BORDER_FILL_COLOR 0x0334 +#define SOLO_VO_BORDER_LINE_MASK 0x0338 +#define SOLO_VO_BORDER_FILL_MASK 0x033c + +#define SOLO_VO_BORDER_X(n) (0x0340+((n)*4)) +#define SOLO_VO_BORDER_Y(n) (0x0354+((n)*4)) + +#define SOLO_VO_CELL_EXT_SET 0x0368 +#define SOLO_VO_CELL_EXT_START 0x036c +#define SOLO_VO_CELL_EXT_STOP 0x0370 + +#define SOLO_VO_CELL_EXT_SET2 0x0374 +#define SOLO_VO_CELL_EXT_START2 0x0378 +#define SOLO_VO_CELL_EXT_STOP2 0x037c + +#define SOLO_VO_RECTANGLE_CTRL(n) (0x0368+((n)*12)) +#define SOLO_VO_RECTANGLE_START(n) (0x036c+((n)*12)) +#define SOLO_VO_RECTANGLE_STOP(n) (0x0370+((n)*12)) + +#define SOLO_VO_CURSOR_POS (0x0380) +#define SOLO_VO_CURSOR_CLR (0x0384) +#define SOLO_VO_CURSOR_CLR2 (0x0388) +#define SOLO_VO_CURSOR_MASK(id) (0x0390+((id)*4)) + +#define SOLO_VO_EXPANSION(id) (0x0250+((id)*4)) + +#define SOLO_OSG_CONFIG 0x03E0 +#define SOLO_VO_OSG_ON (1<<31) +#define SOLO_VO_OSG_COLOR_MUTE (1<<28) +#define SOLO_VO_OSG_ALPHA_RATE(n) ((n)<<22) +#define SOLO_VO_OSG_ALPHA_BG_RATE(n) ((n)<<16) +#define SOLO_VO_OSG_BASE(offset) (((offset)>>16)&0xffff) + +#define SOLO_OSG_ERASE 0x03E4 +#define SOLO_OSG_ERASE_ON (0x80) +#define SOLO_OSG_ERASE_OFF (0x00) + +#define SOLO_VO_OSG_BLINK 0x03E8 +#define SOLO_VO_OSG_BLINK_ON (1<<1) +#define SOLO_VO_OSG_BLINK_INTREVAL18 (1<<0) + +#define SOLO_CAP_BASE 0x0400 +#define SOLO_CAP_MAX_PAGE(n) ((n)<<16) +#define SOLO_CAP_BASE_ADDR(n) ((n)<<0) +#define SOLO_CAP_BTW 0x0404 +#define SOLO_CAP_PROG_BANDWIDTH(n) ((n)<<8) +#define SOLO_CAP_MAX_BANDWIDTH(n) ((n)<<0) + +#define SOLO_DIM_SCALE1 0x0408 +#define SOLO_DIM_SCALE2 0x040C +#define SOLO_DIM_SCALE3 0x0410 +#define SOLO_DIM_SCALE4 0x0414 +#define SOLO_DIM_SCALE5 0x0418 +#define SOLO_DIM_V_MB_NUM_FRAME(n) ((n)<<16) +#define SOLO_DIM_V_MB_NUM_FIELD(n) ((n)<<8) +#define SOLO_DIM_H_MB_NUM(n) ((n)<<0) + +#define SOLO_DIM_PROG 0x041C +#define SOLO_CAP_STATUS 0x0420 + +#define SOLO_CAP_CH_SCALE(ch) (0x0440+((ch)*4)) +#define SOLO_CAP_CH_COMP_ENA_E(ch) (0x0480+((ch)*4)) +#define SOLO_CAP_CH_INTV(ch) (0x04C0+((ch)*4)) +#define SOLO_CAP_CH_INTV_E(ch) (0x0500+((ch)*4)) + + +#define SOLO_VE_CFG0 0x0610 +#define SOLO_VE_TWO_PAGE_MODE (1<<31) +#define SOLO_VE_INTR_CTRL(n) ((n)<<24) +#define SOLO_VE_BLOCK_SIZE(n) ((n)<<16) +#define SOLO_VE_BLOCK_BASE(n) ((n)<<0) + +#define SOLO_VE_CFG1 0x0614 +#define SOLO_VE_BYTE_ALIGN(n) ((n)<<24) +#define SOLO_VE_INSERT_INDEX (1<<18) +#define SOLO_VE_MOTION_MODE(n) ((n)<<16) +#define SOLO_VE_MOTION_BASE(n) ((n)<<0) +#define SOLO_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 Only */ +#define SOLO_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 Only */ +#define SOLO_VE_INSERT_INDEX_JPEG (1<<19) /* 6110 Only */ + +#define SOLO_VE_WMRK_POLY 0x061C +#define SOLO_VE_VMRK_INIT_KEY 0x0620 +#define SOLO_VE_WMRK_STRL 0x0624 +#define SOLO_VE_ENCRYP_POLY 0x0628 +#define SOLO_VE_ENCRYP_INIT 0x062C +#define SOLO_VE_ATTR 0x0630 +#define SOLO_VE_LITTLE_ENDIAN (1<<31) +#define SOLO_COMP_ATTR_RN (1<<30) +#define SOLO_COMP_ATTR_FCODE(n) ((n)<<27) +#define SOLO_COMP_TIME_INC(n) ((n)<<25) +#define SOLO_COMP_TIME_WIDTH(n) ((n)<<21) +#define SOLO_DCT_INTERVAL(n) ((n)<<16) +#define SOLO_VE_COMPT_MOT 0x0634 /* 6110 Only */ + +#define SOLO_VE_STATE(n) (0x0640+((n)*4)) + +#define SOLO_VE_JPEG_QP_TBL 0x0670 +#define SOLO_VE_JPEG_QP_CH_L 0x0674 +#define SOLO_VE_JPEG_QP_CH_H 0x0678 +#define SOLO_VE_JPEG_CFG 0x067C +#define SOLO_VE_JPEG_CTRL 0x0680 +#define SOLO_VE_CODE_ENCRYPT 0x0684 /* 6110 Only */ +#define SOLO_VE_JPEG_CFG1 0x0688 /* 6110 Only */ +#define SOLO_VE_WMRK_ENABLE 0x068C /* 6110 Only */ +#define SOLO_VE_OSD_CH 0x0690 +#define SOLO_VE_OSD_BASE 0x0694 +#define SOLO_VE_OSD_CLR 0x0698 +#define SOLO_VE_OSD_OPT 0x069C +#define SOLO_VE_OSD_V_DOUBLE (1<<16) /* 6110 Only */ +#define SOLO_VE_OSD_H_SHADOW (1<<15) +#define SOLO_VE_OSD_V_SHADOW (1<<14) +#define SOLO_VE_OSD_H_OFFSET(n) ((n & 0x7f)<<7) +#define SOLO_VE_OSD_V_OFFSET(n) (n & 0x7f) + +#define SOLO_VE_CH_INTL(ch) (0x0700+((ch)*4)) +#define SOLO_VE_CH_MOT(ch) (0x0740+((ch)*4)) +#define SOLO_VE_CH_QP(ch) (0x0780+((ch)*4)) +#define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4)) +#define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4)) +#define SOLO_VE_CH_GOP_E(ch) (0x0840+((ch)*4)) +#define SOLO_VE_CH_REF_BASE(ch) (0x0880+((ch)*4)) +#define SOLO_VE_CH_REF_BASE_E(ch) (0x08C0+((ch)*4)) + +#define SOLO_VE_MPEG4_QUE(n) (0x0A00+((n)*8)) +#define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8)) + +#define SOLO_VD_CFG0 0x0900 +#define SOLO_VD_CFG_NO_WRITE_NO_WINDOW (1<<24) +#define SOLO_VD_CFG_BUSY_WIAT_CODE (1<<23) +#define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22) +#define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21) +#define SOLO_VD_CFG_BUSY_WIAT_MS (1<<20) +#define SOLO_VD_CFG_SINGLE_MODE (1<<18) +#define SOLO_VD_CFG_SCAL_MANUAL (1<<17) +#define SOLO_VD_CFG_USER_PAGE_CTRL (1<<16) +#define SOLO_VD_CFG_LITTLE_ENDIAN (1<<15) +#define SOLO_VD_CFG_START_FI (1<<14) +#define SOLO_VD_CFG_ERR_LOCK (1<<13) +#define SOLO_VD_CFG_ERR_INT_ENA (1<<12) +#define SOLO_VD_CFG_TIME_WIDTH(n) ((n)<<8) +#define SOLO_VD_CFG_DCT_INTERVAL(n) ((n)<<0) + +#define SOLO_VD_CFG1 0x0904 + +#define SOLO_VD_DEINTERLACE 0x0908 +#define SOLO_VD_DEINTERLACE_THRESHOLD(n) ((n)<<8) +#define SOLO_VD_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) + +#define SOLO_VD_CODE_ADR 0x090C + +#define SOLO_VD_CTRL 0x0910 +#define SOLO_VD_OPER_ON (1<<31) +#define SOLO_VD_MAX_ITEM(n) ((n)<<0) + +#define SOLO_VD_STATUS0 0x0920 +#define SOLO_VD_STATUS0_INTR_ACK (1<<22) +#define SOLO_VD_STATUS0_INTR_EMPTY (1<<21) +#define SOLO_VD_STATUS0_INTR_ERR (1<<20) + +#define SOLO_VD_STATUS1 0x0924 + +#define SOLO_VD_IDX0 0x0930 +#define SOLO_VD_IDX_INTERLACE (1<<30) +#define SOLO_VD_IDX_CHANNEL(n) ((n)<<24) +#define SOLO_VD_IDX_SIZE(n) ((n)<<0) + +#define SOLO_VD_IDX1 0x0934 +#define SOLO_VD_IDX_SRC_SCALE(n) ((n)<<28) +#define SOLO_VD_IDX_WINDOW(n) ((n)<<24) +#define SOLO_VD_IDX_DEINTERLACE (1<<16) +#define SOLO_VD_IDX_H_BLOCK(n) ((n)<<8) +#define SOLO_VD_IDX_V_BLOCK(n) ((n)<<0) + +#define SOLO_VD_IDX2 0x0938 +#define SOLO_VD_IDX_REF_BASE_SIDE (1<<31) +#define SOLO_VD_IDX_REF_BASE(n) (((n)>>16)&0xffff) + +#define SOLO_VD_IDX3 0x093C +#define SOLO_VD_IDX_DISP_SCALE(n) ((n)<<28) +#define SOLO_VD_IDX_INTERLACE_WR (1<<27) +#define SOLO_VD_IDX_INTERPOL (1<<26) +#define SOLO_VD_IDX_HOR2X (1<<25) +#define SOLO_VD_IDX_OFFSET_X(n) ((n)<<12) +#define SOLO_VD_IDX_OFFSET_Y(n) ((n)<<0) + +#define SOLO_VD_IDX4 0x0940 +#define SOLO_VD_IDX_DEC_WR_PAGE(n) ((n)<<8) +#define SOLO_VD_IDX_DISP_RD_PAGE(n) ((n)<<0) + +#define SOLO_VD_WR_PAGE(n) (0x03F0 + ((n) * 4)) + + +#define SOLO_GPIO_CONFIG_0 0x0B00 +#define SOLO_GPIO_CONFIG_1 0x0B04 +#define SOLO_GPIO_DATA_OUT 0x0B08 +#define SOLO_GPIO_DATA_IN 0x0B0C +#define SOLO_GPIO_INT_ACK_STA 0x0B10 +#define SOLO_GPIO_INT_ENA 0x0B14 +#define SOLO_GPIO_INT_CFG_0 0x0B18 +#define SOLO_GPIO_INT_CFG_1 0x0B1C + + +#define SOLO_IIC_CFG 0x0B20 +#define SOLO_IIC_ENABLE (1<<8) +#define SOLO_IIC_PRESCALE(n) ((n)<<0) + +#define SOLO_IIC_CTRL 0x0B24 +#define SOLO_IIC_AUTO_CLEAR (1<<20) +#define SOLO_IIC_STATE_RX_ACK (1<<19) +#define SOLO_IIC_STATE_BUSY (1<<18) +#define SOLO_IIC_STATE_SIG_ERR (1<<17) +#define SOLO_IIC_STATE_TRNS (1<<16) +#define SOLO_IIC_CH_SET(n) ((n)<<5) +#define SOLO_IIC_ACK_EN (1<<4) +#define SOLO_IIC_START (1<<3) +#define SOLO_IIC_STOP (1<<2) +#define SOLO_IIC_READ (1<<1) +#define SOLO_IIC_WRITE (1<<0) + +#define SOLO_IIC_TXD 0x0B28 +#define SOLO_IIC_RXD 0x0B2C + +/* + * UART REGISTER + */ +#define SOLO_UART_CONTROL(n) (0x0BA0 + ((n)*0x20)) +#define SOLO_UART_CLK_DIV(n) ((n)<<24) +#define SOLO_MODEM_CTRL_EN (1<<20) +#define SOLO_PARITY_ERROR_DROP (1<<18) +#define SOLO_IRQ_ERR_EN (1<<17) +#define SOLO_IRQ_RX_EN (1<<16) +#define SOLO_IRQ_TX_EN (1<<15) +#define SOLO_RX_EN (1<<14) +#define SOLO_TX_EN (1<<13) +#define SOLO_UART_HALF_DUPLEX (1<<12) +#define SOLO_UART_LOOPBACK (1<<11) + +#define SOLO_BAUDRATE_230400 ((0<<9)|(0<<6)) +#define SOLO_BAUDRATE_115200 ((0<<9)|(1<<6)) +#define SOLO_BAUDRATE_57600 ((0<<9)|(2<<6)) +#define SOLO_BAUDRATE_38400 ((0<<9)|(3<<6)) +#define SOLO_BAUDRATE_19200 ((0<<9)|(4<<6)) +#define SOLO_BAUDRATE_9600 ((0<<9)|(5<<6)) +#define SOLO_BAUDRATE_4800 ((0<<9)|(6<<6)) +#define SOLO_BAUDRATE_2400 ((1<<9)|(6<<6)) +#define SOLO_BAUDRATE_1200 ((2<<9)|(6<<6)) +#define SOLO_BAUDRATE_300 ((3<<9)|(6<<6)) + +#define SOLO_UART_DATA_BIT_8 (3<<4) +#define SOLO_UART_DATA_BIT_7 (2<<4) +#define SOLO_UART_DATA_BIT_6 (1<<4) +#define SOLO_UART_DATA_BIT_5 (0<<4) + +#define SOLO_UART_STOP_BIT_1 (0<<2) +#define SOLO_UART_STOP_BIT_2 (1<<2) + +#define SOLO_UART_PARITY_NONE (0<<0) +#define SOLO_UART_PARITY_EVEN (2<<0) +#define SOLO_UART_PARITY_ODD (3<<0) + +#define SOLO_UART_STATUS(n) (0x0BA4 + ((n)*0x20)) +#define SOLO_UART_CTS (1<<15) +#define SOLO_UART_RX_BUSY (1<<14) +#define SOLO_UART_OVERRUN (1<<13) +#define SOLO_UART_FRAME_ERR (1<<12) +#define SOLO_UART_PARITY_ERR (1<<11) +#define SOLO_UART_TX_BUSY (1<<5) + +#define SOLO_UART_RX_BUFF_CNT(stat) (((stat)>>6) & 0x1f) +#define SOLO_UART_RX_BUFF_SIZE 8 +#define SOLO_UART_TX_BUFF_CNT(stat) (((stat)>>0) & 0x1f) +#define SOLO_UART_TX_BUFF_SIZE 8 + +#define SOLO_UART_TX_DATA(n) (0x0BA8 + ((n)*0x20)) +#define SOLO_UART_TX_DATA_PUSH (1<<8) +#define SOLO_UART_RX_DATA(n) (0x0BAC + ((n)*0x20)) +#define SOLO_UART_RX_DATA_POP (1<<8) + +#define SOLO_TIMER_CLOCK_NUM 0x0be0 +#define SOLO_TIMER_USEC 0x0be8 +#define SOLO_TIMER_SEC 0x0bec +#define SOLO_TIMER_USEC_LSB 0x0d20 /* 6110 Only */ + +#define SOLO_AUDIO_CONTROL 0x0D00 +#define SOLO_AUDIO_ENABLE (1<<31) +#define SOLO_AUDIO_MASTER_MODE (1<<30) +#define SOLO_AUDIO_I2S_MODE (1<<29) +#define SOLO_AUDIO_I2S_LR_SWAP (1<<27) +#define SOLO_AUDIO_I2S_8BIT (1<<26) +#define SOLO_AUDIO_I2S_MULTI(n) ((n)<<24) +#define SOLO_AUDIO_MIX_9TO0 (1<<23) +#define SOLO_AUDIO_DEC_9TO0_VOL(n) ((n)<<20) +#define SOLO_AUDIO_MIX_19TO10 (1<<19) +#define SOLO_AUDIO_DEC_19TO10_VOL(n) ((n)<<16) +#define SOLO_AUDIO_MODE(n) ((n)<<0) +#define SOLO_AUDIO_SAMPLE 0x0D04 +#define SOLO_AUDIO_EE_MODE_ON (1<<30) +#define SOLO_AUDIO_EE_ENC_CH(ch) ((ch)<<25) +#define SOLO_AUDIO_BITRATE(n) ((n)<<16) +#define SOLO_AUDIO_CLK_DIV(n) ((n)<<0) +#define SOLO_AUDIO_FDMA_INTR 0x0D08 +#define SOLO_AUDIO_FDMA_INTERVAL(n) ((n)<<19) +#define SOLO_AUDIO_INTR_ORDER(n) ((n)<<16) +#define SOLO_AUDIO_FDMA_BASE(n) ((n)<<0) +#define SOLO_AUDIO_EVOL_0 0x0D0C +#define SOLO_AUDIO_EVOL_1 0x0D10 +#define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10)) +#define SOLO_AUDIO_STA 0x0D14 + +/* + * Watchdog configuration + */ +#define SOLO_WATCHDOG 0x0be4 +#define SOLO_WATCHDOG_SET(status, sec) (status << 8 | (sec & 0xff)) + +#endif /* __SOLO6X10_REGISTERS_H */ diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.c b/drivers/media/pci/solo6x10/solo6x10-tw28.c new file mode 100644 index 0000000..edd0781 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-tw28.c @@ -0,0 +1,871 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 +#include + +#include "solo6x10.h" +#include "solo6x10-tw28.h" + +#define DEFAULT_HDELAY_NTSC (32 - 8) +#define DEFAULT_HACTIVE_NTSC (720 + 16) +#define DEFAULT_VDELAY_NTSC (7 - 2) +#define DEFAULT_VACTIVE_NTSC (240 + 4) + +#define DEFAULT_HDELAY_PAL (32 + 4) +#define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL) +#define DEFAULT_VDELAY_PAL (6) +#define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL) + + +static const u8 tbl_tw2864_ntsc_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, + 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01, + 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, + 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, + 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81, + 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, +}; + +static const u8 tbl_tw2864_pal_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, + 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01, + 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, + 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, + 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81, + 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, +}; + +static const u8 tbl_tw2865_ntsc_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */ + 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, + 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ + 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, + 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, + 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, + 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ + 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8, + 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, + 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, + 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, +}; + +static const u8 tbl_tw2865_pal_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */ + 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, + 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ + 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, + 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, + 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, + 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ + 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8, + 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, + 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, + 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, +}; + +#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id))) + +static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off, + u8 tw_off) +{ + if (is_tw286x(solo_dev, chip_id)) + return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw6x_off); + else + return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw_off); +} + +static void tw_writebyte(struct solo_dev *solo_dev, int chip_id, + u8 tw6x_off, u8 tw_off, u8 val) +{ + if (is_tw286x(solo_dev, chip_id)) + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw6x_off, val); + else + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw_off, val); +} + +static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off, + u8 val) +{ + int i; + + for (i = 0; i < 5; i++) { + u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off); + + if (rval == val) + return; + + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val); + msleep_interruptible(1); + } + +/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */ +/* addr, off, val); */ +} + +static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_tw2865_common[256]; + int i; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) + memcpy(tbl_tw2865_common, tbl_tw2865_pal_template, + sizeof(tbl_tw2865_common)); + else + memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template, + sizeof(tbl_tw2865_common)); + + /* ALINK Mode */ + if (solo_dev->nr_chans == 4) { + tbl_tw2865_common[0xd2] = 0x01; + tbl_tw2865_common[0xcf] = 0x00; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2865_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2865_common[0xcf] = 0x80; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2865_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2865_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2865_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2865_common[0xcf] = 0x80; + } + + for (i = 0; i < 0xff; i++) { + /* Skip read only registers */ + switch (i) { + case 0xb8 ... 0xc1: + case 0xc4 ... 0xc7: + case 0xfd: + continue; + } + switch (i & ~0x30) { + case 0x00: + case 0x0c ... 0x0d: + continue; + } + + tw_write_and_verify(solo_dev, dev_addr, i, + tbl_tw2865_common[i]); + } + + return 0; +} + +static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_tw2864_common[256]; + int i; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) + memcpy(tbl_tw2864_common, tbl_tw2864_pal_template, + sizeof(tbl_tw2864_common)); + else + memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template, + sizeof(tbl_tw2864_common)); + + if (solo_dev->tw2865 == 0) { + /* IRQ Mode */ + if (solo_dev->nr_chans == 4) { + tbl_tw2864_common[0xd2] = 0x01; + tbl_tw2864_common[0xcf] = 0x00; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2864_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x40; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2864_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2864_common[0xcf] = 0x40; + } + } else { + /* ALINK Mode. Assumes that the first tw28xx is a + * 2865 and these are in cascade. */ + for (i = 0; i <= 4; i++) + tbl_tw2864_common[0x08 | i << 4] = 0x12; + + if (solo_dev->nr_chans == 8) { + tbl_tw2864_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x80; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2864_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2864_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2864_common[0xcf] = 0x80; + } + } + + for (i = 0; i < 0xff; i++) { + /* Skip read only registers */ + switch (i) { + case 0xb8 ... 0xc1: + case 0xfd: + continue; + } + switch (i & ~0x30) { + case 0x00: + case 0x0c: + case 0x0d: + continue; + } + + tw_write_and_verify(solo_dev, dev_addr, i, + tbl_tw2864_common[i]); + } + + return 0; +} + +static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_ntsc_tw2815_common[] = { + 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80, + 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11, + }; + + u8 tbl_pal_tw2815_common[] = { + 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80, + 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11, + }; + + u8 tbl_tw2815_sfr[] = { + 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */ + 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */ + 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00, + 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */ + 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88, + 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */ + }; + u8 *tbl_tw2815_common; + int i; + int ch; + + tbl_ntsc_tw2815_common[0x06] = 0; + + /* Horizontal Delay Control */ + tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8); + + /* Horizontal Active Control */ + tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2); + + /* Vertical Delay Control */ + tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4); + + /* Vertical Active Control */ + tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5); + + tbl_pal_tw2815_common[0x06] = 0; + + /* Horizontal Delay Control */ + tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8); + + /* Horizontal Active Control */ + tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2); + + /* Vertical Delay Control */ + tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4); + + /* Vertical Active Control */ + tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5); + + tbl_tw2815_common = + (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ? + tbl_ntsc_tw2815_common : tbl_pal_tw2815_common; + + /* Dual ITU-R BT.656 format */ + tbl_tw2815_common[0x0d] |= 0x04; + + /* Audio configuration */ + tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6); + + if (solo_dev->nr_chans == 4) { + tbl_tw2815_sfr[0x63 - 0x40] |= 1; + tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2815_sfr[0x63 - 0x40] |= 2; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2815_sfr[0x63 - 0x40] |= 3; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; + } + + /* Output mode of R_ADATM pin (0 mixing, 1 record) */ + /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */ + + /* 8KHz, used to be 16KHz, but changed for remote client compat */ + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2; + tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2; + + /* Playback of right channel */ + tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5; + + /* Reserved value (XXX ??) */ + tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5; + + /* Analog output gain and mix ratio playback on full */ + tbl_tw2815_sfr[0x70 - 0x40] |= 0xff; + /* Select playback audio and mute all except */ + tbl_tw2815_sfr[0x71 - 0x40] |= 0x10; + tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f; + + /* End of audio configuration */ + + for (ch = 0; ch < 4; ch++) { + tbl_tw2815_common[0x0d] &= ~3; + switch (ch) { + case 0: + tbl_tw2815_common[0x0d] |= 0x21; + break; + case 1: + tbl_tw2815_common[0x0d] |= 0x20; + break; + case 2: + tbl_tw2815_common[0x0d] |= 0x23; + break; + case 3: + tbl_tw2815_common[0x0d] |= 0x22; + break; + } + + for (i = 0; i < 0x0f; i++) { + if (i == 0x00) + continue; /* read-only */ + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + dev_addr, (ch * 0x10) + i, + tbl_tw2815_common[i]); + } + } + + for (i = 0x40; i < 0x76; i++) { + /* Skip read-only and nop registers */ + if (i == 0x40 || i == 0x59 || i == 0x5a || + i == 0x5d || i == 0x5e || i == 0x5f) + continue; + + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i, + tbl_tw2815_sfr[i - 0x40]); + } + + return 0; +} + +#define FIRST_ACTIVE_LINE 0x0008 +#define LAST_ACTIVE_LINE 0x0102 + +static void saa712x_write_regs(struct solo_dev *dev, const uint8_t *vals, + int start, int n) +{ + for (; start < n; start++, vals++) { + /* Skip read-only registers */ + switch (start) { + /* case 0x00 ... 0x25: */ + case 0x2e ... 0x37: + case 0x60: + case 0x7d: + continue; + } + solo_i2c_writebyte(dev, SOLO_I2C_SAA, 0x46, start, *vals); + } +} + +#define SAA712x_reg7c (0x80 | ((LAST_ACTIVE_LINE & 0x100) >> 2) \ + | ((FIRST_ACTIVE_LINE & 0x100) >> 4)) + +static void saa712x_setup(struct solo_dev *dev) +{ + const int reg_start = 0x26; + const uint8_t saa7128_regs_ntsc[] = { + /* :0x26 */ + 0x0d, 0x00, + /* :0x28 */ + 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, + /* :0x2e XXX: read-only */ + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* :0x38 */ + 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + /* :0x40 */ + 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18, + 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f, + /* :0x50 */ + 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06, + 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e, + /* :0x60 */ + 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77, + 0x41, 0x88, 0x41, 0x52, 0xed, 0x10, 0x10, 0x00, + /* :0x70 */ + 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00, + 0x00, 0x00, FIRST_ACTIVE_LINE, LAST_ACTIVE_LINE & 0xff, + SAA712x_reg7c, 0x00, 0xff, 0xff, + }, saa7128_regs_pal[] = { + /* :0x26 */ + 0x0d, 0x00, + /* :0x28 */ + 0xe1, 0x1d, 0x75, 0x3f, 0x06, 0x3f, + /* :0x2e XXX: read-only */ + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* :0x38 */ + 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + /* :0x40 */ + 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18, + 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f, + /* :0x50 */ + 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06, + 0x02, 0x80, 0x0f, 0x77, 0xa7, 0x67, 0x66, 0x2e, + /* :0x60 */ + 0x7b, 0x02, 0x35, 0xcb, 0x8a, 0x09, 0x2a, 0x77, + 0x41, 0x88, 0x41, 0x52, 0xf1, 0x10, 0x20, 0x00, + /* :0x70 */ + 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x12, 0x30, + SAA712x_reg7c | 0x40, 0x00, 0xff, 0xff, + }; + + if (dev->video_type == SOLO_VO_FMT_TYPE_PAL) + saa712x_write_regs(dev, saa7128_regs_pal, reg_start, + sizeof(saa7128_regs_pal)); + else + saa712x_write_regs(dev, saa7128_regs_ntsc, reg_start, + sizeof(saa7128_regs_ntsc)); +} + +int solo_tw28_init(struct solo_dev *solo_dev) +{ + int i; + u8 value; + + solo_dev->tw28_cnt = 0; + + /* Detect techwell chip type(s) */ + for (i = 0; i < solo_dev->nr_chans / 4; i++) { + value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(i), 0xFF); + + switch (value >> 3) { + case 0x18: + solo_dev->tw2865 |= 1 << i; + solo_dev->tw28_cnt++; + break; + case 0x0c: + solo_dev->tw2864 |= 1 << i; + solo_dev->tw28_cnt++; + break; + default: + value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(i), + 0x59); + if ((value >> 3) == 0x04) { + solo_dev->tw2815 |= 1 << i; + solo_dev->tw28_cnt++; + } + } + } + + if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) { + dev_err(&solo_dev->pdev->dev, + "Could not initialize any techwell chips\n"); + return -EINVAL; + } + + saa712x_setup(solo_dev); + + for (i = 0; i < solo_dev->tw28_cnt; i++) { + if ((solo_dev->tw2865 & (1 << i))) + tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + else if ((solo_dev->tw2864 & (1 << i))) + tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + else + tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + } + + return 0; +} + +/* + * We accessed the video status signal in the Techwell chip through + * iic/i2c because the video status reported by register REG_VI_STATUS1 + * (address 0x012C) of the SOLO6010 chip doesn't give the correct video + * status signal values. + */ +int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch) +{ + u8 val, chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR, + TW_AV_STAT_ADDR) & 0x0f; + + return val & (1 << ch) ? 1 : 0; +} + +#if 0 +/* Status of audio from up to 4 techwell chips are combined into 1 variable. + * See techwell datasheet for details. */ +u16 tw28_get_audio_status(struct solo_dev *solo_dev) +{ + u8 val; + u16 status = 0; + int i; + + for (i = 0; i < solo_dev->tw28_cnt; i++) { + val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR, + TW_AV_STAT_ADDR) & 0xf0) >> 4; + status |= val << (i * 4); + } + + return status; +} +#endif + +bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch) +{ + return is_tw286x(solo_dev, ch / 4); +} + +int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, + s32 val) +{ + char sval; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + if (val > 255 || val < 0) + return -ERANGE; + + switch (ctrl) { + case V4L2_CID_SHARPNESS: + /* Only 286x has sharpness */ + if (is_tw286x(solo_dev, chip_num)) { + u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num)); + v &= 0xf0; + v |= val; + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num), v); + } else { + return -EINVAL; + } + break; + + case V4L2_CID_HUE: + if (is_tw286x(solo_dev, chip_num)) + sval = val - 128; + else + sval = (char)val; + tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), + TW_HUE_ADDR(ch), sval); + + break; + + case V4L2_CID_SATURATION: + /* 286x chips have a U and V component for saturation */ + if (is_tw286x(solo_dev, chip_num)) { + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SATURATIONU_ADDR(ch), val); + } + tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch), + TW_SATURATION_ADDR(ch), val); + + break; + + case V4L2_CID_CONTRAST: + tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch), + TW_CONTRAST_ADDR(ch), val); + break; + + case V4L2_CID_BRIGHTNESS: + if (is_tw286x(solo_dev, chip_num)) + sval = val - 128; + else + sval = (char)val; + tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch), + TW_BRIGHTNESS_ADDR(ch), sval); + + break; + default: + return -EINVAL; + } + + return 0; +} + +int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, + s32 *val) +{ + u8 rval, chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + switch (ctrl) { + case V4L2_CID_SHARPNESS: + /* Only 286x has sharpness */ + if (is_tw286x(solo_dev, chip_num)) { + rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num)); + *val = rval & 0x0f; + } else + *val = 0; + break; + case V4L2_CID_HUE: + rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), + TW_HUE_ADDR(ch)); + if (is_tw286x(solo_dev, chip_num)) + *val = (s32)((char)rval) + 128; + else + *val = rval; + break; + case V4L2_CID_SATURATION: + *val = tw_readbyte(solo_dev, chip_num, + TW286x_SATURATIONU_ADDR(ch), + TW_SATURATION_ADDR(ch)); + break; + case V4L2_CID_CONTRAST: + *val = tw_readbyte(solo_dev, chip_num, + TW286x_CONTRAST_ADDR(ch), + TW_CONTRAST_ADDR(ch)); + break; + case V4L2_CID_BRIGHTNESS: + rval = tw_readbyte(solo_dev, chip_num, + TW286x_BRIGHTNESS_ADDR(ch), + TW_BRIGHTNESS_ADDR(ch)); + if (is_tw286x(solo_dev, chip_num)) + *val = (s32)((char)rval) + 128; + else + *val = rval; + break; + default: + return -EINVAL; + } + + return 0; +} + +#if 0 +/* + * For audio output volume, the output channel is only 1. In this case we + * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used + * is the base address of the techwell chip. + */ +void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val) +{ + unsigned int val; + unsigned int chip_num; + + chip_num = (solo_dev->nr_chans - 1) / 4; + + val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, + TW_AUDIO_OUTPUT_VOL_ADDR); + + u_val = (val & 0x0f) | (u_val << 4); + + tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, + TW_AUDIO_OUTPUT_VOL_ADDR, u_val); +} +#endif + +u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch) +{ + u8 val; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + val = tw_readbyte(solo_dev, chip_num, + TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch)); + + return (ch % 2) ? (val >> 4) : (val & 0x0f); +} + +void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val) +{ + u8 old_val; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + old_val = tw_readbyte(solo_dev, chip_num, + TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch)); + + val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) | + ((ch % 2) ? (val << 4) : val); + + tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch), val); +} diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.h b/drivers/media/pci/solo6x10/solo6x10-tw28.h new file mode 100644 index 0000000..0966b45 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-tw28.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 __SOLO6X10_TW28_H +#define __SOLO6X10_TW28_H + +#include "solo6x10.h" + +#define TW_NUM_CHIP 4 +#define TW_BASE_ADDR 0x28 +#define TW_CHIP_OFFSET_ADDR(n) (TW_BASE_ADDR + (n)) + +/* tw2815 */ +#define TW_AV_STAT_ADDR 0x5a +#define TW_HUE_ADDR(n) (0x07 | ((n) << 4)) +#define TW_SATURATION_ADDR(n) (0x08 | ((n) << 4)) +#define TW_CONTRAST_ADDR(n) (0x09 | ((n) << 4)) +#define TW_BRIGHTNESS_ADDR(n) (0x0a | ((n) << 4)) +#define TW_AUDIO_OUTPUT_VOL_ADDR 0x70 +#define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0)) + +/* tw286x */ +#define TW286x_AV_STAT_ADDR 0xfd +#define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4)) +#define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4)) +#define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4)) +#define TW286x_CONTRAST_ADDR(n) (0x02 | ((n) << 4)) +#define TW286x_BRIGHTNESS_ADDR(n) (0x01 | ((n) << 4)) +#define TW286x_SHARPNESS(n) (0x03 | ((n) << 4)) +#define TW286x_AUDIO_OUTPUT_VOL_ADDR 0xdf +#define TW286x_AUDIO_INPUT_GAIN_ADDR(n) (0xD0 + ((n > 1) ? 1 : 0)) + +int solo_tw28_init(struct solo_dev *solo_dev); + +int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val); +int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 *val); +bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch); + +u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch); +void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val); +int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch); + +#if 0 +unsigned int tw2815_get_audio_status(struct SOLO *solo); +void tw2815_Set_AudioOutVol(struct SOLO *solo, unsigned int u_val); +#endif + +#endif /* __SOLO6X10_TW28_H */ diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c new file mode 100644 index 0000000..2e07b49 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -0,0 +1,1434 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#include "solo6x10.h" +#include "solo6x10-tw28.h" +#include "solo6x10-jpeg.h" + +#define MIN_VID_BUFFERS 2 +#define FRAME_BUF_SIZE (196 * 1024) +#define MP4_QS 16 +#define DMA_ALIGN 4096 + +/* 6010 M4V */ +static unsigned char vop_6010_ntsc_d1[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x58, 0x10, 0xf0, 0x71, 0x18, 0x3f, +}; + +static unsigned char vop_6010_ntsc_cif[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x2c, 0x10, 0x78, 0x51, 0x18, 0x3f, +}; + +static unsigned char vop_6010_pal_d1[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x58, 0x11, 0x20, 0x71, 0x18, 0x3f, +}; + +static unsigned char vop_6010_pal_cif[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x2c, 0x10, 0x90, 0x51, 0x18, 0x3f, +}; + +/* 6110 h.264 */ +static unsigned char vop_6110_ntsc_d1[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x05, 0x81, 0xec, 0x80, 0x00, 0x00, + 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, +}; + +static unsigned char vop_6110_ntsc_cif[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x0b, 0x0f, 0xc8, 0x00, 0x00, 0x00, + 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, +}; + +static unsigned char vop_6110_pal_d1[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x05, 0x80, 0x93, 0x20, 0x00, 0x00, + 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, +}; + +static unsigned char vop_6110_pal_cif[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x0b, 0x04, 0xb2, 0x00, 0x00, 0x00, + 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, +}; + +typedef __le32 vop_header[16]; + +struct solo_enc_buf { + enum solo_enc_types type; + const vop_header *vh; + int motion; +}; + +static int solo_is_motion_on(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + + return (solo_dev->motion_mask >> solo_enc->ch) & 1; +} + +static int solo_motion_detected(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + unsigned long flags; + u32 ch_mask = 1 << solo_enc->ch; + int ret = 0; + + spin_lock_irqsave(&solo_enc->motion_lock, flags); + if (solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS) & ch_mask) { + solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, ch_mask); + ret = 1; + } + spin_unlock_irqrestore(&solo_enc->motion_lock, flags); + + return ret; +} + +static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + u32 mask = 1 << solo_enc->ch; + unsigned long flags; + + spin_lock_irqsave(&solo_enc->motion_lock, flags); + + if (on) + solo_dev->motion_mask |= mask; + else + solo_dev->motion_mask &= ~mask; + + solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, mask); + + solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, + SOLO_VI_MOTION_EN(solo_dev->motion_mask) | + (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); + + spin_unlock_irqrestore(&solo_enc->motion_lock, flags); +} + +void solo_update_mode(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + int vop_len; + unsigned char *vop; + + solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; + solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); + + if (solo_enc->mode == SOLO_ENC_MODE_CIF) { + solo_enc->width = solo_dev->video_hsize >> 1; + solo_enc->height = solo_dev->video_vsize; + if (solo_dev->type == SOLO_DEV_6110) { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6110_ntsc_cif; + vop_len = sizeof(vop_6110_ntsc_cif); + } else { + vop = vop_6110_pal_cif; + vop_len = sizeof(vop_6110_pal_cif); + } + } else { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6010_ntsc_cif; + vop_len = sizeof(vop_6010_ntsc_cif); + } else { + vop = vop_6010_pal_cif; + vop_len = sizeof(vop_6010_pal_cif); + } + } + } else { + solo_enc->width = solo_dev->video_hsize; + solo_enc->height = solo_dev->video_vsize << 1; + solo_enc->bw_weight <<= 2; + if (solo_dev->type == SOLO_DEV_6110) { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6110_ntsc_d1; + vop_len = sizeof(vop_6110_ntsc_d1); + } else { + vop = vop_6110_pal_d1; + vop_len = sizeof(vop_6110_pal_d1); + } + } else { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6010_ntsc_d1; + vop_len = sizeof(vop_6010_ntsc_d1); + } else { + vop = vop_6010_pal_d1; + vop_len = sizeof(vop_6010_pal_d1); + } + } + } + + memcpy(solo_enc->vop, vop, vop_len); + + /* Some fixups for 6010/M4V */ + if (solo_dev->type == SOLO_DEV_6010) { + u16 fps = solo_dev->fps * 1000; + u16 interval = solo_enc->interval * 1000; + + vop = solo_enc->vop; + + /* Frame rate and interval */ + vop[22] = fps >> 4; + vop[23] = ((fps << 4) & 0xf0) | 0x0c + | ((interval >> 13) & 0x3); + vop[24] = (interval >> 5) & 0xff; + vop[25] = ((interval << 3) & 0xf8) | 0x04; + } + + solo_enc->vop_len = vop_len; + + /* Now handle the jpeg header */ + vop = solo_enc->jpeg_header; + vop[SOF0_START + 5] = 0xff & (solo_enc->height >> 8); + vop[SOF0_START + 6] = 0xff & solo_enc->height; + vop[SOF0_START + 7] = 0xff & (solo_enc->width >> 8); + vop[SOF0_START + 8] = 0xff & solo_enc->width; + + memcpy(vop + DQT_START, + jpeg_dqt[solo_g_jpeg_qp(solo_dev, solo_enc->ch)], DQT_LEN); +} + +static int solo_enc_on(struct solo_enc_dev *solo_enc) +{ + u8 ch = solo_enc->ch; + struct solo_dev *solo_dev = solo_enc->solo_dev; + u8 interval; + + solo_update_mode(solo_enc); + + /* Make sure to do a bandwidth check */ + if (solo_enc->bw_weight > solo_dev->enc_bw_remain) + return -EBUSY; + solo_enc->sequence = 0; + solo_enc->motion_last_state = false; + solo_enc->frames_since_last_motion = 0; + solo_dev->enc_bw_remain -= solo_enc->bw_weight; + + if (solo_enc->type == SOLO_ENC_TYPE_EXT) + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1); + + /* Disable all encoding for this channel */ + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0); + + /* Common for both std and ext encoding */ + solo_reg_write(solo_dev, SOLO_VE_CH_INTL(ch), + solo_enc->interlaced ? 1 : 0); + + if (solo_enc->interlaced) + interval = solo_enc->interval - 1; + else + interval = solo_enc->interval; + + /* Standard encoding only */ + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_QP(ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_CAP_CH_INTV(ch), interval); + + /* Extended encoding only */ + solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_CAP_CH_INTV_E(ch), interval); + + /* Enables the standard encoder */ + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode); + + return 0; +} + +static void solo_enc_off(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + + solo_dev->enc_bw_remain += solo_enc->bw_weight; + + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0); +} + +static int enc_get_mpeg_dma(struct solo_dev *solo_dev, dma_addr_t dma, + unsigned int off, unsigned int size) +{ + int ret; + + if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) + return -EINVAL; + + /* Single shot */ + if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { + return solo_p2m_dma_t(solo_dev, 0, dma, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, size, + 0, 0); + } + + /* Buffer wrap */ + ret = solo_p2m_dma_t(solo_dev, 0, dma, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, + SOLO_MP4E_EXT_SIZE(solo_dev) - off, 0, 0); + + if (!ret) { + ret = solo_p2m_dma_t(solo_dev, 0, + dma + SOLO_MP4E_EXT_SIZE(solo_dev) - off, + SOLO_MP4E_EXT_ADDR(solo_dev), + size + off - SOLO_MP4E_EXT_SIZE(solo_dev), 0, 0); + } + + return ret; +} + +/* Build a descriptor queue out of an SG list and send it to the P2M for + * processing. */ +static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip, + struct sg_table *vbuf, int off, int size, + unsigned int base, unsigned int base_size) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct scatterlist *sg; + int i; + int ret; + + if (WARN_ON_ONCE(size > FRAME_BUF_SIZE)) + return -EINVAL; + + solo_enc->desc_count = 1; + + for_each_sg(vbuf->sgl, sg, vbuf->nents, i) { + struct solo_p2m_desc *desc; + dma_addr_t dma; + int len; + int left = base_size - off; + + desc = &solo_enc->desc_items[solo_enc->desc_count++]; + dma = sg_dma_address(sg); + len = sg_dma_len(sg); + + /* We assume this is smaller than the scatter size */ + BUG_ON(skip >= len); + if (skip) { + len -= skip; + dma += skip; + size -= skip; + skip = 0; + } + + len = min(len, size); + + if (len <= left) { + /* Single descriptor */ + solo_p2m_fill_desc(desc, 0, dma, base + off, + len, 0, 0); + } else { + /* Buffer wrap */ + /* XXX: Do these as separate DMA requests, to avoid + timeout errors triggered by awkwardly sized + descriptors. See + + */ + ret = solo_p2m_dma_t(solo_dev, 0, dma, base + off, + left, 0, 0); + if (ret) + return ret; + + ret = solo_p2m_dma_t(solo_dev, 0, dma + left, base, + len - left, 0, 0); + if (ret) + return ret; + + solo_enc->desc_count--; + } + + size -= len; + if (size <= 0) + break; + + off += len; + if (off >= base_size) + off -= base_size; + + /* Because we may use two descriptors per loop */ + if (solo_enc->desc_count >= (solo_enc->desc_nelts - 1)) { + ret = solo_p2m_dma_desc(solo_dev, solo_enc->desc_items, + solo_enc->desc_dma, + solo_enc->desc_count - 1); + if (ret) + return ret; + solo_enc->desc_count = 1; + } + } + + if (solo_enc->desc_count <= 1) + return 0; + + return solo_p2m_dma_desc(solo_dev, solo_enc->desc_items, + solo_enc->desc_dma, solo_enc->desc_count - 1); +} + +/* Extract values from VOP header - VE_STATUSxx */ +static inline int vop_interlaced(const vop_header *vh) +{ + return (__le32_to_cpu((*vh)[0]) >> 30) & 1; +} + +static inline u8 vop_channel(const vop_header *vh) +{ + return (__le32_to_cpu((*vh)[0]) >> 24) & 0x1F; +} + +static inline u8 vop_type(const vop_header *vh) +{ + return (__le32_to_cpu((*vh)[0]) >> 22) & 3; +} + +static inline u32 vop_mpeg_size(const vop_header *vh) +{ + return __le32_to_cpu((*vh)[0]) & 0xFFFFF; +} + +static inline u8 vop_hsize(const vop_header *vh) +{ + return (__le32_to_cpu((*vh)[1]) >> 8) & 0xFF; +} + +static inline u8 vop_vsize(const vop_header *vh) +{ + return __le32_to_cpu((*vh)[1]) & 0xFF; +} + +static inline u32 vop_mpeg_offset(const vop_header *vh) +{ + return __le32_to_cpu((*vh)[2]); +} + +static inline u32 vop_jpeg_offset(const vop_header *vh) +{ + return __le32_to_cpu((*vh)[3]); +} + +static inline u32 vop_jpeg_size(const vop_header *vh) +{ + return __le32_to_cpu((*vh)[4]) & 0xFFFFF; +} + +static inline u32 vop_sec(const vop_header *vh) +{ + return __le32_to_cpu((*vh)[5]); +} + +static inline u32 vop_usec(const vop_header *vh) +{ + return __le32_to_cpu((*vh)[6]); +} + +static int solo_fill_jpeg(struct solo_enc_dev *solo_enc, + struct vb2_buffer *vb, const vop_header *vh) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0); + int frame_size; + int ret; + + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + + if (vb2_plane_size(vb, 0) < vop_jpeg_size(vh) + solo_enc->jpeg_len) + return -EIO; + + frame_size = ALIGN(vop_jpeg_size(vh) + solo_enc->jpeg_len, DMA_ALIGN); + vb2_set_plane_payload(vb, 0, vop_jpeg_size(vh) + solo_enc->jpeg_len); + + /* may discard all previous data in vbuf->sgl */ + if (!dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, + DMA_FROM_DEVICE)) + return -ENOMEM; + ret = solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf, + vop_jpeg_offset(vh) - SOLO_JPEG_EXT_ADDR(solo_dev), + frame_size, SOLO_JPEG_EXT_ADDR(solo_dev), + SOLO_JPEG_EXT_SIZE(solo_dev)); + dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, + DMA_FROM_DEVICE); + + /* add the header only after dma_unmap_sg() */ + sg_copy_from_buffer(vbuf->sgl, vbuf->nents, + solo_enc->jpeg_header, solo_enc->jpeg_len); + + return ret; +} + +static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, + struct vb2_buffer *vb, const vop_header *vh) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0); + int frame_off, frame_size; + int skip = 0; + int ret; + + if (vb2_plane_size(vb, 0) < vop_mpeg_size(vh)) + return -EIO; + + /* If this is a key frame, add extra header */ + vb->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME); + if (!vop_type(vh)) { + skip = solo_enc->vop_len; + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + vb2_set_plane_payload(vb, 0, vop_mpeg_size(vh) + + solo_enc->vop_len); + } else { + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; + vb2_set_plane_payload(vb, 0, vop_mpeg_size(vh)); + } + + /* Now get the actual mpeg payload */ + frame_off = (vop_mpeg_offset(vh) - SOLO_MP4E_EXT_ADDR(solo_dev) + + sizeof(*vh)) % SOLO_MP4E_EXT_SIZE(solo_dev); + frame_size = ALIGN(vop_mpeg_size(vh) + skip, DMA_ALIGN); + + /* may discard all previous data in vbuf->sgl */ + if (!dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, + DMA_FROM_DEVICE)) + return -ENOMEM; + ret = solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size, + SOLO_MP4E_EXT_ADDR(solo_dev), + SOLO_MP4E_EXT_SIZE(solo_dev)); + dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, + DMA_FROM_DEVICE); + + /* add the header only after dma_unmap_sg() */ + if (!vop_type(vh)) + sg_copy_from_buffer(vbuf->sgl, vbuf->nents, + solo_enc->vop, solo_enc->vop_len); + return ret; +} + +static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, + struct vb2_buffer *vb, struct solo_enc_buf *enc_buf) +{ + const vop_header *vh = enc_buf->vh; + int ret; + + switch (solo_enc->fmt) { + case V4L2_PIX_FMT_MPEG4: + case V4L2_PIX_FMT_H264: + ret = solo_fill_mpeg(solo_enc, vb, vh); + break; + default: /* V4L2_PIX_FMT_MJPEG */ + ret = solo_fill_jpeg(solo_enc, vb, vh); + break; + } + + if (!ret) { + bool send_event = false; + + vb->v4l2_buf.sequence = solo_enc->sequence++; + vb->v4l2_buf.timestamp.tv_sec = vop_sec(vh); + vb->v4l2_buf.timestamp.tv_usec = vop_usec(vh); + + /* Check for motion flags */ + if (solo_is_motion_on(solo_enc)) { + /* It takes a few frames for the hardware to detect + * motion. Once it does it clears the motion detection + * register and it takes again a few frames before + * motion is seen. This means in practice that when the + * motion field is 1, it will go back to 0 for the next + * frame. This leads to motion detection event being + * sent all the time, which is not what we want. + * Instead wait a few frames before deciding that the + * motion has halted. After some experimentation it + * turns out that waiting for 5 frames works well. + */ + if (enc_buf->motion == 0 && + solo_enc->motion_last_state && + solo_enc->frames_since_last_motion++ > 5) + send_event = true; + else if (enc_buf->motion) { + solo_enc->frames_since_last_motion = 0; + send_event = !solo_enc->motion_last_state; + } + } + + if (send_event) { + struct v4l2_event ev = { + .type = V4L2_EVENT_MOTION_DET, + .u.motion_det = { + .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, + .frame_sequence = vb->v4l2_buf.sequence, + .region_mask = enc_buf->motion ? 1 : 0, + }, + }; + + solo_enc->motion_last_state = enc_buf->motion; + solo_enc->frames_since_last_motion = 0; + v4l2_event_queue(solo_enc->vfd, &ev); + } + } + + vb2_buffer_done(vb, ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + + return ret; +} + +static void solo_enc_handle_one(struct solo_enc_dev *solo_enc, + struct solo_enc_buf *enc_buf) +{ + struct solo_vb2_buf *vb; + unsigned long flags; + + mutex_lock(&solo_enc->lock); + if (solo_enc->type != enc_buf->type) + goto unlock; + + spin_lock_irqsave(&solo_enc->av_lock, flags); + if (list_empty(&solo_enc->vidq_active)) { + spin_unlock_irqrestore(&solo_enc->av_lock, flags); + goto unlock; + } + vb = list_first_entry(&solo_enc->vidq_active, struct solo_vb2_buf, + list); + list_del(&vb->list); + spin_unlock_irqrestore(&solo_enc->av_lock, flags); + + solo_enc_fillbuf(solo_enc, &vb->vb, enc_buf); +unlock: + mutex_unlock(&solo_enc->lock); +} + +void solo_enc_v4l2_isr(struct solo_dev *solo_dev) +{ + wake_up_interruptible_all(&solo_dev->ring_thread_wait); +} + +static void solo_handle_ring(struct solo_dev *solo_dev) +{ + for (;;) { + struct solo_enc_dev *solo_enc; + struct solo_enc_buf enc_buf; + u32 mpeg_current, off; + u8 ch; + u8 cur_q; + + /* Check if the hardware has any new ones in the queue */ + cur_q = solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xff; + if (cur_q == solo_dev->enc_idx) + break; + + mpeg_current = solo_reg_read(solo_dev, + SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); + solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS; + + ch = (mpeg_current >> 24) & 0x1f; + off = mpeg_current & 0x00ffffff; + + if (ch >= SOLO_MAX_CHANNELS) { + ch -= SOLO_MAX_CHANNELS; + enc_buf.type = SOLO_ENC_TYPE_EXT; + } else + enc_buf.type = SOLO_ENC_TYPE_STD; + + solo_enc = solo_dev->v4l2_enc[ch]; + if (solo_enc == NULL) { + dev_err(&solo_dev->pdev->dev, + "Got spurious packet for channel %d\n", ch); + continue; + } + + /* FAIL... */ + if (enc_get_mpeg_dma(solo_dev, solo_dev->vh_dma, off, + sizeof(vop_header))) + continue; + + enc_buf.vh = solo_dev->vh_buf; + + /* Sanity check */ + if (vop_mpeg_offset(enc_buf.vh) != + SOLO_MP4E_EXT_ADDR(solo_dev) + off) + continue; + + if (solo_motion_detected(solo_enc)) + enc_buf.motion = 1; + else + enc_buf.motion = 0; + + solo_enc_handle_one(solo_enc, &enc_buf); + } +} + +static int solo_ring_thread(void *data) +{ + struct solo_dev *solo_dev = data; + DECLARE_WAITQUEUE(wait, current); + + set_freezable(); + add_wait_queue(&solo_dev->ring_thread_wait, &wait); + + for (;;) { + long timeout = schedule_timeout_interruptible(HZ); + + if (timeout == -ERESTARTSYS || kthread_should_stop()) + break; + solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); + solo_handle_ring(solo_dev); + solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); + try_to_freeze(); + } + + remove_wait_queue(&solo_dev->ring_thread_wait, &wait); + + return 0; +} + +static int solo_enc_queue_setup(struct vb2_queue *q, + const struct v4l2_format *fmt, + unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + void *alloc_ctxs[]) +{ + sizes[0] = FRAME_BUF_SIZE; + *num_planes = 1; + + if (*num_buffers < MIN_VID_BUFFERS) + *num_buffers = MIN_VID_BUFFERS; + + return 0; +} + +static void solo_enc_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct solo_enc_dev *solo_enc = vb2_get_drv_priv(vq); + struct solo_vb2_buf *solo_vb = + container_of(vb, struct solo_vb2_buf, vb); + + spin_lock(&solo_enc->av_lock); + list_add_tail(&solo_vb->list, &solo_enc->vidq_active); + spin_unlock(&solo_enc->av_lock); +} + +static int solo_ring_start(struct solo_dev *solo_dev) +{ + solo_dev->ring_thread = kthread_run(solo_ring_thread, solo_dev, + SOLO6X10_NAME "_ring"); + if (IS_ERR(solo_dev->ring_thread)) { + int err = PTR_ERR(solo_dev->ring_thread); + + solo_dev->ring_thread = NULL; + return err; + } + + solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); + + return 0; +} + +static void solo_ring_stop(struct solo_dev *solo_dev) +{ + if (solo_dev->ring_thread) { + kthread_stop(solo_dev->ring_thread); + solo_dev->ring_thread = NULL; + } + + solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); +} + +static int solo_enc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); + int ret; + + ret = solo_enc_on(solo_enc); + if (ret) + return ret; + return solo_ring_start(solo_enc->solo_dev); +} + +static void solo_enc_stop_streaming(struct vb2_queue *q) +{ + struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); + + solo_enc_off(solo_enc); + INIT_LIST_HEAD(&solo_enc->vidq_active); + solo_ring_stop(solo_enc->solo_dev); +} + +static struct vb2_ops solo_enc_video_qops = { + .queue_setup = solo_enc_queue_setup, + .buf_queue = solo_enc_buf_queue, + .start_streaming = solo_enc_start_streaming, + .stop_streaming = solo_enc_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int solo_enc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + strcpy(cap->driver, SOLO6X10_NAME); + snprintf(cap->card, sizeof(cap->card), "Softlogic 6x10 Enc %d", + solo_enc->ch); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", + pci_name(solo_dev->pdev)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int solo_enc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (input->index) + return -EINVAL; + + snprintf(input->name, sizeof(input->name), "Encoder %d", + solo_enc->ch + 1); + input->type = V4L2_INPUT_TYPE_CAMERA; + input->std = solo_enc->vfd->tvnorms; + + if (!tw28_get_video_status(solo_dev, solo_enc->ch)) + input->status = V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int solo_enc_set_input(struct file *file, void *priv, + unsigned int index) +{ + if (index) + return -EINVAL; + + return 0; +} + +static int solo_enc_get_input(struct file *file, void *priv, + unsigned int *index) +{ + *index = 0; + + return 0; +} + +static int solo_enc_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + int dev_type = solo_enc->solo_dev->type; + + switch (f->index) { + case 0: + switch (dev_type) { + case SOLO_DEV_6010: + f->pixelformat = V4L2_PIX_FMT_MPEG4; + strcpy(f->description, "MPEG-4 part 2"); + break; + case SOLO_DEV_6110: + f->pixelformat = V4L2_PIX_FMT_H264; + strcpy(f->description, "H.264"); + break; + } + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_MJPEG; + strcpy(f->description, "MJPEG"); + break; + default: + return -EINVAL; + } + + f->flags = V4L2_FMT_FLAG_COMPRESSED; + + return 0; +} + +static inline int solo_valid_pixfmt(u32 pixfmt, int dev_type) +{ + return (pixfmt == V4L2_PIX_FMT_H264 && dev_type == SOLO_DEV_6110) + || (pixfmt == V4L2_PIX_FMT_MPEG4 && dev_type == SOLO_DEV_6010) + || pixfmt == V4L2_PIX_FMT_MJPEG ? 0 : -EINVAL; +} + +static int solo_enc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (solo_valid_pixfmt(pix->pixelformat, solo_dev->type)) + return -EINVAL; + + if (pix->width < solo_dev->video_hsize || + pix->height < solo_dev->video_vsize << 1) { + /* Default to CIF 1/2 size */ + pix->width = solo_dev->video_hsize >> 1; + pix->height = solo_dev->video_vsize; + } else { + /* Full frame */ + pix->width = solo_dev->video_hsize; + pix->height = solo_dev->video_vsize << 1; + } + + switch (pix->field) { + case V4L2_FIELD_NONE: + case V4L2_FIELD_INTERLACED: + break; + case V4L2_FIELD_ANY: + default: + pix->field = V4L2_FIELD_INTERLACED; + break; + } + + /* Just set these */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->sizeimage = FRAME_BUF_SIZE; + pix->bytesperline = 0; + pix->priv = 0; + + return 0; +} + +static int solo_enc_set_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + if (vb2_is_busy(&solo_enc->vidq)) + return -EBUSY; + + ret = solo_enc_try_fmt_cap(file, priv, f); + if (ret) + return ret; + + if (pix->width == solo_dev->video_hsize) + solo_enc->mode = SOLO_ENC_MODE_D1; + else + solo_enc->mode = SOLO_ENC_MODE_CIF; + + /* This does not change the encoder at all */ + solo_enc->fmt = pix->pixelformat; + + /* + * More information is needed about these 'extended' types. As far + * as I can tell these are basically additional video streams with + * different MPEG encoding attributes that can run in parallel with + * the main stream. If so, then this should be implemented as a + * second video node. Abusing priv like this is certainly not the + * right approach. + if (pix->priv) + solo_enc->type = SOLO_ENC_TYPE_EXT; + */ + solo_update_mode(solo_enc); + return 0; +} + +static int solo_enc_get_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = solo_enc->width; + pix->height = solo_enc->height; + pix->pixelformat = solo_enc->fmt; + pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED : + V4L2_FIELD_NONE; + pix->sizeimage = FRAME_BUF_SIZE; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->priv = 0; + + return 0; +} + +static int solo_enc_g_std(struct file *file, void *priv, v4l2_std_id *i) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + *i = V4L2_STD_NTSC_M; + else + *i = V4L2_STD_PAL; + return 0; +} + +static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id std) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + + return solo_set_video_type(solo_enc->solo_dev, std & V4L2_STD_625_50); +} + +static int solo_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (solo_valid_pixfmt(fsize->pixel_format, solo_dev->type)) + return -EINVAL; + + switch (fsize->index) { + case 0: + fsize->discrete.width = solo_dev->video_hsize >> 1; + fsize->discrete.height = solo_dev->video_vsize; + break; + case 1: + fsize->discrete.width = solo_dev->video_hsize; + fsize->discrete.height = solo_dev->video_vsize << 1; + break; + default: + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + + return 0; +} + +static int solo_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fintv) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (solo_valid_pixfmt(fintv->pixel_format, solo_dev->type)) + return -EINVAL; + if (fintv->index) + return -EINVAL; + if ((fintv->width != solo_dev->video_hsize >> 1 || + fintv->height != solo_dev->video_vsize) && + (fintv->width != solo_dev->video_hsize || + fintv->height != solo_dev->video_vsize << 1)) + return -EINVAL; + + fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; + + fintv->stepwise.min.numerator = 1; + fintv->stepwise.min.denominator = solo_dev->fps; + + fintv->stepwise.max.numerator = 15; + fintv->stepwise.max.denominator = solo_dev->fps; + + fintv->stepwise.step.numerator = 1; + fintv->stepwise.step.denominator = solo_dev->fps; + + return 0; +} + +static int solo_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct v4l2_captureparm *cp = &sp->parm.capture; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = solo_enc->interval; + cp->timeperframe.denominator = solo_enc->solo_dev->fps; + cp->capturemode = 0; + /* XXX: Shouldn't we be able to get/set this from videobuf? */ + cp->readbuffers = 2; + + return 0; +} + +static inline int calc_interval(u8 fps, u32 n, u32 d) +{ + if (!n || !d) + return 1; + if (d == fps) + return n; + n *= fps; + return min(15U, n / d + (n % d >= (fps >> 1))); +} + +static int solo_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct v4l2_fract *t = &sp->parm.capture.timeperframe; + u8 fps = solo_enc->solo_dev->fps; + + if (vb2_is_streaming(&solo_enc->vidq)) + return -EBUSY; + + solo_enc->interval = calc_interval(fps, t->numerator, t->denominator); + solo_update_mode(solo_enc); + return solo_g_parm(file, priv, sp); +} + +static int solo_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct solo_enc_dev *solo_enc = + container_of(ctrl->handler, struct solo_enc_dev, hdl); + struct solo_dev *solo_dev = solo_enc->solo_dev; + int err; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_SHARPNESS: + return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, + ctrl->val); + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + solo_enc->gop = ctrl->val; + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(solo_enc->ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(solo_enc->ch), solo_enc->gop); + return 0; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + solo_enc->qp = ctrl->val; + solo_reg_write(solo_dev, SOLO_VE_CH_QP(solo_enc->ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(solo_enc->ch), solo_enc->qp); + return 0; + case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: + solo_enc->motion_thresh = ctrl->val << 8; + if (!solo_enc->motion_global || !solo_enc->motion_enabled) + return 0; + return solo_set_motion_threshold(solo_dev, solo_enc->ch, + solo_enc->motion_thresh); + case V4L2_CID_DETECT_MD_MODE: + solo_enc->motion_global = ctrl->val == V4L2_DETECT_MD_MODE_GLOBAL; + solo_enc->motion_enabled = ctrl->val > V4L2_DETECT_MD_MODE_DISABLED; + if (ctrl->val) { + if (solo_enc->motion_global) + solo_set_motion_threshold(solo_dev, solo_enc->ch, + solo_enc->motion_thresh); + else + solo_set_motion_block(solo_dev, solo_enc->ch, + solo_enc->md_thresholds->p_cur.p_u16); + } + solo_motion_toggle(solo_enc, ctrl->val); + return 0; + case V4L2_CID_DETECT_MD_THRESHOLD_GRID: + if (solo_enc->motion_enabled && !solo_enc->motion_global) + return solo_set_motion_block(solo_dev, solo_enc->ch, + solo_enc->md_thresholds->p_new.p_u16); + break; + case V4L2_CID_OSD_TEXT: + strcpy(solo_enc->osd_text, ctrl->p_new.p_char); + err = solo_osd_print(solo_enc); + return err; + default: + return -EINVAL; + } + + return 0; +} + +static int solo_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + case V4L2_EVENT_MOTION_DET: + /* Allow for up to 30 events (1 second for NTSC) to be + * stored. */ + return v4l2_event_subscribe(fh, sub, 30, NULL); + } + return -EINVAL; +} + +static const struct v4l2_file_operations solo_enc_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { + .vidioc_querycap = solo_enc_querycap, + .vidioc_s_std = solo_enc_s_std, + .vidioc_g_std = solo_enc_g_std, + /* Input callbacks */ + .vidioc_enum_input = solo_enc_enum_input, + .vidioc_s_input = solo_enc_set_input, + .vidioc_g_input = solo_enc_get_input, + /* Video capture format callbacks */ + .vidioc_enum_fmt_vid_cap = solo_enc_enum_fmt_cap, + .vidioc_try_fmt_vid_cap = solo_enc_try_fmt_cap, + .vidioc_s_fmt_vid_cap = solo_enc_set_fmt_cap, + .vidioc_g_fmt_vid_cap = solo_enc_get_fmt_cap, + /* Streaming I/O */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + /* Frame size and interval */ + .vidioc_enum_framesizes = solo_enum_framesizes, + .vidioc_enum_frameintervals = solo_enum_frameintervals, + /* Video capture parameters */ + .vidioc_s_parm = solo_s_parm, + .vidioc_g_parm = solo_g_parm, + /* Logging and events */ + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = solo_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct video_device solo_enc_template = { + .name = SOLO6X10_NAME, + .fops = &solo_enc_fops, + .ioctl_ops = &solo_enc_ioctl_ops, + .minor = -1, + .release = video_device_release, + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, +}; + +static const struct v4l2_ctrl_ops solo_ctrl_ops = { + .s_ctrl = solo_s_ctrl, +}; + +static const struct v4l2_ctrl_config solo_osd_text_ctrl = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_OSD_TEXT, + .name = "OSD Text", + .type = V4L2_CTRL_TYPE_STRING, + .max = OSD_TEXT_MAX, + .step = 1, +}; + +/* Motion Detection Threshold matrix */ +static const struct v4l2_ctrl_config solo_md_thresholds = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_DETECT_MD_THRESHOLD_GRID, + .dims = { SOLO_MOTION_SZ, SOLO_MOTION_SZ }, + .def = SOLO_DEF_MOT_THRESH, + .max = 65535, + .step = 1, +}; + +static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, + u8 ch, unsigned nr) +{ + struct solo_enc_dev *solo_enc; + struct v4l2_ctrl_handler *hdl; + int ret; + + solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL); + if (!solo_enc) + return ERR_PTR(-ENOMEM); + + hdl = &solo_enc->hdl; + v4l2_ctrl_handler_init(hdl, 10); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, 128); + if (tw28_has_sharpness(solo_dev, ch)) + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 15, 1, 0); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, solo_dev->fps); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 31, 1, SOLO_DEFAULT_QP); + v4l2_ctrl_new_std_menu(hdl, &solo_ctrl_ops, + V4L2_CID_DETECT_MD_MODE, + V4L2_DETECT_MD_MODE_THRESHOLD_GRID, 0, + V4L2_DETECT_MD_MODE_DISABLED); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD, 0, 0xff, 1, + SOLO_DEF_MOT_THRESH >> 8); + v4l2_ctrl_new_custom(hdl, &solo_osd_text_ctrl, NULL); + solo_enc->md_thresholds = + v4l2_ctrl_new_custom(hdl, &solo_md_thresholds, NULL); + if (hdl->error) { + ret = hdl->error; + goto hdl_free; + } + + solo_enc->solo_dev = solo_dev; + solo_enc->ch = ch; + mutex_init(&solo_enc->lock); + spin_lock_init(&solo_enc->av_lock); + INIT_LIST_HEAD(&solo_enc->vidq_active); + solo_enc->fmt = (solo_dev->type == SOLO_DEV_6010) ? + V4L2_PIX_FMT_MPEG4 : V4L2_PIX_FMT_H264; + solo_enc->type = SOLO_ENC_TYPE_STD; + + solo_enc->qp = SOLO_DEFAULT_QP; + solo_enc->gop = solo_dev->fps; + solo_enc->interval = 1; + solo_enc->mode = SOLO_ENC_MODE_CIF; + solo_enc->motion_global = true; + solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; + solo_enc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + solo_enc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + solo_enc->vidq.ops = &solo_enc_video_qops; + solo_enc->vidq.mem_ops = &vb2_dma_sg_memops; + solo_enc->vidq.drv_priv = solo_enc; + solo_enc->vidq.gfp_flags = __GFP_DMA32; + solo_enc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + solo_enc->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); + solo_enc->vidq.lock = &solo_enc->lock; + ret = vb2_queue_init(&solo_enc->vidq); + if (ret) + goto hdl_free; + solo_update_mode(solo_enc); + + spin_lock_init(&solo_enc->motion_lock); + + /* Initialize this per encoder */ + solo_enc->jpeg_len = sizeof(jpeg_header); + memcpy(solo_enc->jpeg_header, jpeg_header, solo_enc->jpeg_len); + + solo_enc->desc_nelts = 32; + solo_enc->desc_items = pci_alloc_consistent(solo_dev->pdev, + sizeof(struct solo_p2m_desc) * + solo_enc->desc_nelts, + &solo_enc->desc_dma); + ret = -ENOMEM; + if (solo_enc->desc_items == NULL) + goto hdl_free; + + solo_enc->vfd = video_device_alloc(); + if (!solo_enc->vfd) + goto pci_free; + + *solo_enc->vfd = solo_enc_template; + solo_enc->vfd->v4l2_dev = &solo_dev->v4l2_dev; + solo_enc->vfd->ctrl_handler = hdl; + solo_enc->vfd->queue = &solo_enc->vidq; + solo_enc->vfd->lock = &solo_enc->lock; + video_set_drvdata(solo_enc->vfd, solo_enc); + ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr); + if (ret < 0) + goto vdev_release; + + snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name), + "%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num, + solo_enc->vfd->num); + + return solo_enc; + +vdev_release: + video_device_release(solo_enc->vfd); +pci_free: + pci_free_consistent(solo_enc->solo_dev->pdev, + sizeof(struct solo_p2m_desc) * solo_enc->desc_nelts, + solo_enc->desc_items, solo_enc->desc_dma); +hdl_free: + v4l2_ctrl_handler_free(hdl); + kfree(solo_enc); + return ERR_PTR(ret); +} + +static void solo_enc_free(struct solo_enc_dev *solo_enc) +{ + if (solo_enc == NULL) + return; + + video_unregister_device(solo_enc->vfd); + v4l2_ctrl_handler_free(&solo_enc->hdl); + kfree(solo_enc); +} + +int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr) +{ + int i; + + init_waitqueue_head(&solo_dev->ring_thread_wait); + + solo_dev->vh_size = sizeof(vop_header); + solo_dev->vh_buf = pci_alloc_consistent(solo_dev->pdev, + solo_dev->vh_size, + &solo_dev->vh_dma); + if (solo_dev->vh_buf == NULL) + return -ENOMEM; + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i, nr); + if (IS_ERR(solo_dev->v4l2_enc[i])) + break; + } + + if (i != solo_dev->nr_chans) { + int ret = PTR_ERR(solo_dev->v4l2_enc[i]); + + while (i--) + solo_enc_free(solo_dev->v4l2_enc[i]); + pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, + solo_dev->vh_buf, solo_dev->vh_dma); + solo_dev->vh_buf = NULL; + return ret; + } + + if (solo_dev->type == SOLO_DEV_6010) + solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4; + else + solo_dev->enc_bw_remain = solo_dev->fps * 4 * 5; + + dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n", + solo_dev->v4l2_enc[0]->vfd->num, + solo_dev->v4l2_enc[solo_dev->nr_chans - 1]->vfd->num); + + return 0; +} + +void solo_enc_v4l2_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) + solo_enc_free(solo_dev->v4l2_enc[i]); + + if (solo_dev->vh_buf) + pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, + solo_dev->vh_buf, solo_dev->vh_dma); +} diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2.c b/drivers/media/pci/solo6x10/solo6x10-v4l2.c new file mode 100644 index 0000000..63ae8a6 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2.c @@ -0,0 +1,733 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#include "solo6x10.h" +#include "solo6x10-tw28.h" + +/* Image size is two fields, SOLO_HW_BPL is one horizontal line in hardware */ +#define SOLO_HW_BPL 2048 +#define solo_vlines(__solo) (__solo->video_vsize * 2) +#define solo_image_size(__solo) (solo_bytesperline(__solo) * \ + solo_vlines(__solo)) +#define solo_bytesperline(__solo) (__solo->video_hsize * 2) + +#define MIN_VID_BUFFERS 2 + +static inline void erase_on(struct solo_dev *solo_dev) +{ + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); + solo_dev->erasing = 1; + solo_dev->frame_blank = 0; +} + +static inline int erase_off(struct solo_dev *solo_dev) +{ + if (!solo_dev->erasing) + return 0; + + /* First time around, assert erase off */ + if (!solo_dev->frame_blank) + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0); + /* Keep the erasing flag on for 8 frames minimum */ + if (solo_dev->frame_blank++ >= 8) + solo_dev->erasing = 0; + + return 1; +} + +void solo_video_in_isr(struct solo_dev *solo_dev) +{ + wake_up_interruptible_all(&solo_dev->disp_thread_wait); +} + +static void solo_win_setup(struct solo_dev *solo_dev, u8 ch, + int sx, int sy, int ex, int ey, int scale) +{ + if (ch >= solo_dev->nr_chans) + return; + + /* Here, we just keep window/channel the same */ + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch), + SOLO_VI_WIN_CHANNEL(ch) | + SOLO_VI_WIN_SX(sx) | + SOLO_VI_WIN_EX(ex) | + SOLO_VI_WIN_SCALE(scale)); + + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch), + SOLO_VI_WIN_SY(sy) | + SOLO_VI_WIN_EY(ey)); +} + +static int solo_v4l2_ch_ext_4up(struct solo_dev *solo_dev, u8 idx, int on) +{ + u8 ch = idx * 4; + + if (ch >= solo_dev->nr_chans) + return -EINVAL; + + if (!on) { + u8 i; + + for (i = ch; i < ch + 4; i++) + solo_win_setup(solo_dev, i, solo_dev->video_hsize, + solo_vlines(solo_dev), + solo_dev->video_hsize, + solo_vlines(solo_dev), 0); + return 0; + } + + /* Row 1 */ + solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2, + solo_vlines(solo_dev) / 2, 3); + solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0, + solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3); + /* Row 2 */ + solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2, + solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3); + solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2, + solo_vlines(solo_dev) / 2, solo_dev->video_hsize, + solo_vlines(solo_dev), 3); + + return 0; +} + +static int solo_v4l2_ch_ext_16up(struct solo_dev *solo_dev, int on) +{ + int sy, ysize, hsize, i; + + if (!on) { + for (i = 0; i < 16; i++) + solo_win_setup(solo_dev, i, solo_dev->video_hsize, + solo_vlines(solo_dev), + solo_dev->video_hsize, + solo_vlines(solo_dev), 0); + return 0; + } + + ysize = solo_vlines(solo_dev) / 4; + hsize = solo_dev->video_hsize / 4; + + for (sy = 0, i = 0; i < 4; i++, sy += ysize) { + solo_win_setup(solo_dev, i * 4, 0, sy, hsize, + sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy, + hsize * 2, sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy, + hsize * 3, sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy, + solo_dev->video_hsize, sy + ysize, 5); + } + + return 0; +} + +static int solo_v4l2_ch(struct solo_dev *solo_dev, u8 ch, int on) +{ + u8 ext_ch; + + if (ch < solo_dev->nr_chans) { + solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize, + on ? 0 : solo_vlines(solo_dev), + solo_dev->video_hsize, solo_vlines(solo_dev), + on ? 1 : 0); + return 0; + } + + if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) + return -EINVAL; + + ext_ch = ch - solo_dev->nr_chans; + + /* 4up's first */ + if (ext_ch < 4) + return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on); + + /* Remaining case is 16up for 16-port */ + return solo_v4l2_ch_ext_16up(solo_dev, on); +} + +static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch) +{ + if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) + return -EINVAL; + + erase_on(solo_dev); + + solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0); + solo_v4l2_ch(solo_dev, ch, 1); + + solo_dev->cur_disp_ch = ch; + + return 0; +} + +static void solo_fillbuf(struct solo_dev *solo_dev, + struct vb2_buffer *vb) +{ + dma_addr_t vbuf; + unsigned int fdma_addr; + int error = -1; + int i; + + vbuf = vb2_dma_contig_plane_dma_addr(vb, 0); + if (!vbuf) + goto finish_buf; + + if (erase_off(solo_dev)) { + void *p = vb2_plane_vaddr(vb, 0); + int image_size = solo_image_size(solo_dev); + + for (i = 0; i < image_size; i += 2) { + ((u8 *)p)[i] = 0x80; + ((u8 *)p)[i + 1] = 0x00; + } + error = 0; + } else { + fdma_addr = SOLO_DISP_EXT_ADDR + (solo_dev->old_write * + (SOLO_HW_BPL * solo_vlines(solo_dev))); + + error = solo_p2m_dma_t(solo_dev, 0, vbuf, fdma_addr, + solo_bytesperline(solo_dev), + solo_vlines(solo_dev), SOLO_HW_BPL); + } + +finish_buf: + if (!error) { + vb2_set_plane_payload(vb, 0, + solo_vlines(solo_dev) * solo_bytesperline(solo_dev)); + vb->v4l2_buf.sequence = solo_dev->sequence++; + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); + } + + vb2_buffer_done(vb, error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); +} + +static void solo_thread_try(struct solo_dev *solo_dev) +{ + struct solo_vb2_buf *vb; + + /* Only "break" from this loop if slock is held, otherwise + * just return. */ + for (;;) { + unsigned int cur_write; + + cur_write = SOLO_VI_STATUS0_PAGE( + solo_reg_read(solo_dev, SOLO_VI_STATUS0)); + if (cur_write == solo_dev->old_write) + return; + + spin_lock(&solo_dev->slock); + + if (list_empty(&solo_dev->vidq_active)) + break; + + vb = list_first_entry(&solo_dev->vidq_active, struct solo_vb2_buf, + list); + + solo_dev->old_write = cur_write; + list_del(&vb->list); + + spin_unlock(&solo_dev->slock); + + solo_fillbuf(solo_dev, &vb->vb); + } + + assert_spin_locked(&solo_dev->slock); + spin_unlock(&solo_dev->slock); +} + +static int solo_thread(void *data) +{ + struct solo_dev *solo_dev = data; + DECLARE_WAITQUEUE(wait, current); + + set_freezable(); + add_wait_queue(&solo_dev->disp_thread_wait, &wait); + + for (;;) { + long timeout = schedule_timeout_interruptible(HZ); + + if (timeout == -ERESTARTSYS || kthread_should_stop()) + break; + solo_thread_try(solo_dev); + try_to_freeze(); + } + + remove_wait_queue(&solo_dev->disp_thread_wait, &wait); + + return 0; +} + +static int solo_start_thread(struct solo_dev *solo_dev) +{ + int ret = 0; + + solo_dev->kthread = kthread_run(solo_thread, solo_dev, SOLO6X10_NAME "_disp"); + + if (IS_ERR(solo_dev->kthread)) { + ret = PTR_ERR(solo_dev->kthread); + solo_dev->kthread = NULL; + return ret; + } + solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN); + + return ret; +} + +static void solo_stop_thread(struct solo_dev *solo_dev) +{ + if (!solo_dev->kthread) + return; + + solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN); + kthread_stop(solo_dev->kthread); + solo_dev->kthread = NULL; +} + +static int solo_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct solo_dev *solo_dev = vb2_get_drv_priv(q); + + sizes[0] = solo_image_size(solo_dev); + alloc_ctxs[0] = solo_dev->alloc_ctx; + *num_planes = 1; + + if (*num_buffers < MIN_VID_BUFFERS) + *num_buffers = MIN_VID_BUFFERS; + + return 0; +} + +static int solo_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct solo_dev *solo_dev = vb2_get_drv_priv(q); + + solo_dev->sequence = 0; + return solo_start_thread(solo_dev); +} + +static void solo_stop_streaming(struct vb2_queue *q) +{ + struct solo_dev *solo_dev = vb2_get_drv_priv(q); + + solo_stop_thread(solo_dev); + INIT_LIST_HEAD(&solo_dev->vidq_active); +} + +static void solo_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct solo_dev *solo_dev = vb2_get_drv_priv(vq); + struct solo_vb2_buf *solo_vb = + container_of(vb, struct solo_vb2_buf, vb); + + spin_lock(&solo_dev->slock); + list_add_tail(&solo_vb->list, &solo_dev->vidq_active); + spin_unlock(&solo_dev->slock); + wake_up_interruptible(&solo_dev->disp_thread_wait); +} + +static const struct vb2_ops solo_video_qops = { + .queue_setup = solo_queue_setup, + .buf_queue = solo_buf_queue, + .start_streaming = solo_start_streaming, + .stop_streaming = solo_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int solo_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + strcpy(cap->driver, SOLO6X10_NAME); + strcpy(cap->card, "Softlogic 6x10"); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", + pci_name(solo_dev->pdev)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int solo_enum_ext_input(struct solo_dev *solo_dev, + struct v4l2_input *input) +{ + static const char * const dispnames_1[] = { "4UP" }; + static const char * const dispnames_2[] = { "4UP-1", "4UP-2" }; + static const char * const dispnames_5[] = { + "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" + }; + const char * const *dispnames; + + if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext)) + return -EINVAL; + + if (solo_dev->nr_ext == 5) + dispnames = dispnames_5; + else if (solo_dev->nr_ext == 2) + dispnames = dispnames_2; + else + dispnames = dispnames_1; + + snprintf(input->name, sizeof(input->name), "Multi %s", + dispnames[input->index - solo_dev->nr_chans]); + + return 0; +} + +static int solo_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + if (input->index >= solo_dev->nr_chans) { + int ret = solo_enum_ext_input(solo_dev, input); + + if (ret < 0) + return ret; + } else { + snprintf(input->name, sizeof(input->name), "Camera %d", + input->index + 1); + + /* We can only check this for normal inputs */ + if (!tw28_get_video_status(solo_dev, input->index)) + input->status = V4L2_IN_ST_NO_SIGNAL; + } + + input->type = V4L2_INPUT_TYPE_CAMERA; + input->std = solo_dev->vfd->tvnorms; + return 0; +} + +static int solo_set_input(struct file *file, void *priv, unsigned int index) +{ + struct solo_dev *solo_dev = video_drvdata(file); + int ret = solo_v4l2_set_ch(solo_dev, index); + + if (!ret) { + while (erase_off(solo_dev)) + /* Do nothing */; + } + + return ret; +} + +static int solo_get_input(struct file *file, void *priv, unsigned int *index) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + *index = solo_dev->cur_disp_ch; + + return 0; +} + +static int solo_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_PIX_FMT_UYVY; + strlcpy(f->description, "UYUV 4:2:2 Packed", sizeof(f->description)); + + return 0; +} + +static int solo_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_dev *solo_dev = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + int image_size = solo_image_size(solo_dev); + + if (pix->pixelformat != V4L2_PIX_FMT_UYVY) + return -EINVAL; + + pix->width = solo_dev->video_hsize; + pix->height = solo_vlines(solo_dev); + pix->sizeimage = image_size; + pix->field = V4L2_FIELD_INTERLACED; + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->priv = 0; + return 0; +} + +static int solo_set_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + if (vb2_is_busy(&solo_dev->vidq)) + return -EBUSY; + + /* For right now, if it doesn't match our running config, + * then fail */ + return solo_try_fmt_cap(file, priv, f); +} + +static int solo_get_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_dev *solo_dev = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = solo_dev->video_hsize; + pix->height = solo_vlines(solo_dev); + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->field = V4L2_FIELD_INTERLACED; + pix->sizeimage = solo_image_size(solo_dev); + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->bytesperline = solo_bytesperline(solo_dev); + pix->priv = 0; + + return 0; +} + +static int solo_g_std(struct file *file, void *priv, v4l2_std_id *i) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + *i = V4L2_STD_NTSC_M; + else + *i = V4L2_STD_PAL; + return 0; +} + +int solo_set_video_type(struct solo_dev *solo_dev, bool is_50hz) +{ + int i; + + /* Make sure all video nodes are idle */ + if (vb2_is_busy(&solo_dev->vidq)) + return -EBUSY; + for (i = 0; i < solo_dev->nr_chans; i++) + if (vb2_is_busy(&solo_dev->v4l2_enc[i]->vidq)) + return -EBUSY; + solo_dev->video_type = is_50hz ? SOLO_VO_FMT_TYPE_PAL : + SOLO_VO_FMT_TYPE_NTSC; + /* Reconfigure for the new standard */ + solo_disp_init(solo_dev); + solo_enc_init(solo_dev); + solo_tw28_init(solo_dev); + for (i = 0; i < solo_dev->nr_chans; i++) + solo_update_mode(solo_dev->v4l2_enc[i]); + return solo_v4l2_set_ch(solo_dev, solo_dev->cur_disp_ch); +} + +static int solo_s_std(struct file *file, void *priv, v4l2_std_id std) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + return solo_set_video_type(solo_dev, std & V4L2_STD_625_50); +} + +static int solo_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct solo_dev *solo_dev = + container_of(ctrl->handler, struct solo_dev, disp_hdl); + + switch (ctrl->id) { + case V4L2_CID_MOTION_TRACE: + if (ctrl->val) { + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, + SOLO_VI_MOTION_Y_ADD | + SOLO_VI_MOTION_Y_VALUE(0x20) | + SOLO_VI_MOTION_CB_VALUE(0x10) | + SOLO_VI_MOTION_CR_VALUE(0x10)); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, + SOLO_VI_MOTION_CR_ADD | + SOLO_VI_MOTION_Y_VALUE(0x10) | + SOLO_VI_MOTION_CB_VALUE(0x80) | + SOLO_VI_MOTION_CR_VALUE(0x10)); + } else { + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); + } + return 0; + default: + break; + } + return -EINVAL; +} + +static const struct v4l2_file_operations solo_v4l2_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = { + .vidioc_querycap = solo_querycap, + .vidioc_s_std = solo_s_std, + .vidioc_g_std = solo_g_std, + /* Input callbacks */ + .vidioc_enum_input = solo_enum_input, + .vidioc_s_input = solo_set_input, + .vidioc_g_input = solo_get_input, + /* Video capture format callbacks */ + .vidioc_enum_fmt_vid_cap = solo_enum_fmt_cap, + .vidioc_try_fmt_vid_cap = solo_try_fmt_cap, + .vidioc_s_fmt_vid_cap = solo_set_fmt_cap, + .vidioc_g_fmt_vid_cap = solo_get_fmt_cap, + /* Streaming I/O */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + /* Logging and events */ + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device solo_v4l2_template = { + .name = SOLO6X10_NAME, + .fops = &solo_v4l2_fops, + .ioctl_ops = &solo_v4l2_ioctl_ops, + .minor = -1, + .release = video_device_release, + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, +}; + +static const struct v4l2_ctrl_ops solo_ctrl_ops = { + .s_ctrl = solo_s_ctrl, +}; + +static const struct v4l2_ctrl_config solo_motion_trace_ctrl = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_MOTION_TRACE, + .name = "Motion Detection Trace", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr) +{ + int ret; + int i; + + init_waitqueue_head(&solo_dev->disp_thread_wait); + spin_lock_init(&solo_dev->slock); + mutex_init(&solo_dev->lock); + INIT_LIST_HEAD(&solo_dev->vidq_active); + + solo_dev->vfd = video_device_alloc(); + if (!solo_dev->vfd) + return -ENOMEM; + + *solo_dev->vfd = solo_v4l2_template; + solo_dev->vfd->v4l2_dev = &solo_dev->v4l2_dev; + solo_dev->vfd->queue = &solo_dev->vidq; + solo_dev->vfd->lock = &solo_dev->lock; + v4l2_ctrl_handler_init(&solo_dev->disp_hdl, 1); + v4l2_ctrl_new_custom(&solo_dev->disp_hdl, &solo_motion_trace_ctrl, NULL); + if (solo_dev->disp_hdl.error) { + ret = solo_dev->disp_hdl.error; + goto fail; + } + solo_dev->vfd->ctrl_handler = &solo_dev->disp_hdl; + + video_set_drvdata(solo_dev->vfd, solo_dev); + + solo_dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + solo_dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + solo_dev->vidq.ops = &solo_video_qops; + solo_dev->vidq.mem_ops = &vb2_dma_contig_memops; + solo_dev->vidq.drv_priv = solo_dev; + solo_dev->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + solo_dev->vidq.gfp_flags = __GFP_DMA32; + solo_dev->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); + solo_dev->vidq.lock = &solo_dev->lock; + ret = vb2_queue_init(&solo_dev->vidq); + if (ret < 0) + goto fail; + + solo_dev->alloc_ctx = vb2_dma_contig_init_ctx(&solo_dev->pdev->dev); + if (IS_ERR(solo_dev->alloc_ctx)) { + dev_err(&solo_dev->pdev->dev, "Can't allocate buffer context"); + return PTR_ERR(solo_dev->alloc_ctx); + } + + /* Cycle all the channels and clear */ + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_v4l2_set_ch(solo_dev, i); + while (erase_off(solo_dev)) + /* Do nothing */; + } + + /* Set the default display channel */ + solo_v4l2_set_ch(solo_dev, 0); + while (erase_off(solo_dev)) + /* Do nothing */; + + ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr); + if (ret < 0) + goto fail; + + snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)", + SOLO6X10_NAME, solo_dev->vfd->num); + + dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with " + "%d inputs (%d extended)\n", solo_dev->vfd->num, + solo_dev->nr_chans, solo_dev->nr_ext); + + return 0; + +fail: + video_device_release(solo_dev->vfd); + vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); + v4l2_ctrl_handler_free(&solo_dev->disp_hdl); + solo_dev->vfd = NULL; + return ret; +} + +void solo_v4l2_exit(struct solo_dev *solo_dev) +{ + if (solo_dev->vfd == NULL) + return; + + video_unregister_device(solo_dev->vfd); + vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); + v4l2_ctrl_handler_free(&solo_dev->disp_hdl); + solo_dev->vfd = NULL; +} diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h new file mode 100644 index 0000000..c6154b0 --- /dev/null +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC + * + * Original author: + * Ben Collins + * + * Additional work by: + * John Brooks + * + * 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 __SOLO6X10_H +#define __SOLO6X10_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "solo6x10-regs.h" + +#ifndef PCI_VENDOR_ID_SOFTLOGIC +#define PCI_VENDOR_ID_SOFTLOGIC 0x9413 +#define PCI_DEVICE_ID_SOLO6010 0x6010 +#define PCI_DEVICE_ID_SOLO6110 0x6110 +#endif + +#ifndef PCI_VENDOR_ID_BLUECHERRY +#define PCI_VENDOR_ID_BLUECHERRY 0x1BB3 +/* Neugent Softlogic 6010 based cards */ +#define PCI_DEVICE_ID_NEUSOLO_4 0x4304 +#define PCI_DEVICE_ID_NEUSOLO_9 0x4309 +#define PCI_DEVICE_ID_NEUSOLO_16 0x4310 +/* Bluecherry Softlogic 6010 based cards */ +#define PCI_DEVICE_ID_BC_SOLO_4 0x4E04 +#define PCI_DEVICE_ID_BC_SOLO_9 0x4E09 +#define PCI_DEVICE_ID_BC_SOLO_16 0x4E10 +/* Bluecherry Softlogic 6110 based cards */ +#define PCI_DEVICE_ID_BC_6110_4 0x5304 +#define PCI_DEVICE_ID_BC_6110_8 0x5308 +#define PCI_DEVICE_ID_BC_6110_16 0x5310 +#endif /* Bluecherry */ + +/* Used in pci_device_id, and solo_dev->type */ +#define SOLO_DEV_6010 0 +#define SOLO_DEV_6110 1 + +#define SOLO6X10_NAME "solo6x10" + +#define SOLO_MAX_CHANNELS 16 + +#define SOLO6X10_VERSION "3.0.0" + +/* + * The SOLO6x10 actually has 8 i2c channels, but we only use 2. + * 0 - Techwell chip(s) + * 1 - SAA7128 + */ +#define SOLO_I2C_ADAPTERS 2 +#define SOLO_I2C_TW 0 +#define SOLO_I2C_SAA 1 + +/* DMA Engine setup */ +#define SOLO_NR_P2M 4 +#define SOLO_NR_P2M_DESC 256 +#define SOLO_P2M_DESC_SIZE (SOLO_NR_P2M_DESC * 16) + +/* Encoder standard modes */ +#define SOLO_ENC_MODE_CIF 2 +#define SOLO_ENC_MODE_HD1 1 +#define SOLO_ENC_MODE_D1 9 + +#define SOLO_DEFAULT_QP 3 + +#define SOLO_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) +#define V4L2_CID_MOTION_TRACE (SOLO_CID_CUSTOM_BASE+2) +#define V4L2_CID_OSD_TEXT (SOLO_CID_CUSTOM_BASE+3) + +/* + * Motion thresholds are in a table of 64x64 samples, with + * each sample representing 16x16 pixels of the source. In + * effect, 44x30 samples are used for NTSC, and 44x36 for PAL. + * The 5th sample on the 10th row is (10*64)+5 = 645. + * + * Internally it is stored as a 45x45 array (45*16 = 720, which is the + * maximum PAL/NTSC width). + */ +#define SOLO_MOTION_SZ (45) + +enum SOLO_I2C_STATE { + IIC_STATE_IDLE, + IIC_STATE_START, + IIC_STATE_READ, + IIC_STATE_WRITE, + IIC_STATE_STOP +}; + +/* Defined in Table 4-16, Page 68-69 of the 6010 Datasheet */ +struct solo_p2m_desc { + u32 ctrl; + u32 cfg; + u32 dma_addr; + u32 ext_addr; +}; + +struct solo_p2m_dev { + struct mutex mutex; + struct completion completion; + int desc_count; + int desc_idx; + struct solo_p2m_desc *descs; + int error; +}; + +#define OSD_TEXT_MAX 44 + +struct solo_vb2_buf { + struct vb2_buffer vb; + struct list_head list; +}; + +enum solo_enc_types { + SOLO_ENC_TYPE_STD, + SOLO_ENC_TYPE_EXT, +}; + +struct solo_enc_dev { + struct solo_dev *solo_dev; + /* V4L2 Items */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *md_thresholds; + struct video_device *vfd; + /* General accounting */ + struct mutex lock; + spinlock_t motion_lock; + u8 ch; + u8 mode, gop, qp, interlaced, interval; + u8 bw_weight; + u16 motion_thresh; + bool motion_global; + bool motion_enabled; + bool motion_last_state; + u8 frames_since_last_motion; + u16 width; + u16 height; + + /* OSD buffers */ + char osd_text[OSD_TEXT_MAX + 1]; + u8 osd_buf[SOLO_EOSD_EXT_SIZE_MAX] + __aligned(4); + + /* VOP stuff */ + unsigned char vop[64]; + int vop_len; + unsigned char jpeg_header[1024]; + int jpeg_len; + + u32 fmt; + enum solo_enc_types type; + u32 sequence; + struct vb2_queue vidq; + struct list_head vidq_active; + int desc_count; + int desc_nelts; + struct solo_p2m_desc *desc_items; + dma_addr_t desc_dma; + spinlock_t av_lock; +}; + +/* The SOLO6x10 PCI Device */ +struct solo_dev { + /* General stuff */ + struct pci_dev *pdev; + int type; + unsigned int time_sync; + unsigned int usec_lsb; + unsigned int clock_mhz; + u8 __iomem *reg_base; + int nr_chans; + int nr_ext; + u32 irq_mask; + u32 motion_mask; + spinlock_t reg_io_lock; + struct v4l2_device v4l2_dev; + + /* tw28xx accounting */ + u8 tw2865, tw2864, tw2815; + u8 tw28_cnt; + + /* i2c related items */ + struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS]; + enum SOLO_I2C_STATE i2c_state; + struct mutex i2c_mutex; + int i2c_id; + wait_queue_head_t i2c_wait; + struct i2c_msg *i2c_msg; + unsigned int i2c_msg_num; + unsigned int i2c_msg_ptr; + + /* P2M DMA Engine */ + struct solo_p2m_dev p2m_dev[SOLO_NR_P2M]; + atomic_t p2m_count; + int p2m_jiffies; + unsigned int p2m_timeouts; + + /* V4L2 Display items */ + struct video_device *vfd; + unsigned int erasing; + unsigned int frame_blank; + u8 cur_disp_ch; + wait_queue_head_t disp_thread_wait; + struct v4l2_ctrl_handler disp_hdl; + + /* V4L2 Encoder items */ + struct solo_enc_dev *v4l2_enc[SOLO_MAX_CHANNELS]; + u16 enc_bw_remain; + /* IDX into hw mp4 encoder */ + u8 enc_idx; + + /* Current video settings */ + u32 video_type; + u16 video_hsize, video_vsize; + u16 vout_hstart, vout_vstart; + u16 vin_hstart, vin_vstart; + u8 fps; + + /* JPEG Qp setting */ + spinlock_t jpeg_qp_lock; + u32 jpeg_qp[2]; + + /* Audio components */ + struct snd_card *snd_card; + struct snd_pcm *snd_pcm; + atomic_t snd_users; + int g723_hw_idx; + + /* sysfs stuffs */ + struct device dev; + int sdram_size; + struct bin_attribute sdram_attr; + unsigned int sys_config; + + /* Ring thread */ + struct task_struct *ring_thread; + wait_queue_head_t ring_thread_wait; + + /* VOP_HEADER handling */ + void *vh_buf; + dma_addr_t vh_dma; + int vh_size; + + /* Buffer handling */ + struct vb2_queue vidq; + struct vb2_alloc_ctx *alloc_ctx; + u32 sequence; + struct task_struct *kthread; + struct mutex lock; + spinlock_t slock; + int old_write; + struct list_head vidq_active; +}; + +static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg) +{ + unsigned long flags; + u32 ret; + u16 val; + + spin_lock_irqsave(&solo_dev->reg_io_lock, flags); + + ret = readl(solo_dev->reg_base + reg); + rmb(); + pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); + rmb(); + + spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); + + return ret; +} + +static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, + u32 data) +{ + unsigned long flags; + u16 val; + + spin_lock_irqsave(&solo_dev->reg_io_lock, flags); + + writel(data, solo_dev->reg_base + reg); + wmb(); + pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); + rmb(); + + spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); +} + +static inline void solo_irq_on(struct solo_dev *dev, u32 mask) +{ + dev->irq_mask |= mask; + solo_reg_write(dev, SOLO_IRQ_MASK, dev->irq_mask); +} + +static inline void solo_irq_off(struct solo_dev *dev, u32 mask) +{ + dev->irq_mask &= ~mask; + solo_reg_write(dev, SOLO_IRQ_MASK, dev->irq_mask); +} + +/* Init/exit routines for subsystems */ +int solo_disp_init(struct solo_dev *solo_dev); +void solo_disp_exit(struct solo_dev *solo_dev); + +int solo_gpio_init(struct solo_dev *solo_dev); +void solo_gpio_exit(struct solo_dev *solo_dev); + +int solo_i2c_init(struct solo_dev *solo_dev); +void solo_i2c_exit(struct solo_dev *solo_dev); + +int solo_p2m_init(struct solo_dev *solo_dev); +void solo_p2m_exit(struct solo_dev *solo_dev); + +int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr); +void solo_v4l2_exit(struct solo_dev *solo_dev); + +int solo_enc_init(struct solo_dev *solo_dev); +void solo_enc_exit(struct solo_dev *solo_dev); + +int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr); +void solo_enc_v4l2_exit(struct solo_dev *solo_dev); + +int solo_g723_init(struct solo_dev *solo_dev); +void solo_g723_exit(struct solo_dev *solo_dev); + +/* ISR's */ +int solo_i2c_isr(struct solo_dev *solo_dev); +void solo_p2m_isr(struct solo_dev *solo_dev, int id); +void solo_p2m_error_isr(struct solo_dev *solo_dev); +void solo_enc_v4l2_isr(struct solo_dev *solo_dev); +void solo_g723_isr(struct solo_dev *solo_dev); +void solo_motion_isr(struct solo_dev *solo_dev); +void solo_video_in_isr(struct solo_dev *solo_dev); + +/* i2c read/write */ +u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off); +void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off, + u8 data); + +/* P2M DMA */ +int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size); +int solo_p2m_dma(struct solo_dev *solo_dev, int wr, + void *sys_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size); +void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size); +int solo_p2m_dma_desc(struct solo_dev *solo_dev, + struct solo_p2m_desc *desc, dma_addr_t desc_dma, + int desc_cnt); + +/* Global s_std ioctl */ +int solo_set_video_type(struct solo_dev *solo_dev, bool is_50hz); +void solo_update_mode(struct solo_enc_dev *solo_enc); + +/* Set the threshold for motion detection */ +int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val); +int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, + const u16 *thresholds); +#define SOLO_DEF_MOT_THRESH 0x0300 + +/* Write text on OSD */ +int solo_osd_print(struct solo_enc_dev *solo_enc); + +/* EEPROM commands */ +unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en); +unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc); +int solo_eeprom_write(struct solo_dev *solo_dev, int loc, + unsigned short data); + +/* JPEG Qp functions */ +void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch, + unsigned int qp); +int solo_g_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch); + +#define CHK_FLAGS(v, flags) (((v) & (flags)) == (flags)) + +#endif /* __SOLO6X10_H */ diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index e91c538..3312a3b 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -33,8 +33,6 @@ source "drivers/staging/media/go7007/Kconfig" source "drivers/staging/media/omap24xx/Kconfig" -source "drivers/staging/media/solo6x10/Kconfig" - source "drivers/staging/media/omap4iss/Kconfig" # Keep LIRC at the end, as it has sub-menus diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 4f6c0ee..205eba2 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -2,7 +2,6 @@ obj-$(CONFIG_DVB_AS102) += as102/ obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_LIRC_STAGING) += lirc/ -obj-$(CONFIG_SOLO6X10) += solo6x10/ obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ diff --git a/drivers/staging/media/solo6x10/Kconfig b/drivers/staging/media/solo6x10/Kconfig deleted file mode 100644 index 1ce2819..0000000 --- a/drivers/staging/media/solo6x10/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -config SOLO6X10 - tristate "Bluecherry / Softlogic 6x10 capture cards (MPEG-4/H.264)" - depends on PCI && VIDEO_DEV && SND && I2C - select BITREVERSE - select FONT_SUPPORT - select FONT_8x16 - select VIDEOBUF2_DMA_SG - select VIDEOBUF2_DMA_CONTIG - select SND_PCM - select FONT_8x16 - ---help--- - This driver supports the Bluecherry H.264 and MPEG-4 hardware - compression capture cards and other Softlogic-based ones. - - Following cards have been tested: - * Bluecherry BC-H16480A (PCIe, 16 port, H.264) - * Bluecherry BC-H04120A (PCIe, 4 port, H.264) - * Bluecherry BC-H04120A-MPCI (Mini-PCI, 4 port, H.264) - * Bluecherry BC-04120A (PCIe, 4 port, MPEG-4) diff --git a/drivers/staging/media/solo6x10/Makefile b/drivers/staging/media/solo6x10/Makefile deleted file mode 100644 index 7aae118..0000000 --- a/drivers/staging/media/solo6x10/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -solo6x10-y := solo6x10-core.o solo6x10-i2c.o solo6x10-p2m.o solo6x10-v4l2.o \ - solo6x10-tw28.o solo6x10-gpio.o solo6x10-disp.o solo6x10-enc.o \ - solo6x10-v4l2-enc.o solo6x10-g723.o solo6x10-eeprom.o - -obj-$(CONFIG_SOLO6X10) += solo6x10.o diff --git a/drivers/staging/media/solo6x10/TODO b/drivers/staging/media/solo6x10/TODO deleted file mode 100644 index 7b8db75..0000000 --- a/drivers/staging/media/solo6x10/TODO +++ /dev/null @@ -1,15 +0,0 @@ -- batch up desc requests for more efficient use of p2m? -- encoder on/off controls -- mpeg cid bitrate mode (vbr/cbr) -- mpeg cid bitrate/bitrate-peak -- mpeg encode of user data -- mpeg decode of user data -- implement CID controls for mozaic areas - -- sound - - implement playback via external sound jack - - implement loopback of external sound jack with incoming audio? - - implement pause/resume (make use of in bc-server) - -Please send patches to the linux media list and -Cc Ismael Luceno . diff --git a/drivers/staging/media/solo6x10/solo6x10-core.c b/drivers/staging/media/solo6x10/solo6x10-core.c deleted file mode 100644 index 172583d..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-core.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include "solo6x10.h" -#include "solo6x10-tw28.h" - -MODULE_DESCRIPTION("Softlogic 6x10 MPEG4/H.264/G.723 CODEC V4L2/ALSA Driver"); -MODULE_AUTHOR("Bluecherry "); -MODULE_VERSION(SOLO6X10_VERSION); -MODULE_LICENSE("GPL"); - -static unsigned video_nr = -1; -module_param(video_nr, uint, 0644); -MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)"); - -static int full_eeprom; /* default is only top 64B */ -module_param(full_eeprom, uint, 0644); -MODULE_PARM_DESC(full_eeprom, "Allow access to full 128B EEPROM (dangerous)"); - - -static void solo_set_time(struct solo_dev *solo_dev) -{ - struct timespec ts; - - ktime_get_ts(&ts); - - solo_reg_write(solo_dev, SOLO_TIMER_SEC, ts.tv_sec); - solo_reg_write(solo_dev, SOLO_TIMER_USEC, ts.tv_nsec / NSEC_PER_USEC); -} - -static void solo_timer_sync(struct solo_dev *solo_dev) -{ - u32 sec, usec; - struct timespec ts; - long diff; - - if (solo_dev->type != SOLO_DEV_6110) - return; - - if (++solo_dev->time_sync < 60) - return; - - solo_dev->time_sync = 0; - - sec = solo_reg_read(solo_dev, SOLO_TIMER_SEC); - usec = solo_reg_read(solo_dev, SOLO_TIMER_USEC); - - ktime_get_ts(&ts); - - diff = (long)ts.tv_sec - (long)sec; - diff = (diff * 1000000) - + ((long)(ts.tv_nsec / NSEC_PER_USEC) - (long)usec); - - if (diff > 1000 || diff < -1000) { - solo_set_time(solo_dev); - } else if (diff) { - long usec_lsb = solo_dev->usec_lsb; - - usec_lsb -= diff / 4; - if (usec_lsb < 0) - usec_lsb = 0; - else if (usec_lsb > 255) - usec_lsb = 255; - - solo_dev->usec_lsb = usec_lsb; - solo_reg_write(solo_dev, SOLO_TIMER_USEC_LSB, - solo_dev->usec_lsb); - } -} - -static irqreturn_t solo_isr(int irq, void *data) -{ - struct solo_dev *solo_dev = data; - u32 status; - int i; - - status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); - if (!status) - return IRQ_NONE; - - if (status & ~solo_dev->irq_mask) { - solo_reg_write(solo_dev, SOLO_IRQ_STAT, - status & ~solo_dev->irq_mask); - status &= solo_dev->irq_mask; - } - - if (status & SOLO_IRQ_PCI_ERR) - solo_p2m_error_isr(solo_dev); - - for (i = 0; i < SOLO_NR_P2M; i++) - if (status & SOLO_IRQ_P2M(i)) - solo_p2m_isr(solo_dev, i); - - if (status & SOLO_IRQ_IIC) - solo_i2c_isr(solo_dev); - - if (status & SOLO_IRQ_VIDEO_IN) { - solo_video_in_isr(solo_dev); - solo_timer_sync(solo_dev); - } - - if (status & SOLO_IRQ_ENCODER) - solo_enc_v4l2_isr(solo_dev); - - if (status & SOLO_IRQ_G723) - solo_g723_isr(solo_dev); - - /* Clear all interrupts handled */ - solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); - - return IRQ_HANDLED; -} - -static void free_solo_dev(struct solo_dev *solo_dev) -{ - struct pci_dev *pdev; - - if (!solo_dev) - return; - - if (solo_dev->dev.parent) - device_unregister(&solo_dev->dev); - - pdev = solo_dev->pdev; - - /* If we never initialized the PCI device, then nothing else - * below here needs cleanup */ - if (!pdev) { - kfree(solo_dev); - return; - } - - if (solo_dev->reg_base) { - /* Bring down the sub-devices first */ - solo_g723_exit(solo_dev); - solo_enc_v4l2_exit(solo_dev); - solo_enc_exit(solo_dev); - solo_v4l2_exit(solo_dev); - solo_disp_exit(solo_dev); - solo_gpio_exit(solo_dev); - solo_p2m_exit(solo_dev); - solo_i2c_exit(solo_dev); - - /* Now cleanup the PCI device */ - solo_irq_off(solo_dev, ~0); - pci_iounmap(pdev, solo_dev->reg_base); - if (pdev->irq) - free_irq(pdev->irq, solo_dev); - } - - pci_release_regions(pdev); - pci_disable_device(pdev); - v4l2_device_unregister(&solo_dev->v4l2_dev); - pci_set_drvdata(pdev, NULL); - - kfree(solo_dev); -} - -static ssize_t eeprom_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - unsigned short *p = (unsigned short *)buf; - int i; - - if (count & 0x1) - dev_warn(dev, "EEPROM Write not aligned (truncating)\n"); - - if (!full_eeprom && count > 64) { - dev_warn(dev, "EEPROM Write truncated to 64 bytes\n"); - count = 64; - } else if (full_eeprom && count > 128) { - dev_warn(dev, "EEPROM Write truncated to 128 bytes\n"); - count = 128; - } - - solo_eeprom_ewen(solo_dev, 1); - - for (i = full_eeprom ? 0 : 32; i < min((int)(full_eeprom ? 64 : 32), - (int)(count / 2)); i++) - solo_eeprom_write(solo_dev, i, cpu_to_be16(p[i])); - - solo_eeprom_ewen(solo_dev, 0); - - return count; -} - -static ssize_t eeprom_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - unsigned short *p = (unsigned short *)buf; - int count = (full_eeprom ? 128 : 64); - int i; - - for (i = (full_eeprom ? 0 : 32); i < (count / 2); i++) - p[i] = be16_to_cpu(solo_eeprom_read(solo_dev, i)); - - return count; -} - -static ssize_t p2m_timeouts_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - - return sprintf(buf, "%d\n", solo_dev->p2m_timeouts); -} - -static ssize_t sdram_size_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - - return sprintf(buf, "%dMegs\n", solo_dev->sdram_size >> 20); -} - -static ssize_t tw28xx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - - return sprintf(buf, "tw2815[%d] tw2864[%d] tw2865[%d]\n", - hweight32(solo_dev->tw2815), - hweight32(solo_dev->tw2864), - hweight32(solo_dev->tw2865)); -} - -static ssize_t input_map_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - unsigned int val; - char *out = buf; - - val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_0); - out += sprintf(out, "Channel 0 => Input %d\n", val & 0x1f); - out += sprintf(out, "Channel 1 => Input %d\n", (val >> 5) & 0x1f); - out += sprintf(out, "Channel 2 => Input %d\n", (val >> 10) & 0x1f); - out += sprintf(out, "Channel 3 => Input %d\n", (val >> 15) & 0x1f); - out += sprintf(out, "Channel 4 => Input %d\n", (val >> 20) & 0x1f); - out += sprintf(out, "Channel 5 => Input %d\n", (val >> 25) & 0x1f); - - val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_1); - out += sprintf(out, "Channel 6 => Input %d\n", val & 0x1f); - out += sprintf(out, "Channel 7 => Input %d\n", (val >> 5) & 0x1f); - out += sprintf(out, "Channel 8 => Input %d\n", (val >> 10) & 0x1f); - out += sprintf(out, "Channel 9 => Input %d\n", (val >> 15) & 0x1f); - out += sprintf(out, "Channel 10 => Input %d\n", (val >> 20) & 0x1f); - out += sprintf(out, "Channel 11 => Input %d\n", (val >> 25) & 0x1f); - - val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_2); - out += sprintf(out, "Channel 12 => Input %d\n", val & 0x1f); - out += sprintf(out, "Channel 13 => Input %d\n", (val >> 5) & 0x1f); - out += sprintf(out, "Channel 14 => Input %d\n", (val >> 10) & 0x1f); - out += sprintf(out, "Channel 15 => Input %d\n", (val >> 15) & 0x1f); - out += sprintf(out, "Spot Output => Input %d\n", (val >> 20) & 0x1f); - - return out - buf; -} - -static ssize_t p2m_timeout_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - unsigned long ms; - int ret = kstrtoul(buf, 10, &ms); - - if (ret < 0 || ms > 200) - return -EINVAL; - solo_dev->p2m_jiffies = msecs_to_jiffies(ms); - - return count; -} - -static ssize_t p2m_timeout_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - - return sprintf(buf, "%ums\n", jiffies_to_msecs(solo_dev->p2m_jiffies)); -} - -static ssize_t intervals_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - char *out = buf; - int fps = solo_dev->fps; - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) { - out += sprintf(out, "Channel %d: %d/%d (0x%08x)\n", - i, solo_dev->v4l2_enc[i]->interval, fps, - solo_reg_read(solo_dev, SOLO_CAP_CH_INTV(i))); - } - - return out - buf; -} - -static ssize_t sdram_offsets_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - char *out = buf; - - out += sprintf(out, "DISP: 0x%08x @ 0x%08x\n", - SOLO_DISP_EXT_ADDR, - SOLO_DISP_EXT_SIZE); - - out += sprintf(out, "EOSD: 0x%08x @ 0x%08x (0x%08x * %d)\n", - SOLO_EOSD_EXT_ADDR, - SOLO_EOSD_EXT_AREA(solo_dev), - SOLO_EOSD_EXT_SIZE(solo_dev), - SOLO_EOSD_EXT_AREA(solo_dev) / - SOLO_EOSD_EXT_SIZE(solo_dev)); - - out += sprintf(out, "MOTI: 0x%08x @ 0x%08x\n", - SOLO_MOTION_EXT_ADDR(solo_dev), - SOLO_MOTION_EXT_SIZE); - - out += sprintf(out, "G723: 0x%08x @ 0x%08x\n", - SOLO_G723_EXT_ADDR(solo_dev), - SOLO_G723_EXT_SIZE); - - out += sprintf(out, "CAPT: 0x%08x @ 0x%08x (0x%08x * %d)\n", - SOLO_CAP_EXT_ADDR(solo_dev), - SOLO_CAP_EXT_SIZE(solo_dev), - SOLO_CAP_PAGE_SIZE, - SOLO_CAP_EXT_SIZE(solo_dev) / SOLO_CAP_PAGE_SIZE); - - out += sprintf(out, "EREF: 0x%08x @ 0x%08x (0x%08x * %d)\n", - SOLO_EREF_EXT_ADDR(solo_dev), - SOLO_EREF_EXT_AREA(solo_dev), - SOLO_EREF_EXT_SIZE, - SOLO_EREF_EXT_AREA(solo_dev) / SOLO_EREF_EXT_SIZE); - - out += sprintf(out, "MPEG: 0x%08x @ 0x%08x\n", - SOLO_MP4E_EXT_ADDR(solo_dev), - SOLO_MP4E_EXT_SIZE(solo_dev)); - - out += sprintf(out, "JPEG: 0x%08x @ 0x%08x\n", - SOLO_JPEG_EXT_ADDR(solo_dev), - SOLO_JPEG_EXT_SIZE(solo_dev)); - - return out - buf; -} - -static ssize_t sdram_show(struct file *file, struct kobject *kobj, - struct bin_attribute *a, char *buf, - loff_t off, size_t count) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct solo_dev *solo_dev = - container_of(dev, struct solo_dev, dev); - const int size = solo_dev->sdram_size; - - if (off >= size) - return 0; - - if (off + count > size) - count = size - off; - - if (solo_p2m_dma(solo_dev, 0, buf, off, count, 0, 0)) - return -EIO; - - return count; -} - -static const struct device_attribute solo_dev_attrs[] = { - __ATTR(eeprom, 0640, eeprom_show, eeprom_store), - __ATTR(p2m_timeout, 0644, p2m_timeout_show, p2m_timeout_store), - __ATTR_RO(p2m_timeouts), - __ATTR_RO(sdram_size), - __ATTR_RO(tw28xx), - __ATTR_RO(input_map), - __ATTR_RO(intervals), - __ATTR_RO(sdram_offsets), -}; - -static void solo_device_release(struct device *dev) -{ - /* Do nothing */ -} - -static int solo_sysfs_init(struct solo_dev *solo_dev) -{ - struct bin_attribute *sdram_attr = &solo_dev->sdram_attr; - struct device *dev = &solo_dev->dev; - const char *driver; - int i; - - if (solo_dev->type == SOLO_DEV_6110) - driver = "solo6110"; - else - driver = "solo6010"; - - dev->release = solo_device_release; - dev->parent = &solo_dev->pdev->dev; - set_dev_node(dev, dev_to_node(&solo_dev->pdev->dev)); - dev_set_name(dev, "%s-%d-%d", driver, solo_dev->vfd->num, - solo_dev->nr_chans); - - if (device_register(dev)) { - dev->parent = NULL; - return -ENOMEM; - } - - for (i = 0; i < ARRAY_SIZE(solo_dev_attrs); i++) { - if (device_create_file(dev, &solo_dev_attrs[i])) { - device_unregister(dev); - return -ENOMEM; - } - } - - sysfs_attr_init(&sdram_attr->attr); - sdram_attr->attr.name = "sdram"; - sdram_attr->attr.mode = 0440; - sdram_attr->read = sdram_show; - sdram_attr->size = solo_dev->sdram_size; - - if (device_create_bin_file(dev, sdram_attr)) { - device_unregister(dev); - return -ENOMEM; - } - - return 0; -} - -static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct solo_dev *solo_dev; - int ret; - u8 chip_id; - - solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); - if (solo_dev == NULL) - return -ENOMEM; - - if (id->driver_data == SOLO_DEV_6010) - dev_info(&pdev->dev, "Probing Softlogic 6010\n"); - else - dev_info(&pdev->dev, "Probing Softlogic 6110\n"); - - solo_dev->type = id->driver_data; - solo_dev->pdev = pdev; - spin_lock_init(&solo_dev->reg_io_lock); - ret = v4l2_device_register(&pdev->dev, &solo_dev->v4l2_dev); - if (ret) - goto fail_probe; - - /* Only for during init */ - solo_dev->p2m_jiffies = msecs_to_jiffies(100); - - ret = pci_enable_device(pdev); - if (ret) - goto fail_probe; - - pci_set_master(pdev); - - /* RETRY/TRDY Timeout disabled */ - pci_write_config_byte(pdev, 0x40, 0x00); - pci_write_config_byte(pdev, 0x41, 0x00); - - ret = pci_request_regions(pdev, SOLO6X10_NAME); - if (ret) - goto fail_probe; - - solo_dev->reg_base = pci_ioremap_bar(pdev, 0); - if (solo_dev->reg_base == NULL) { - ret = -ENOMEM; - goto fail_probe; - } - - chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & - SOLO_CHIP_ID_MASK; - switch (chip_id) { - case 7: - solo_dev->nr_chans = 16; - solo_dev->nr_ext = 5; - break; - case 6: - solo_dev->nr_chans = 8; - solo_dev->nr_ext = 2; - break; - default: - dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n", - chip_id); - case 5: - solo_dev->nr_chans = 4; - solo_dev->nr_ext = 1; - } - - /* Disable all interrupts to start */ - solo_irq_off(solo_dev, ~0); - - /* Initial global settings */ - if (solo_dev->type == SOLO_DEV_6010) { - solo_dev->clock_mhz = 108; - solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT - | SOLO_SYS_CFG_INPUTDIV(25) - | SOLO_SYS_CFG_FEEDBACKDIV(solo_dev->clock_mhz * 2 - 2) - | SOLO_SYS_CFG_OUTDIV(3); - solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); - } else { - u32 divq, divf; - - solo_dev->clock_mhz = 135; - - if (solo_dev->clock_mhz < 125) { - divq = 3; - divf = (solo_dev->clock_mhz * 4) / 3 - 1; - } else { - divq = 2; - divf = (solo_dev->clock_mhz * 2) / 3 - 1; - } - - solo_reg_write(solo_dev, SOLO_PLL_CONFIG, - (1 << 20) | /* PLL_RANGE */ - (8 << 15) | /* PLL_DIVR */ - (divq << 12) | - (divf << 4) | - (1 << 1) /* PLL_FSEN */); - - solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT; - } - - solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); - solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, - solo_dev->clock_mhz - 1); - - /* PLL locking time of 1ms */ - mdelay(1); - - ret = request_irq(pdev->irq, solo_isr, IRQF_SHARED, SOLO6X10_NAME, - solo_dev); - if (ret) - goto fail_probe; - - /* Handle this from the start */ - solo_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); - - ret = solo_i2c_init(solo_dev); - if (ret) - goto fail_probe; - - /* Setup the DMA engine */ - solo_reg_write(solo_dev, SOLO_DMA_CTRL, - SOLO_DMA_CTRL_REFRESH_CYCLE(1) | - SOLO_DMA_CTRL_SDRAM_SIZE(2) | - SOLO_DMA_CTRL_SDRAM_CLK_INVERT | - SOLO_DMA_CTRL_READ_CLK_SELECT | - SOLO_DMA_CTRL_LATENCY(1)); - - /* Undocumented crap */ - solo_reg_write(solo_dev, SOLO_DMA_CTRL1, - solo_dev->type == SOLO_DEV_6010 ? 0x100 : 0x300); - - if (solo_dev->type != SOLO_DEV_6010) { - solo_dev->usec_lsb = 0x3f; - solo_set_time(solo_dev); - } - - /* Disable watchdog */ - solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); - - /* Initialize sub components */ - - ret = solo_p2m_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_disp_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_gpio_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_tw28_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_v4l2_init(solo_dev, video_nr); - if (ret) - goto fail_probe; - - ret = solo_enc_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_enc_v4l2_init(solo_dev, video_nr); - if (ret) - goto fail_probe; - - ret = solo_g723_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_sysfs_init(solo_dev); - if (ret) - goto fail_probe; - - /* Now that init is over, set this lower */ - solo_dev->p2m_jiffies = msecs_to_jiffies(20); - - return 0; - -fail_probe: - free_solo_dev(solo_dev); - return ret; -} - -static void solo_pci_remove(struct pci_dev *pdev) -{ - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); - struct solo_dev *solo_dev = container_of(v4l2_dev, struct solo_dev, v4l2_dev); - - free_solo_dev(solo_dev); -} - -static const struct pci_device_id solo_id_table[] = { - /* 6010 based cards */ - { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9), - .driver_data = SOLO_DEV_6010 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16), - .driver_data = SOLO_DEV_6010 }, - /* 6110 based cards */ - { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), - .driver_data = SOLO_DEV_6110 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4), - .driver_data = SOLO_DEV_6110 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8), - .driver_data = SOLO_DEV_6110 }, - { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16), - .driver_data = SOLO_DEV_6110 }, - {0,} -}; - -MODULE_DEVICE_TABLE(pci, solo_id_table); - -static struct pci_driver solo_pci_driver = { - .name = SOLO6X10_NAME, - .id_table = solo_id_table, - .probe = solo_pci_probe, - .remove = solo_pci_remove, -}; - -module_pci_driver(solo_pci_driver); diff --git a/drivers/staging/media/solo6x10/solo6x10-disp.c b/drivers/staging/media/solo6x10/solo6x10-disp.c deleted file mode 100644 index ed88ab4..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-disp.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 -#include -#include -#include - -#include "solo6x10.h" - -#define SOLO_VCLK_DELAY 3 -#define SOLO_PROGRESSIVE_VSIZE 1024 - -#define SOLO_MOT_THRESH_W 64 -#define SOLO_MOT_THRESH_H 64 -#define SOLO_MOT_THRESH_SIZE 8192 -#define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) -#define SOLO_MOT_FLAG_SIZE 1024 -#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 16) - -static void solo_vin_config(struct solo_dev *solo_dev) -{ - solo_dev->vin_hstart = 8; - solo_dev->vin_vstart = 2; - - solo_reg_write(solo_dev, SOLO_SYS_VCLK, - SOLO_VCLK_SELECT(2) | - SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_I_P, - SOLO_VI_H_START(solo_dev->vin_hstart) | - SOLO_VI_V_START(solo_dev->vin_vstart) | - SOLO_VI_V_STOP(solo_dev->vin_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_I_S, - SOLO_VI_H_START(solo_dev->vout_hstart) | - SOLO_VI_V_START(solo_dev->vout_vstart) | - SOLO_VI_V_STOP(solo_dev->vout_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_P, - SOLO_VI_H_START(0) | - SOLO_VI_V_START(1) | - SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE)); - - solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, - SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); - - /* On 6110, initialize mozaic darkness stength */ - if (solo_dev->type == SOLO_DEV_6010) - solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0); - else - solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 16 << 22); - - solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2); - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, - SOLO_VI_PB_USER_MODE); - solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, - SOLO_VI_PB_HSIZE(858) | SOLO_VI_PB_VSIZE(246)); - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, - SOLO_VI_PB_VSTART(4) | - SOLO_VI_PB_VSTOP(4 + 240)); - } else { - solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, - SOLO_VI_PB_USER_MODE | SOLO_VI_PB_PAL); - solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, - SOLO_VI_PB_HSIZE(864) | SOLO_VI_PB_VSIZE(294)); - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, - SOLO_VI_PB_VSTART(4) | - SOLO_VI_PB_VSTOP(4 + 288)); - } - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_H, SOLO_VI_PB_HSTART(16) | - SOLO_VI_PB_HSTOP(16 + 720)); -} - -static void solo_vout_config_cursor(struct solo_dev *dev) -{ - int i; - - /* Load (blank) cursor bitmap mask (2bpp) */ - for (i = 0; i < 20; i++) - solo_reg_write(dev, SOLO_VO_CURSOR_MASK(i), 0); - - solo_reg_write(dev, SOLO_VO_CURSOR_POS, 0); - - solo_reg_write(dev, SOLO_VO_CURSOR_CLR, - (0x80 << 24) | (0x80 << 16) | (0x10 << 8) | 0x80); - solo_reg_write(dev, SOLO_VO_CURSOR_CLR2, (0xe0 << 8) | 0x80); -} - -static void solo_vout_config(struct solo_dev *solo_dev) -{ - solo_dev->vout_hstart = 6; - solo_dev->vout_vstart = 8; - - solo_reg_write(solo_dev, SOLO_VO_FMT_ENC, - solo_dev->video_type | - SOLO_VO_USER_COLOR_SET_NAV | - SOLO_VO_USER_COLOR_SET_NAH | - SOLO_VO_NA_COLOR_Y(0) | - SOLO_VO_NA_COLOR_CB(0) | - SOLO_VO_NA_COLOR_CR(0)); - - solo_reg_write(solo_dev, SOLO_VO_ACT_H, - SOLO_VO_H_START(solo_dev->vout_hstart) | - SOLO_VO_H_STOP(solo_dev->vout_hstart + - solo_dev->video_hsize)); - - solo_reg_write(solo_dev, SOLO_VO_ACT_V, - SOLO_VO_V_START(solo_dev->vout_vstart) | - SOLO_VO_V_STOP(solo_dev->vout_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VO_RANGE_HV, - SOLO_VO_H_LEN(solo_dev->video_hsize) | - SOLO_VO_V_LEN(solo_dev->video_vsize)); - - /* Border & background colors */ - solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, - (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); - solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, - (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); - solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, - (16 << 24) | (128 << 16) | (16 << 8) | 128); - - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); - - solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 0); - - solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); - solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); - - solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON | - SOLO_VO_DISP_ERASE_COUNT(8) | - SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); - - - solo_vout_config_cursor(solo_dev); - - /* Enable channels we support */ - solo_reg_write(solo_dev, SOLO_VI_CH_ENA, - (1 << solo_dev->nr_chans) - 1); -} - -static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, - u16 val, int reg_size) -{ - u16 *buf; - const int n = 64, size = n * sizeof(*buf); - int i, ret = 0; - - buf = kmalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (i = 0; i < n; i++) - buf[i] = cpu_to_le16(val); - - for (i = 0; i < reg_size; i += size) { - ret = solo_p2m_dma(solo_dev, 1, buf, - SOLO_MOTION_EXT_ADDR(solo_dev) + off + i, - size, 0, 0); - - if (ret) - break; - } - - kfree(buf); - return ret; -} - -int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) -{ - if (ch > solo_dev->nr_chans) - return -EINVAL; - - return solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + - (ch * SOLO_MOT_THRESH_SIZE * 2), - val, SOLO_MOT_THRESH_SIZE); -} - -int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, - const u16 *thresholds) -{ - const unsigned size = sizeof(u16) * 64; - u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2; - u16 *buf; - int x, y; - int ret = 0; - - buf = kzalloc(size, GFP_KERNEL); - for (y = 0; y < SOLO_MOTION_SZ; y++) { - for (x = 0; x < SOLO_MOTION_SZ; x++) - buf[x] = cpu_to_le16(thresholds[y * SOLO_MOTION_SZ + x]); - ret |= solo_p2m_dma(solo_dev, 1, buf, - SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * size, - size, 0, 0); - } - kfree(buf); - return ret; -} - -/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k - * threshold and working table for each channel. Atleast that's what the - * spec says. However, this code (taken from rdk) has some mystery 8k - * block right after the flag area, before the first thresh table. */ -static void solo_motion_config(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) { - /* Clear motion flag area */ - solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000, - SOLO_MOT_FLAG_SIZE); - - /* Clear working cache table */ - solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + - (i * SOLO_MOT_THRESH_SIZE * 2) + - SOLO_MOT_THRESH_SIZE, 0x0000, - SOLO_MOT_THRESH_SIZE); - - /* Set default threshold table */ - solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH); - } - - /* Default motion settings */ - solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) | - (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); - solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, - SOLO_VI_MOTION_FRAME_COUNT(3) | - SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) - /* | SOLO_VI_MOTION_INTR_START_STOP */ - | SOLO_VI_MOTION_SAMPLE_COUNT(10)); - - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); -} - -int solo_disp_init(struct solo_dev *solo_dev) -{ - int i; - - solo_dev->video_hsize = 704; - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - solo_dev->video_vsize = 240; - solo_dev->fps = 30; - } else { - solo_dev->video_vsize = 288; - solo_dev->fps = 25; - } - - solo_vin_config(solo_dev); - solo_motion_config(solo_dev); - solo_vout_config(solo_dev); - - for (i = 0; i < solo_dev->nr_chans; i++) - solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1); - - return 0; -} - -void solo_disp_exit(struct solo_dev *solo_dev) -{ - int i; - - solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0); - solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); - solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(i), 0); - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(i), 0); - solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 0); - } - - /* Set default border */ - for (i = 0; i < 5; i++) - solo_reg_write(solo_dev, SOLO_VO_BORDER_X(i), 0); - - for (i = 0; i < 5; i++) - solo_reg_write(solo_dev, SOLO_VO_BORDER_Y(i), 0); - - solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_MASK, 0); - solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_MASK, 0); - - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(0), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(0), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(0), 0); - - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(1), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(1), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(1), 0); -} diff --git a/drivers/staging/media/solo6x10/solo6x10-eeprom.c b/drivers/staging/media/solo6x10/solo6x10-eeprom.c deleted file mode 100644 index af40b3a..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-eeprom.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 -#include - -#include "solo6x10.h" - -/* Control */ -#define EE_SHIFT_CLK 0x04 -#define EE_CS 0x08 -#define EE_DATA_WRITE 0x02 -#define EE_DATA_READ 0x01 -#define EE_ENB (0x80 | EE_CS) - -#define eeprom_delay() udelay(100) -#if 0 -#define eeprom_delay() solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) -#define eeprom_delay() ({ \ - int i, ret; \ - udelay(100); \ - for (i = ret = 0; i < 1000 && !ret; i++) \ - ret = solo_eeprom_reg_read(solo_dev); \ -}) -#endif -#define ADDR_LEN 6 - -/* Commands */ -#define EE_EWEN_CMD 4 -#define EE_EWDS_CMD 4 -#define EE_WRITE_CMD 5 -#define EE_READ_CMD 6 -#define EE_ERASE_CMD 7 - -static unsigned int solo_eeprom_reg_read(struct solo_dev *solo_dev) -{ - return solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) & EE_DATA_READ; -} - -static void solo_eeprom_reg_write(struct solo_dev *solo_dev, u32 data) -{ - solo_reg_write(solo_dev, SOLO_EEPROM_CTRL, data); - eeprom_delay(); -} - -static void solo_eeprom_cmd(struct solo_dev *solo_dev, int cmd) -{ - int i; - - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ACCESS_EN); - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); - - for (i = 4 + ADDR_LEN; i >= 0; i--) { - int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0; - - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | dataval); - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | - EE_SHIFT_CLK | dataval); - } - - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); -} - -unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en) -{ - int ewen_cmd = (w_en ? 0x3f : 0) | (EE_EWEN_CMD << ADDR_LEN); - unsigned int retval = 0; - int i; - - solo_eeprom_cmd(solo_dev, ewen_cmd); - - for (i = 0; i < 16; i++) { - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | - EE_SHIFT_CLK); - retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); - retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); - } - - solo_eeprom_reg_write(solo_dev, ~EE_CS); - retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); - - return retval; -} - -unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc) -{ - int read_cmd = loc | (EE_READ_CMD << ADDR_LEN); - unsigned short retval = 0; - int i; - - solo_eeprom_cmd(solo_dev, read_cmd); - - for (i = 0; i < 16; i++) { - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | - EE_SHIFT_CLK); - retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); - solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); - } - - solo_eeprom_reg_write(solo_dev, ~EE_CS); - - return retval; -} - -int solo_eeprom_write(struct solo_dev *solo_dev, int loc, - unsigned short data) -{ - int write_cmd = loc | (EE_WRITE_CMD << ADDR_LEN); - unsigned int retval; - int i; - - solo_eeprom_cmd(solo_dev, write_cmd); - - for (i = 15; i >= 0; i--) { - unsigned int dataval = (data >> i) & 1; - - solo_eeprom_reg_write(solo_dev, EE_ENB); - solo_eeprom_reg_write(solo_dev, - EE_ENB | (dataval << 1) | EE_SHIFT_CLK); - } - - solo_eeprom_reg_write(solo_dev, EE_ENB); - solo_eeprom_reg_write(solo_dev, ~EE_CS); - solo_eeprom_reg_write(solo_dev, EE_ENB); - - for (i = retval = 0; i < 10000 && !retval; i++) - retval = solo_eeprom_reg_read(solo_dev); - - solo_eeprom_reg_write(solo_dev, ~EE_CS); - - return !retval; -} diff --git a/drivers/staging/media/solo6x10/solo6x10-enc.c b/drivers/staging/media/solo6x10/solo6x10-enc.c deleted file mode 100644 index d19c0ae..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-enc.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 -#include -#include -#include - -#include "solo6x10.h" - -#define VI_PROG_HSIZE (1280 - 16) -#define VI_PROG_VSIZE (1024 - 16) - -#define IRQ_LEVEL 2 - -static void solo_capture_config(struct solo_dev *solo_dev) -{ - unsigned long height; - unsigned long width; - void *buf; - int i; - - solo_reg_write(solo_dev, SOLO_CAP_BASE, - SOLO_CAP_MAX_PAGE((SOLO_CAP_EXT_SIZE(solo_dev) - - SOLO_CAP_PAGE_SIZE) >> 16) - | SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); - - /* XXX: Undocumented bits at b17 and b24 */ - if (solo_dev->type == SOLO_DEV_6110) { - /* NOTE: Ref driver has (62 << 24) here as well, but it causes - * wacked out frame timing on 4-port 6110. */ - solo_reg_write(solo_dev, SOLO_CAP_BTW, - (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | - SOLO_CAP_MAX_BANDWIDTH(36)); - } else { - solo_reg_write(solo_dev, SOLO_CAP_BTW, - (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | - SOLO_CAP_MAX_BANDWIDTH(32)); - } - - /* Set scale 1, 9 dimension */ - width = solo_dev->video_hsize; - height = solo_dev->video_vsize; - solo_reg_write(solo_dev, SOLO_DIM_SCALE1, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 2, 10 dimension */ - width = solo_dev->video_hsize / 2; - height = solo_dev->video_vsize; - solo_reg_write(solo_dev, SOLO_DIM_SCALE2, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 3, 11 dimension */ - width = solo_dev->video_hsize / 2; - height = solo_dev->video_vsize / 2; - solo_reg_write(solo_dev, SOLO_DIM_SCALE3, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 4, 12 dimension */ - width = solo_dev->video_hsize / 3; - height = solo_dev->video_vsize / 3; - solo_reg_write(solo_dev, SOLO_DIM_SCALE4, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 5, 13 dimension */ - width = solo_dev->video_hsize / 4; - height = solo_dev->video_vsize / 2; - solo_reg_write(solo_dev, SOLO_DIM_SCALE5, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Progressive */ - width = VI_PROG_HSIZE; - height = VI_PROG_VSIZE; - solo_reg_write(solo_dev, SOLO_DIM_PROG, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 16) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Clear OSD */ - solo_reg_write(solo_dev, SOLO_VE_OSD_CH, 0); - solo_reg_write(solo_dev, SOLO_VE_OSD_BASE, SOLO_EOSD_EXT_ADDR >> 16); - solo_reg_write(solo_dev, SOLO_VE_OSD_CLR, - 0xF0 << 16 | 0x80 << 8 | 0x80); - - if (solo_dev->type == SOLO_DEV_6010) - solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, - SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); - else - solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, SOLO_VE_OSD_V_DOUBLE - | SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); - - /* Clear OSG buffer */ - buf = kzalloc(SOLO_EOSD_EXT_SIZE(solo_dev), GFP_KERNEL); - if (!buf) - return; - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_p2m_dma(solo_dev, 1, buf, - SOLO_EOSD_EXT_ADDR + - (SOLO_EOSD_EXT_SIZE(solo_dev) * i), - SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0); - } - kfree(buf); -} - -#define SOLO_OSD_WRITE_SIZE (16 * OSD_TEXT_MAX) - -/* Should be called with enable_lock held */ -int solo_osd_print(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - unsigned char *str = solo_enc->osd_text; - u8 *buf = solo_enc->osd_buf; - u32 reg; - const struct font_desc *vga = find_font("VGA8x16"); - const unsigned char *vga_data; - int i, j; - - if (WARN_ON_ONCE(!vga)) - return -ENODEV; - - reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH); - if (!*str) { - /* Disable OSD on this channel */ - reg &= ~(1 << solo_enc->ch); - goto out; - } - - memset(buf, 0, SOLO_OSD_WRITE_SIZE); - vga_data = (const unsigned char *)vga->data; - - for (i = 0; *str; i++, str++) { - for (j = 0; j < 16; j++) { - buf[(j << 1) | (i & 1) | ((i & ~1) << 4)] = - bitrev8(vga_data[(*str << 4) | j]); - } - } - - solo_p2m_dma(solo_dev, 1, buf, - SOLO_EOSD_EXT_ADDR_CHAN(solo_dev, solo_enc->ch), - SOLO_OSD_WRITE_SIZE, 0, 0); - - /* Enable OSD on this channel */ - reg |= (1 << solo_enc->ch); - -out: - solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); - return 0; -} - -/** - * Set channel Quality Profile (0-3). - */ -void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch, - unsigned int qp) -{ - unsigned long flags; - unsigned int idx, reg; - - if ((ch > 31) || (qp > 3)) - return; - - if (solo_dev->type == SOLO_DEV_6010) - return; - - if (ch < 16) { - idx = 0; - reg = SOLO_VE_JPEG_QP_CH_L; - } else { - ch -= 16; - idx = 1; - reg = SOLO_VE_JPEG_QP_CH_H; - } - ch *= 2; - - spin_lock_irqsave(&solo_dev->jpeg_qp_lock, flags); - - solo_dev->jpeg_qp[idx] &= ~(3 << ch); - solo_dev->jpeg_qp[idx] |= (qp & 3) << ch; - - solo_reg_write(solo_dev, reg, solo_dev->jpeg_qp[idx]); - - spin_unlock_irqrestore(&solo_dev->jpeg_qp_lock, flags); -} - -int solo_g_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch) -{ - int idx; - - if (solo_dev->type == SOLO_DEV_6010) - return 2; - - if (WARN_ON_ONCE(ch > 31)) - return 2; - - if (ch < 16) { - idx = 0; - } else { - ch -= 16; - idx = 1; - } - ch *= 2; - - return (solo_dev->jpeg_qp[idx] >> ch) & 3; -} - -#define SOLO_QP_INIT 0xaaaaaaaa - -static void solo_jpeg_config(struct solo_dev *solo_dev) -{ - if (solo_dev->type == SOLO_DEV_6010) { - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, - (2 << 24) | (2 << 16) | (2 << 8) | 2); - } else { - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, - (4 << 24) | (3 << 16) | (2 << 8) | 1); - } - - spin_lock_init(&solo_dev->jpeg_qp_lock); - - /* Initialize Quality Profile for all channels */ - solo_dev->jpeg_qp[0] = solo_dev->jpeg_qp[1] = SOLO_QP_INIT; - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, SOLO_QP_INIT); - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, SOLO_QP_INIT); - - solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG, - (SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) | - ((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff)); - solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff); - if (solo_dev->type == SOLO_DEV_6110) { - solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG1, - (0 << 16) | (30 << 8) | 60); - } -} - -static void solo_mp4e_config(struct solo_dev *solo_dev) -{ - int i; - u32 cfg; - - solo_reg_write(solo_dev, SOLO_VE_CFG0, - SOLO_VE_INTR_CTRL(IRQ_LEVEL) | - SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) | - SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16)); - - - cfg = SOLO_VE_BYTE_ALIGN(2) | SOLO_VE_INSERT_INDEX - | SOLO_VE_MOTION_MODE(0); - if (solo_dev->type != SOLO_DEV_6010) { - cfg |= SOLO_VE_MPEG_SIZE_H( - (SOLO_MP4E_EXT_SIZE(solo_dev) >> 24) & 0x0f); - cfg |= SOLO_VE_JPEG_SIZE_H( - (SOLO_JPEG_EXT_SIZE(solo_dev) >> 24) & 0x0f); - } - solo_reg_write(solo_dev, SOLO_VE_CFG1, cfg); - - solo_reg_write(solo_dev, SOLO_VE_WMRK_POLY, 0); - solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0); - solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, 0); - if (solo_dev->type == SOLO_DEV_6110) - solo_reg_write(solo_dev, SOLO_VE_WMRK_ENABLE, 0); - solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0); - solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0); - - solo_reg_write(solo_dev, SOLO_VE_ATTR, - SOLO_VE_LITTLE_ENDIAN | - SOLO_COMP_ATTR_FCODE(1) | - SOLO_COMP_TIME_INC(0) | - SOLO_COMP_TIME_WIDTH(15) | - SOLO_DCT_INTERVAL(solo_dev->type == SOLO_DEV_6010 ? 9 : 10)); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i), - (SOLO_EREF_EXT_ADDR(solo_dev) + - (i * SOLO_EREF_EXT_SIZE)) >> 16); - solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE_E(i), - (SOLO_EREF_EXT_ADDR(solo_dev) + - ((i + 16) * SOLO_EREF_EXT_SIZE)) >> 16); - } - - if (solo_dev->type == SOLO_DEV_6110) { - solo_reg_write(solo_dev, SOLO_VE_COMPT_MOT, 0x00040008); - } else { - for (i = 0; i < solo_dev->nr_chans; i++) - solo_reg_write(solo_dev, SOLO_VE_CH_MOT(i), 0x100); - } -} - -int solo_enc_init(struct solo_dev *solo_dev) -{ - int i; - - solo_capture_config(solo_dev); - solo_mp4e_config(solo_dev); - solo_jpeg_config(solo_dev); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); - } - - return 0; -} - -void solo_enc_exit(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); - } -} diff --git a/drivers/staging/media/solo6x10/solo6x10-g723.c b/drivers/staging/media/solo6x10/solo6x10-g723.c deleted file mode 100644 index c7141f2..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-g723.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "solo6x10.h" -#include "solo6x10-tw28.h" - -#define G723_FDMA_PAGES 32 -#define G723_PERIOD_BYTES 48 -#define G723_PERIOD_BLOCK 1024 -#define G723_FRAMES_PER_PAGE 48 - -/* Sets up channels 16-19 for decoding and 0-15 for encoding */ -#define OUTMODE_MASK 0x300 - -#define SAMPLERATE 8000 -#define BITRATE 25 - -/* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page - * is broken down to 20 * 48 byte regions (one for each channel possible) - * with the rest of the page being dummy data. */ -#define G723_MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) -#define G723_INTR_ORDER 4 /* 0 - 4 */ -#define PERIODS_MIN (1 << G723_INTR_ORDER) -#define PERIODS_MAX G723_FDMA_PAGES - -struct solo_snd_pcm { - int on; - spinlock_t lock; - struct solo_dev *solo_dev; - unsigned char *g723_buf; - dma_addr_t g723_dma; -}; - -static void solo_g723_config(struct solo_dev *solo_dev) -{ - int clk_div; - - clk_div = (solo_dev->clock_mhz * 1000000) - / (SAMPLERATE * (BITRATE * 2) * 2); - - solo_reg_write(solo_dev, SOLO_AUDIO_SAMPLE, - SOLO_AUDIO_BITRATE(BITRATE) - | SOLO_AUDIO_CLK_DIV(clk_div)); - - solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR, - SOLO_AUDIO_FDMA_INTERVAL(1) - | SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) - | SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); - - solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, - SOLO_AUDIO_ENABLE - | SOLO_AUDIO_I2S_MODE - | SOLO_AUDIO_I2S_MULTI(3) - | SOLO_AUDIO_MODE(OUTMODE_MASK)); -} - -void solo_g723_isr(struct solo_dev *solo_dev) -{ - struct snd_pcm_str *pstr = - &solo_dev->snd_pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; - struct snd_pcm_substream *ss; - struct solo_snd_pcm *solo_pcm; - - for (ss = pstr->substream; ss != NULL; ss = ss->next) { - if (snd_pcm_substream_chip(ss) == NULL) - continue; - - /* This means open() hasn't been called on this one */ - if (snd_pcm_substream_chip(ss) == solo_dev) - continue; - - /* Haven't triggered a start yet */ - solo_pcm = snd_pcm_substream_chip(ss); - if (!solo_pcm->on) - continue; - - snd_pcm_period_elapsed(ss); - } -} - -static int snd_solo_hw_params(struct snd_pcm_substream *ss, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); -} - -static int snd_solo_hw_free(struct snd_pcm_substream *ss) -{ - return snd_pcm_lib_free_pages(ss); -} - -static const struct snd_pcm_hardware snd_solo_pcm_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_U8, - .rates = SNDRV_PCM_RATE_8000, - .rate_min = SAMPLERATE, - .rate_max = SAMPLERATE, - .channels_min = 1, - .channels_max = 1, - .buffer_bytes_max = G723_MAX_BUFFER, - .period_bytes_min = G723_PERIOD_BYTES, - .period_bytes_max = G723_PERIOD_BYTES, - .periods_min = PERIODS_MIN, - .periods_max = PERIODS_MAX, -}; - -static int snd_solo_pcm_open(struct snd_pcm_substream *ss) -{ - struct solo_dev *solo_dev = snd_pcm_substream_chip(ss); - struct solo_snd_pcm *solo_pcm; - - solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL); - if (solo_pcm == NULL) - goto oom; - - solo_pcm->g723_buf = pci_alloc_consistent(solo_dev->pdev, - G723_PERIOD_BYTES, - &solo_pcm->g723_dma); - if (solo_pcm->g723_buf == NULL) - goto oom; - - spin_lock_init(&solo_pcm->lock); - solo_pcm->solo_dev = solo_dev; - ss->runtime->hw = snd_solo_pcm_hw; - - snd_pcm_substream_chip(ss) = solo_pcm; - - return 0; - -oom: - kfree(solo_pcm); - return -ENOMEM; -} - -static int snd_solo_pcm_close(struct snd_pcm_substream *ss) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - - snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; - pci_free_consistent(solo_pcm->solo_dev->pdev, G723_PERIOD_BYTES, - solo_pcm->g723_buf, solo_pcm->g723_dma); - kfree(solo_pcm); - - return 0; -} - -static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo_dev *solo_dev = solo_pcm->solo_dev; - int ret = 0; - - spin_lock(&solo_pcm->lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (solo_pcm->on == 0) { - /* If this is the first user, switch on interrupts */ - if (atomic_inc_return(&solo_dev->snd_users) == 1) - solo_irq_on(solo_dev, SOLO_IRQ_G723); - solo_pcm->on = 1; - } - break; - case SNDRV_PCM_TRIGGER_STOP: - if (solo_pcm->on) { - /* If this was our last user, switch them off */ - if (atomic_dec_return(&solo_dev->snd_users) == 0) - solo_irq_off(solo_dev, SOLO_IRQ_G723); - solo_pcm->on = 0; - } - break; - default: - ret = -EINVAL; - } - - spin_unlock(&solo_pcm->lock); - - return ret; -} - -static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss) -{ - return 0; -} - -static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo_dev *solo_dev = solo_pcm->solo_dev; - snd_pcm_uframes_t idx = solo_reg_read(solo_dev, SOLO_AUDIO_STA) & 0x1f; - - return idx * G723_FRAMES_PER_PAGE; -} - -static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, - snd_pcm_uframes_t pos, void __user *dst, - snd_pcm_uframes_t count) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo_dev *solo_dev = solo_pcm->solo_dev; - int err, i; - - for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { - int page = (pos / G723_FRAMES_PER_PAGE) + i; - - err = solo_p2m_dma_t(solo_dev, 0, solo_pcm->g723_dma, - SOLO_G723_EXT_ADDR(solo_dev) + - (page * G723_PERIOD_BLOCK) + - (ss->number * G723_PERIOD_BYTES), - G723_PERIOD_BYTES, 0, 0); - if (err) - return err; - - err = copy_to_user(dst + (i * G723_PERIOD_BYTES), - solo_pcm->g723_buf, G723_PERIOD_BYTES); - - if (err) - return -EFAULT; - } - - return 0; -} - -static struct snd_pcm_ops snd_solo_pcm_ops = { - .open = snd_solo_pcm_open, - .close = snd_solo_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_solo_hw_params, - .hw_free = snd_solo_hw_free, - .prepare = snd_solo_pcm_prepare, - .trigger = snd_solo_pcm_trigger, - .pointer = snd_solo_pcm_pointer, - .copy = snd_solo_pcm_copy, -}; - -static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *info) -{ - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = 1; - info->value.integer.min = 0; - info->value.integer.max = 15; - info->value.integer.step = 1; - - return 0; -} - -static int snd_solo_capture_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); - u8 ch = value->id.numid - 1; - - value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch); - - return 0; -} - -static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); - u8 ch = value->id.numid - 1; - u8 old_val; - - old_val = tw28_get_audio_gain(solo_dev, ch); - if (old_val == value->value.integer.value[0]) - return 0; - - tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]); - - return 1; -} - -static struct snd_kcontrol_new snd_solo_capture_volume = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Volume", - .info = snd_solo_capture_volume_info, - .get = snd_solo_capture_volume_get, - .put = snd_solo_capture_volume_put, -}; - -static int solo_snd_pcm_init(struct solo_dev *solo_dev) -{ - struct snd_card *card = solo_dev->snd_card; - struct snd_pcm *pcm; - struct snd_pcm_substream *ss; - int ret; - int i; - - ret = snd_pcm_new(card, card->driver, 0, 0, solo_dev->nr_chans, - &pcm); - if (ret < 0) - return ret; - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_solo_pcm_ops); - - snd_pcm_chip(pcm) = solo_dev; - pcm->info_flags = 0; - strcpy(pcm->name, card->shortname); - - for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - ss; ss = ss->next, i++) - sprintf(ss->name, "Camera #%d Audio", i); - - ret = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - G723_MAX_BUFFER, G723_MAX_BUFFER); - if (ret < 0) - return ret; - - solo_dev->snd_pcm = pcm; - - return 0; -} - -int solo_g723_init(struct solo_dev *solo_dev) -{ - static struct snd_device_ops ops = { NULL }; - struct snd_card *card; - struct snd_kcontrol_new kctl; - char name[32]; - int ret; - - atomic_set(&solo_dev->snd_users, 0); - - /* Allows for easier mapping between video and audio */ - sprintf(name, "Softlogic%d", solo_dev->vfd->num); - - ret = snd_card_new(&solo_dev->pdev->dev, - SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0, - &solo_dev->snd_card); - if (ret < 0) - return ret; - - card = solo_dev->snd_card; - - strcpy(card->driver, SOLO6X10_NAME); - strcpy(card->shortname, "SOLO-6x10 Audio"); - sprintf(card->longname, "%s on %s IRQ %d", card->shortname, - pci_name(solo_dev->pdev), solo_dev->pdev->irq); - - ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops); - if (ret < 0) - goto snd_error; - - /* Mixer controls */ - strcpy(card->mixername, "SOLO-6x10"); - kctl = snd_solo_capture_volume; - kctl.count = solo_dev->nr_chans; - - ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); - if (ret < 0) - return ret; - - ret = solo_snd_pcm_init(solo_dev); - if (ret < 0) - goto snd_error; - - ret = snd_card_register(card); - if (ret < 0) - goto snd_error; - - solo_g723_config(solo_dev); - - dev_info(&solo_dev->pdev->dev, "Alsa sound card as %s\n", name); - - return 0; - -snd_error: - snd_card_free(card); - return ret; -} - -void solo_g723_exit(struct solo_dev *solo_dev) -{ - if (!solo_dev->snd_card) - return; - - solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0); - solo_irq_off(solo_dev, SOLO_IRQ_G723); - - snd_card_free(solo_dev->snd_card); - solo_dev->snd_card = NULL; -} diff --git a/drivers/staging/media/solo6x10/solo6x10-gpio.c b/drivers/staging/media/solo6x10/solo6x10-gpio.c deleted file mode 100644 index 6d3b4a3..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-gpio.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 -#include -#include -#include - -#include "solo6x10.h" - -static void solo_gpio_mode(struct solo_dev *solo_dev, - unsigned int port_mask, unsigned int mode) -{ - int port; - unsigned int ret; - - ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0); - - /* To set gpio */ - for (port = 0; port < 16; port++) { - if (!((1 << port) & port_mask)) - continue; - - ret &= (~(3 << (port << 1))); - ret |= ((mode & 3) << (port << 1)); - } - - solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_0, ret); - - /* To set extended gpio - sensor */ - ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1); - - for (port = 0; port < 16; port++) { - if (!((1 << (port + 16)) & port_mask)) - continue; - - if (!mode) - ret &= ~(1 << port); - else - ret |= 1 << port; - } - - solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_1, ret); -} - -static void solo_gpio_set(struct solo_dev *solo_dev, unsigned int value) -{ - solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, - solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) | value); -} - -static void solo_gpio_clear(struct solo_dev *solo_dev, unsigned int value) -{ - solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, - solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) & ~value); -} - -static void solo_gpio_config(struct solo_dev *solo_dev) -{ - /* Video reset */ - solo_gpio_mode(solo_dev, 0x30, 1); - solo_gpio_clear(solo_dev, 0x30); - udelay(100); - solo_gpio_set(solo_dev, 0x30); - udelay(100); - - /* Warning: Don't touch the next line unless you're sure of what - * you're doing: first four gpio [0-3] are used for video. */ - solo_gpio_mode(solo_dev, 0x0f, 2); - - /* We use bit 8-15 of SOLO_GPIO_CONFIG_0 for relay purposes */ - solo_gpio_mode(solo_dev, 0xff00, 1); - - /* Initially set relay status to 0 */ - solo_gpio_clear(solo_dev, 0xff00); -} - -int solo_gpio_init(struct solo_dev *solo_dev) -{ - solo_gpio_config(solo_dev); - return 0; -} - -void solo_gpio_exit(struct solo_dev *solo_dev) -{ - solo_gpio_clear(solo_dev, 0x30); - solo_gpio_config(solo_dev); -} diff --git a/drivers/staging/media/solo6x10/solo6x10-i2c.c b/drivers/staging/media/solo6x10/solo6x10-i2c.c deleted file mode 100644 index c908672..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-i2c.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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. - */ - -/* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c - * channel. The bus can only handle one i2c event at a time. The below handles - * this all wrong. We should be using the status registers to see if the bus - * is in use, and have a global lock to check the status register. Also, - * the bulk of the work should be handled out-of-interrupt. The ugly loops - * that occur during interrupt scare me. The ISR should merely signal - * thread context, ACK the interrupt, and move on. -- BenC */ - -#include - -#include "solo6x10.h" - -u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) -{ - struct i2c_msg msgs[2]; - u8 data; - - msgs[0].flags = 0; - msgs[0].addr = addr; - msgs[0].len = 1; - msgs[0].buf = &off; - - msgs[1].flags = I2C_M_RD; - msgs[1].addr = addr; - msgs[1].len = 1; - msgs[1].buf = &data; - - i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2); - - return data; -} - -void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, - u8 off, u8 data) -{ - struct i2c_msg msgs; - u8 buf[2]; - - buf[0] = off; - buf[1] = data; - msgs.flags = 0; - msgs.addr = addr; - msgs.len = 2; - msgs.buf = buf; - - i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1); -} - -static void solo_i2c_flush(struct solo_dev *solo_dev, int wr) -{ - u32 ctrl; - - ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); - - if (solo_dev->i2c_state == IIC_STATE_START) - ctrl |= SOLO_IIC_START; - - if (wr) { - ctrl |= SOLO_IIC_WRITE; - } else { - ctrl |= SOLO_IIC_READ; - if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) - ctrl |= SOLO_IIC_ACK_EN; - } - - if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) - ctrl |= SOLO_IIC_STOP; - - solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl); -} - -static void solo_i2c_start(struct solo_dev *solo_dev) -{ - u32 addr = solo_dev->i2c_msg->addr << 1; - - if (solo_dev->i2c_msg->flags & I2C_M_RD) - addr |= 1; - - solo_dev->i2c_state = IIC_STATE_START; - solo_reg_write(solo_dev, SOLO_IIC_TXD, addr); - solo_i2c_flush(solo_dev, 1); -} - -static void solo_i2c_stop(struct solo_dev *solo_dev) -{ - solo_irq_off(solo_dev, SOLO_IRQ_IIC); - solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); - solo_dev->i2c_state = IIC_STATE_STOP; - wake_up(&solo_dev->i2c_wait); -} - -static int solo_i2c_handle_read(struct solo_dev *solo_dev) -{ -prepare_read: - if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { - solo_i2c_flush(solo_dev, 0); - return 0; - } - - solo_dev->i2c_msg_ptr = 0; - solo_dev->i2c_msg++; - solo_dev->i2c_msg_num--; - - if (solo_dev->i2c_msg_num == 0) { - solo_i2c_stop(solo_dev); - return 0; - } - - if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { - solo_i2c_start(solo_dev); - } else { - if (solo_dev->i2c_msg->flags & I2C_M_RD) - goto prepare_read; - else - solo_i2c_stop(solo_dev); - } - - return 0; -} - -static int solo_i2c_handle_write(struct solo_dev *solo_dev) -{ -retry_write: - if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { - solo_reg_write(solo_dev, SOLO_IIC_TXD, - solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); - solo_dev->i2c_msg_ptr++; - solo_i2c_flush(solo_dev, 1); - return 0; - } - - solo_dev->i2c_msg_ptr = 0; - solo_dev->i2c_msg++; - solo_dev->i2c_msg_num--; - - if (solo_dev->i2c_msg_num == 0) { - solo_i2c_stop(solo_dev); - return 0; - } - - if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { - solo_i2c_start(solo_dev); - } else { - if (solo_dev->i2c_msg->flags & I2C_M_RD) - solo_i2c_stop(solo_dev); - else - goto retry_write; - } - - return 0; -} - -int solo_i2c_isr(struct solo_dev *solo_dev) -{ - u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); - int ret = -EINVAL; - - - if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) - || solo_dev->i2c_id < 0) { - solo_i2c_stop(solo_dev); - return -ENXIO; - } - - switch (solo_dev->i2c_state) { - case IIC_STATE_START: - if (solo_dev->i2c_msg->flags & I2C_M_RD) { - solo_dev->i2c_state = IIC_STATE_READ; - ret = solo_i2c_handle_read(solo_dev); - break; - } - - solo_dev->i2c_state = IIC_STATE_WRITE; - case IIC_STATE_WRITE: - ret = solo_i2c_handle_write(solo_dev); - break; - - case IIC_STATE_READ: - solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = - solo_reg_read(solo_dev, SOLO_IIC_RXD); - solo_dev->i2c_msg_ptr++; - - ret = solo_i2c_handle_read(solo_dev); - break; - - default: - solo_i2c_stop(solo_dev); - } - - return ret; -} - -static int solo_i2c_master_xfer(struct i2c_adapter *adap, - struct i2c_msg msgs[], int num) -{ - struct solo_dev *solo_dev = adap->algo_data; - unsigned long timeout; - int ret; - int i; - DEFINE_WAIT(wait); - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (&solo_dev->i2c_adap[i] == adap) - break; - } - - if (i == SOLO_I2C_ADAPTERS) - return num; /* XXX Right return value for failure? */ - - mutex_lock(&solo_dev->i2c_mutex); - solo_dev->i2c_id = i; - solo_dev->i2c_msg = msgs; - solo_dev->i2c_msg_num = num; - solo_dev->i2c_msg_ptr = 0; - - solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); - solo_irq_on(solo_dev, SOLO_IRQ_IIC); - solo_i2c_start(solo_dev); - - timeout = HZ / 2; - - for (;;) { - prepare_to_wait(&solo_dev->i2c_wait, &wait, - TASK_INTERRUPTIBLE); - - if (solo_dev->i2c_state == IIC_STATE_STOP) - break; - - timeout = schedule_timeout(timeout); - if (!timeout) - break; - - if (signal_pending(current)) - break; - } - - finish_wait(&solo_dev->i2c_wait, &wait); - ret = num - solo_dev->i2c_msg_num; - solo_dev->i2c_state = IIC_STATE_IDLE; - solo_dev->i2c_id = -1; - - mutex_unlock(&solo_dev->i2c_mutex); - - return ret; -} - -static u32 solo_i2c_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C; -} - -static const struct i2c_algorithm solo_i2c_algo = { - .master_xfer = solo_i2c_master_xfer, - .functionality = solo_i2c_functionality, -}; - -int solo_i2c_init(struct solo_dev *solo_dev) -{ - int i; - int ret; - - solo_reg_write(solo_dev, SOLO_IIC_CFG, - SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); - - solo_dev->i2c_id = -1; - solo_dev->i2c_state = IIC_STATE_IDLE; - init_waitqueue_head(&solo_dev->i2c_wait); - mutex_init(&solo_dev->i2c_mutex); - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; - - snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", - SOLO6X10_NAME, i); - adap->algo = &solo_i2c_algo; - adap->algo_data = solo_dev; - adap->retries = 1; - adap->dev.parent = &solo_dev->pdev->dev; - - ret = i2c_add_adapter(adap); - if (ret) { - adap->algo_data = NULL; - break; - } - } - - if (ret) { - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (!solo_dev->i2c_adap[i].algo_data) - break; - i2c_del_adapter(&solo_dev->i2c_adap[i]); - solo_dev->i2c_adap[i].algo_data = NULL; - } - return ret; - } - - return 0; -} - -void solo_i2c_exit(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (!solo_dev->i2c_adap[i].algo_data) - continue; - i2c_del_adapter(&solo_dev->i2c_adap[i]); - solo_dev->i2c_adap[i].algo_data = NULL; - } -} diff --git a/drivers/staging/media/solo6x10/solo6x10-jpeg.h b/drivers/staging/media/solo6x10/solo6x10-jpeg.h deleted file mode 100644 index 1c66a46..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-jpeg.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 __SOLO6X10_JPEG_H -#define __SOLO6X10_JPEG_H - -static const unsigned char jpeg_header[] = { - 0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c, - 0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79, - 0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16, - 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c, - 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, - 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a, 0x3a, 0x50, - 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, 0x70, 0x6e, - 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, - 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, - 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2, 0xf2, 0xe0, - 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0xff, 0xdb, - 0x00, 0x43, 0x01, 0x22, 0x24, 0x24, 0x30, 0x2a, - 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6, 0x84, 0x70, - 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xff, 0xc4, 0x01, 0xa2, 0x00, - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, - 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, - 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, - 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, - 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, - 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, - 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, - 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, - 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, - 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, - 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, - 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, - 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, - 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, - 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, - 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, - 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, - 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, - 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, - 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, - 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, - 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, - 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, - 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, - 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, - 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, - 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, - 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, - 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, - 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, - 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, - 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, - 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, - 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, - 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, - 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, - 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, - 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, - 0xc0, 0x00, 0x11, 0x08, 0x00, 0xf0, 0x02, 0xc0, - 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, - 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, - 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 -}; - -/* This is the byte marker for the start of SOF0: 0xffc0 marker */ -#define SOF0_START 575 - -/* This is the byte marker for the start of the DQT */ -#define DQT_START 17 -#define DQT_LEN 138 -static const unsigned char jpeg_dqt[4][DQT_LEN] = { - { - 0xff, 0xdb, 0x00, 0x43, 0x00, - 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, - 0x07, 0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, - 0x0d, 0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13, - 0x0f, 0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, - 0x1c, 0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22, - 0x2c, 0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, - 0x30, 0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39, - 0x3d, 0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, - 0xff, 0xdb, 0x00, 0x43, 0x01, - 0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0d, - 0x0d, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32 - }, { - 0xff, 0xdb, 0x00, 0x43, 0x00, - 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, - 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, - 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, - 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, - 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, - 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, - 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, - 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, - 0xff, 0xdb, 0x00, 0x43, 0x01, - 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, - 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 - }, { - 0xff, 0xdb, 0x00, 0x43, 0x00, - 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, - 0x1a, 0x1c, 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, - 0x34, 0x30, 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a, - 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, - 0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, - 0xae, 0x8a, 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae, - 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2, - 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, - 0xff, 0xdb, 0x00, 0x43, 0x01, - 0x22, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, - 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6 - }, { - 0xff, 0xdb, 0x00, 0x43, 0x00, - 0x30, 0x21, 0x24, 0x2a, 0x24, 0x1e, 0x30, 0x2a, - 0x27, 0x2a, 0x36, 0x33, 0x30, 0x39, 0x48, 0x78, - 0x4e, 0x48, 0x42, 0x42, 0x48, 0x93, 0x69, 0x6f, - 0x57, 0x78, 0xae, 0x99, 0xb7, 0xb4, 0xab, 0x99, - 0xa8, 0xa5, 0xc0, 0xd8, 0xff, 0xea, 0xc0, 0xcc, - 0xff, 0xcf, 0xa5, 0xa8, 0xf0, 0xff, 0xf3, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xba, 0xe7, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xdb, 0x00, 0x43, 0x01, - 0x33, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e, - 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff - } -}; - -#endif /* __SOLO6X10_JPEG_H */ diff --git a/drivers/staging/media/solo6x10/solo6x10-offsets.h b/drivers/staging/media/solo6x10/solo6x10-offsets.h deleted file mode 100644 index d6aea7c..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-offsets.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 __SOLO6X10_OFFSETS_H -#define __SOLO6X10_OFFSETS_H - -#define SOLO_DISP_EXT_ADDR 0x00000000 -#define SOLO_DISP_EXT_SIZE 0x00480000 - -#define SOLO_EOSD_EXT_ADDR \ - (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE) -#define SOLO_EOSD_EXT_SIZE(__solo) \ - (__solo->type == SOLO_DEV_6010 ? 0x10000 : 0x20000) -#define SOLO_EOSD_EXT_SIZE_MAX 0x20000 -#define SOLO_EOSD_EXT_AREA(__solo) \ - (SOLO_EOSD_EXT_SIZE(__solo) * 32) -#define SOLO_EOSD_EXT_ADDR_CHAN(__solo, ch) \ - (SOLO_EOSD_EXT_ADDR + SOLO_EOSD_EXT_SIZE(__solo) * (ch)) - -#define SOLO_MOTION_EXT_ADDR(__solo) \ - (SOLO_EOSD_EXT_ADDR + SOLO_EOSD_EXT_AREA(__solo)) -#define SOLO_MOTION_EXT_SIZE 0x00080000 - -#define SOLO_G723_EXT_ADDR(__solo) \ - (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE) -#define SOLO_G723_EXT_SIZE 0x00010000 - -#define SOLO_CAP_EXT_ADDR(__solo) \ - (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE) - -/* 18 is the maximum number of pages required for PAL@D1, the largest frame - * possible */ -#define SOLO_CAP_PAGE_SIZE (18 << 16) - -/* Always allow the encoder enough for 16 channels, even if we have less. The - * exception is if we have card with only 32Megs of memory. */ -#define SOLO_CAP_EXT_SIZE(__solo) \ - ((((__solo->sdram_size <= (32 << 20)) ? 4 : 16) + 1) \ - * SOLO_CAP_PAGE_SIZE) - -#define SOLO_EREF_EXT_ADDR(__solo) \ - (SOLO_CAP_EXT_ADDR(__solo) + SOLO_CAP_EXT_SIZE(__solo)) -#define SOLO_EREF_EXT_SIZE 0x00140000 -#define SOLO_EREF_EXT_AREA(__solo) \ - (SOLO_EREF_EXT_SIZE * __solo->nr_chans * 2) - -#define __SOLO_JPEG_MIN_SIZE(__solo) (__solo->nr_chans * 0x00080000) - -#define SOLO_MP4E_EXT_ADDR(__solo) \ - (SOLO_EREF_EXT_ADDR(__solo) + SOLO_EREF_EXT_AREA(__solo)) -#define SOLO_MP4E_EXT_SIZE(__solo) \ - max((__solo->nr_chans * 0x00080000), \ - min(((__solo->sdram_size - SOLO_MP4E_EXT_ADDR(__solo)) - \ - __SOLO_JPEG_MIN_SIZE(__solo)), 0x00ff0000)) - -#define __SOLO_JPEG_MIN_SIZE(__solo) (__solo->nr_chans * 0x00080000) -#define SOLO_JPEG_EXT_ADDR(__solo) \ - (SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo)) -#define SOLO_JPEG_EXT_SIZE(__solo) \ - max(__SOLO_JPEG_MIN_SIZE(__solo), \ - min((__solo->sdram_size - SOLO_JPEG_EXT_ADDR(__solo)), 0x00ff0000)) - -#define SOLO_SDRAM_END(__solo) \ - (SOLO_JPEG_EXT_ADDR(__solo) + SOLO_JPEG_EXT_SIZE(__solo)) - -#endif /* __SOLO6X10_OFFSETS_H */ diff --git a/drivers/staging/media/solo6x10/solo6x10-p2m.c b/drivers/staging/media/solo6x10/solo6x10-p2m.c deleted file mode 100644 index 8c84846..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-p2m.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 -#include -#include - -#include "solo6x10.h" - -static int multi_p2m; -module_param(multi_p2m, uint, 0644); -MODULE_PARM_DESC(multi_p2m, - "Use multiple P2M DMA channels (default: no, 6010-only)"); - -static int desc_mode; -module_param(desc_mode, uint, 0644); -MODULE_PARM_DESC(desc_mode, - "Allow use of descriptor mode DMA (default: no, 6010-only)"); - -int solo_p2m_dma(struct solo_dev *solo_dev, int wr, - void *sys_addr, u32 ext_addr, u32 size, - int repeat, u32 ext_size) -{ - dma_addr_t dma_addr; - int ret; - - if (WARN_ON_ONCE((unsigned long)sys_addr & 0x03)) - return -EINVAL; - if (WARN_ON_ONCE(!size)) - return -EINVAL; - - dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(solo_dev->pdev, dma_addr)) - return -ENOMEM; - - ret = solo_p2m_dma_t(solo_dev, wr, dma_addr, ext_addr, size, - repeat, ext_size); - - pci_unmap_single(solo_dev->pdev, dma_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - - return ret; -} - -/* Mutex must be held for p2m_id before calling this!! */ -int solo_p2m_dma_desc(struct solo_dev *solo_dev, - struct solo_p2m_desc *desc, dma_addr_t desc_dma, - int desc_cnt) -{ - struct solo_p2m_dev *p2m_dev; - unsigned int timeout; - unsigned int config = 0; - int ret = 0; - int p2m_id = 0; - - /* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */ - if (solo_dev->type != SOLO_DEV_6110 && multi_p2m) { - p2m_id = atomic_inc_return(&solo_dev->p2m_count) % SOLO_NR_P2M; - if (p2m_id < 0) - p2m_id = -p2m_id; - } - - p2m_dev = &solo_dev->p2m_dev[p2m_id]; - - if (mutex_lock_interruptible(&p2m_dev->mutex)) - return -EINTR; - - reinit_completion(&p2m_dev->completion); - p2m_dev->error = 0; - - if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && desc_mode) { - /* For 6010 with more than one desc, we can do a one-shot */ - p2m_dev->desc_count = p2m_dev->desc_idx = 0; - config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(p2m_id)); - - solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(p2m_id), desc_dma); - solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(p2m_id), desc_cnt); - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config | - SOLO_P2M_DESC_MODE); - } else { - /* For single descriptors and 6110, we need to run each desc */ - p2m_dev->desc_count = desc_cnt; - p2m_dev->desc_idx = 1; - p2m_dev->descs = desc; - - solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(p2m_id), - desc[1].dma_addr); - solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(p2m_id), - desc[1].ext_addr); - solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(p2m_id), - desc[1].cfg); - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), - desc[1].ctrl); - } - - timeout = wait_for_completion_timeout(&p2m_dev->completion, - solo_dev->p2m_jiffies); - - if (WARN_ON_ONCE(p2m_dev->error)) - ret = -EIO; - else if (timeout == 0) { - solo_dev->p2m_timeouts++; - ret = -EAGAIN; - } - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), 0); - - /* Don't write here for the no_desc_mode case, because config is 0. - * We can't test no_desc_mode again, it might race. */ - if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && config) - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config); - - mutex_unlock(&p2m_dev->mutex); - - return ret; -} - -void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size, - int repeat, u32 ext_size) -{ - WARN_ON_ONCE(dma_addr & 0x03); - WARN_ON_ONCE(!size); - - desc->cfg = SOLO_P2M_COPY_SIZE(size >> 2); - desc->ctrl = SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | - (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON; - - if (repeat) { - desc->cfg |= SOLO_P2M_EXT_INC(ext_size >> 2); - desc->ctrl |= SOLO_P2M_PCI_INC(size >> 2) | - SOLO_P2M_REPEAT(repeat); - } - - desc->dma_addr = dma_addr; - desc->ext_addr = ext_addr; -} - -int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size, - int repeat, u32 ext_size) -{ - struct solo_p2m_desc desc[2]; - - solo_p2m_fill_desc(&desc[1], wr, dma_addr, ext_addr, size, repeat, - ext_size); - - /* No need for desc_dma since we know it is a single-shot */ - return solo_p2m_dma_desc(solo_dev, desc, 0, 1); -} - -void solo_p2m_isr(struct solo_dev *solo_dev, int id) -{ - struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id]; - struct solo_p2m_desc *desc; - - if (p2m_dev->desc_count <= p2m_dev->desc_idx) { - complete(&p2m_dev->completion); - return; - } - - /* Setup next descriptor */ - p2m_dev->desc_idx++; - desc = &p2m_dev->descs[p2m_dev->desc_idx]; - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); - solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), desc->dma_addr); - solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), desc->ext_addr); - solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id), desc->cfg); - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), desc->ctrl); -} - -void solo_p2m_error_isr(struct solo_dev *solo_dev) -{ - unsigned int err = solo_reg_read(solo_dev, SOLO_PCI_ERR); - struct solo_p2m_dev *p2m_dev; - int i; - - if (!(err & (SOLO_PCI_ERR_P2M | SOLO_PCI_ERR_P2M_DESC))) - return; - - for (i = 0; i < SOLO_NR_P2M; i++) { - p2m_dev = &solo_dev->p2m_dev[i]; - p2m_dev->error = 1; - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); - complete(&p2m_dev->completion); - } -} - -void solo_p2m_exit(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < SOLO_NR_P2M; i++) - solo_irq_off(solo_dev, SOLO_IRQ_P2M(i)); -} - -static int solo_p2m_test(struct solo_dev *solo_dev, int base, int size) -{ - u32 *wr_buf; - u32 *rd_buf; - int i; - int ret = -EIO; - int order = get_order(size); - - wr_buf = (u32 *)__get_free_pages(GFP_KERNEL, order); - if (wr_buf == NULL) - return -1; - - rd_buf = (u32 *)__get_free_pages(GFP_KERNEL, order); - if (rd_buf == NULL) { - free_pages((unsigned long)wr_buf, order); - return -1; - } - - for (i = 0; i < (size >> 3); i++) - *(wr_buf + i) = (i << 16) | (i + 1); - - for (i = (size >> 3); i < (size >> 2); i++) - *(wr_buf + i) = ~((i << 16) | (i + 1)); - - memset(rd_buf, 0x55, size); - - if (solo_p2m_dma(solo_dev, 1, wr_buf, base, size, 0, 0)) - goto test_fail; - - if (solo_p2m_dma(solo_dev, 0, rd_buf, base, size, 0, 0)) - goto test_fail; - - for (i = 0; i < (size >> 2); i++) { - if (*(wr_buf + i) != *(rd_buf + i)) - goto test_fail; - } - - ret = 0; - -test_fail: - free_pages((unsigned long)wr_buf, order); - free_pages((unsigned long)rd_buf, order); - - return ret; -} - -int solo_p2m_init(struct solo_dev *solo_dev) -{ - struct solo_p2m_dev *p2m_dev; - int i; - - for (i = 0; i < SOLO_NR_P2M; i++) { - p2m_dev = &solo_dev->p2m_dev[i]; - - mutex_init(&p2m_dev->mutex); - init_completion(&p2m_dev->completion); - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), - SOLO_P2M_CSC_16BIT_565 | - SOLO_P2M_DESC_INTR_OPT | - SOLO_P2M_DMA_INTERVAL(0) | - SOLO_P2M_PCI_MASTER_MODE); - solo_irq_on(solo_dev, SOLO_IRQ_P2M(i)); - } - - /* Find correct SDRAM size */ - for (solo_dev->sdram_size = 0, i = 2; i >= 0; i--) { - solo_reg_write(solo_dev, SOLO_DMA_CTRL, - SOLO_DMA_CTRL_REFRESH_CYCLE(1) | - SOLO_DMA_CTRL_SDRAM_SIZE(i) | - SOLO_DMA_CTRL_SDRAM_CLK_INVERT | - SOLO_DMA_CTRL_READ_CLK_SELECT | - SOLO_DMA_CTRL_LATENCY(1)); - - solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config | - SOLO_SYS_CFG_RESET); - solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); - - switch (i) { - case 2: - if (solo_p2m_test(solo_dev, 0x07ff0000, 0x00010000) || - solo_p2m_test(solo_dev, 0x05ff0000, 0x00010000)) - continue; - break; - - case 1: - if (solo_p2m_test(solo_dev, 0x03ff0000, 0x00010000)) - continue; - break; - - default: - if (solo_p2m_test(solo_dev, 0x01ff0000, 0x00010000)) - continue; - } - - solo_dev->sdram_size = (32 << 20) << i; - break; - } - - if (!solo_dev->sdram_size) { - dev_err(&solo_dev->pdev->dev, "Error detecting SDRAM size\n"); - return -EIO; - } - - if (SOLO_SDRAM_END(solo_dev) > solo_dev->sdram_size) { - dev_err(&solo_dev->pdev->dev, - "SDRAM is not large enough (%u < %u)\n", - solo_dev->sdram_size, SOLO_SDRAM_END(solo_dev)); - return -EIO; - } - - return 0; -} diff --git a/drivers/staging/media/solo6x10/solo6x10-regs.h b/drivers/staging/media/solo6x10/solo6x10-regs.h deleted file mode 100644 index e34ac56..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-regs.h +++ /dev/null @@ -1,635 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 __SOLO6X10_REGISTERS_H -#define __SOLO6X10_REGISTERS_H - -#include "solo6x10-offsets.h" - -/* Global 6010 system configuration */ -#define SOLO_SYS_CFG 0x0000 -#define SOLO_SYS_CFG_FOUT_EN 0x00000001 -#define SOLO_SYS_CFG_PLL_BYPASS 0x00000002 -#define SOLO_SYS_CFG_PLL_PWDN 0x00000004 -#define SOLO_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) -#define SOLO_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) -#define SOLO_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) -#define SOLO_SYS_CFG_CLOCK_DIV 0x00080000 -#define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24) -#define SOLO_SYS_CFG_PCLK_DELAY(__n) (((__n) & 0x00f) << 26) -#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000 -#define SOLO_SYS_CFG_RESET 0x80000000 - -#define SOLO_DMA_CTRL 0x0004 -#define SOLO_DMA_CTRL_REFRESH_CYCLE(n) ((n)<<8) -/* 0=16/32MB, 1=32/64MB, 2=64/128MB, 3=128/256MB */ -#define SOLO_DMA_CTRL_SDRAM_SIZE(n) ((n)<<6) -#define SOLO_DMA_CTRL_SDRAM_CLK_INVERT (1<<5) -#define SOLO_DMA_CTRL_STROBE_SELECT (1<<4) -#define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3) -#define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2) -#define SOLO_DMA_CTRL_LATENCY(n) ((n)<<0) - -/* Some things we set in this are undocumented. Why Softlogic?!?! */ -#define SOLO_DMA_CTRL1 0x0008 - -#define SOLO_SYS_VCLK 0x000C -#define SOLO_VCLK_INVERT (1<<22) -/* 0=sys_clk/4, 1=sys_clk/2, 2=clk_in/2 of system input */ -#define SOLO_VCLK_SELECT(n) ((n)<<20) -#define SOLO_VCLK_VIN1415_DELAY(n) ((n)<<14) -#define SOLO_VCLK_VIN1213_DELAY(n) ((n)<<12) -#define SOLO_VCLK_VIN1011_DELAY(n) ((n)<<10) -#define SOLO_VCLK_VIN0809_DELAY(n) ((n)<<8) -#define SOLO_VCLK_VIN0607_DELAY(n) ((n)<<6) -#define SOLO_VCLK_VIN0405_DELAY(n) ((n)<<4) -#define SOLO_VCLK_VIN0203_DELAY(n) ((n)<<2) -#define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0) - -#define SOLO_IRQ_STAT 0x0010 -#define SOLO_IRQ_MASK 0x0014 -#define SOLO_IRQ_P2M(n) (1<<((n)+17)) -#define SOLO_IRQ_GPIO (1<<16) -#define SOLO_IRQ_VIDEO_LOSS (1<<15) -#define SOLO_IRQ_VIDEO_IN (1<<14) -#define SOLO_IRQ_MOTION (1<<13) -#define SOLO_IRQ_ATA_CMD (1<<12) -#define SOLO_IRQ_ATA_DIR (1<<11) -#define SOLO_IRQ_PCI_ERR (1<<10) -#define SOLO_IRQ_PS2_1 (1<<9) -#define SOLO_IRQ_PS2_0 (1<<8) -#define SOLO_IRQ_SPI (1<<7) -#define SOLO_IRQ_IIC (1<<6) -#define SOLO_IRQ_UART(n) (1<<((n) + 4)) -#define SOLO_IRQ_G723 (1<<3) -#define SOLO_IRQ_DECODER (1<<1) -#define SOLO_IRQ_ENCODER (1<<0) - -#define SOLO_CHIP_OPTION 0x001C -#define SOLO_CHIP_ID_MASK 0x00000007 - -#define SOLO_PLL_CONFIG 0x0020 /* 6110 Only */ - -#define SOLO_EEPROM_CTRL 0x0060 -#define SOLO_EEPROM_ACCESS_EN (1<<7) -#define SOLO_EEPROM_CS (1<<3) -#define SOLO_EEPROM_CLK (1<<2) -#define SOLO_EEPROM_DO (1<<1) -#define SOLO_EEPROM_DI (1<<0) -#define SOLO_EEPROM_ENABLE (SOLO_EEPROM_ACCESS_EN | SOLO_EEPROM_CS) - -#define SOLO_PCI_ERR 0x0070 -#define SOLO_PCI_ERR_FATAL 0x00000001 -#define SOLO_PCI_ERR_PARITY 0x00000002 -#define SOLO_PCI_ERR_TARGET 0x00000004 -#define SOLO_PCI_ERR_TIMEOUT 0x00000008 -#define SOLO_PCI_ERR_P2M 0x00000010 -#define SOLO_PCI_ERR_ATA 0x00000020 -#define SOLO_PCI_ERR_P2M_DESC 0x00000040 -#define SOLO_PCI_ERR_FSM0(__s) (((__s) >> 16) & 0x0f) -#define SOLO_PCI_ERR_FSM1(__s) (((__s) >> 20) & 0x0f) -#define SOLO_PCI_ERR_FSM2(__s) (((__s) >> 24) & 0x1f) - -#define SOLO_P2M_BASE 0x0080 - -#define SOLO_P2M_CONFIG(n) (0x0080 + ((n)*0x20)) -#define SOLO_P2M_DMA_INTERVAL(n) ((n)<<6)/* N*32 clocks */ -#define SOLO_P2M_CSC_BYTE_REORDER (1<<5) /* BGR -> RGB */ -/* 0:r=[14:10] g=[9:5] b=[4:0], 1:r=[15:11] g=[10:5] b=[4:0] */ -#define SOLO_P2M_CSC_16BIT_565 (1<<4) -#define SOLO_P2M_UV_SWAP (1<<3) -#define SOLO_P2M_PCI_MASTER_MODE (1<<2) -#define SOLO_P2M_DESC_INTR_OPT (1<<1) /* 1:Empty, 0:Each */ -#define SOLO_P2M_DESC_MODE (1<<0) - -#define SOLO_P2M_DES_ADR(n) (0x0084 + ((n)*0x20)) - -#define SOLO_P2M_DESC_ID(n) (0x0088 + ((n)*0x20)) -#define SOLO_P2M_UPDATE_ID(n) ((n)<<0) - -#define SOLO_P2M_STATUS(n) (0x008C + ((n)*0x20)) -#define SOLO_P2M_COMMAND_DONE (1<<8) -#define SOLO_P2M_CURRENT_ID(stat) (0xff & (stat)) - -#define SOLO_P2M_CONTROL(n) (0x0090 + ((n)*0x20)) -#define SOLO_P2M_PCI_INC(n) ((n)<<20) -#define SOLO_P2M_REPEAT(n) ((n)<<10) -/* 0:512, 1:256, 2:128, 3:64, 4:32, 5:128(2page) */ -#define SOLO_P2M_BURST_SIZE(n) ((n)<<7) -#define SOLO_P2M_BURST_512 0 -#define SOLO_P2M_BURST_256 1 -#define SOLO_P2M_BURST_128 2 -#define SOLO_P2M_BURST_64 3 -#define SOLO_P2M_BURST_32 4 -#define SOLO_P2M_CSC_16BIT (1<<6) /* 0:24bit, 1:16bit */ -/* 0:Y[0]<-0(OFF), 1:Y[0]<-1(ON), 2:Y[0]<-G[0], 3:Y[0]<-Bit[15] */ -#define SOLO_P2M_ALPHA_MODE(n) ((n)<<4) -#define SOLO_P2M_CSC_ON (1<<3) -#define SOLO_P2M_INTERRUPT_REQ (1<<2) -#define SOLO_P2M_WRITE (1<<1) -#define SOLO_P2M_TRANS_ON (1<<0) - -#define SOLO_P2M_EXT_CFG(n) (0x0094 + ((n)*0x20)) -#define SOLO_P2M_EXT_INC(n) ((n)<<20) -#define SOLO_P2M_COPY_SIZE(n) ((n)<<0) - -#define SOLO_P2M_TAR_ADR(n) (0x0098 + ((n)*0x20)) - -#define SOLO_P2M_EXT_ADR(n) (0x009C + ((n)*0x20)) - -#define SOLO_P2M_BUFFER(i) (0x2000 + ((i)*4)) - -#define SOLO_VI_CH_SWITCH_0 0x0100 -#define SOLO_VI_CH_SWITCH_1 0x0104 -#define SOLO_VI_CH_SWITCH_2 0x0108 - -#define SOLO_VI_CH_ENA 0x010C -#define SOLO_VI_CH_FORMAT 0x0110 -#define SOLO_VI_FD_SEL_MASK(n) ((n)<<16) -#define SOLO_VI_PROG_MASK(n) ((n)<<0) - -#define SOLO_VI_FMT_CFG 0x0114 -#define SOLO_VI_FMT_CHECK_VCOUNT (1<<31) -#define SOLO_VI_FMT_CHECK_HCOUNT (1<<30) -#define SOLO_VI_FMT_TEST_SIGNAL (1<<28) - -#define SOLO_VI_PAGE_SW 0x0118 -#define SOLO_FI_INV_DISP_LIVE(n) ((n)<<8) -#define SOLO_FI_INV_DISP_OUT(n) ((n)<<7) -#define SOLO_DISP_SYNC_FI(n) ((n)<<6) -#define SOLO_PIP_PAGE_ADD(n) ((n)<<3) -#define SOLO_NORMAL_PAGE_ADD(n) ((n)<<0) - -#define SOLO_VI_ACT_I_P 0x011C -#define SOLO_VI_ACT_I_S 0x0120 -#define SOLO_VI_ACT_P 0x0124 -#define SOLO_VI_FI_INVERT (1<<31) -#define SOLO_VI_H_START(n) ((n)<<21) -#define SOLO_VI_V_START(n) ((n)<<11) -#define SOLO_VI_V_STOP(n) ((n)<<0) - -#define SOLO_VI_STATUS0 0x0128 -#define SOLO_VI_STATUS0_PAGE(__n) ((__n) & 0x07) -#define SOLO_VI_STATUS1 0x012C - -/* XXX: Might be better off in kernel level disp.h */ -#define DISP_PAGE(stat) ((stat) & 0x07) - -#define SOLO_VI_PB_CONFIG 0x0130 -#define SOLO_VI_PB_USER_MODE (1<<1) -#define SOLO_VI_PB_PAL (1<<0) -#define SOLO_VI_PB_RANGE_HV 0x0134 -#define SOLO_VI_PB_HSIZE(h) ((h)<<12) -#define SOLO_VI_PB_VSIZE(v) ((v)<<0) -#define SOLO_VI_PB_ACT_H 0x0138 -#define SOLO_VI_PB_HSTART(n) ((n)<<12) -#define SOLO_VI_PB_HSTOP(n) ((n)<<0) -#define SOLO_VI_PB_ACT_V 0x013C -#define SOLO_VI_PB_VSTART(n) ((n)<<12) -#define SOLO_VI_PB_VSTOP(n) ((n)<<0) - -#define SOLO_VI_MOSAIC(ch) (0x0140 + ((ch)*4)) -#define SOLO_VI_MOSAIC_SX(x) ((x)<<24) -#define SOLO_VI_MOSAIC_EX(x) ((x)<<16) -#define SOLO_VI_MOSAIC_SY(x) ((x)<<8) -#define SOLO_VI_MOSAIC_EY(x) ((x)<<0) - -#define SOLO_VI_WIN_CTRL0(ch) (0x0180 + ((ch)*4)) -#define SOLO_VI_WIN_CTRL1(ch) (0x01C0 + ((ch)*4)) - -#define SOLO_VI_WIN_CHANNEL(n) ((n)<<28) - -#define SOLO_VI_WIN_PIP(n) ((n)<<27) -#define SOLO_VI_WIN_SCALE(n) ((n)<<24) - -#define SOLO_VI_WIN_SX(x) ((x)<<12) -#define SOLO_VI_WIN_EX(x) ((x)<<0) - -#define SOLO_VI_WIN_SY(x) ((x)<<12) -#define SOLO_VI_WIN_EY(x) ((x)<<0) - -#define SOLO_VI_WIN_ON(ch) (0x0200 + ((ch)*4)) - -#define SOLO_VI_WIN_SW 0x0240 -#define SOLO_VI_WIN_LIVE_AUTO_MUTE 0x0244 - -#define SOLO_VI_MOT_ADR 0x0260 -#define SOLO_VI_MOTION_EN(mask) ((mask)<<16) -#define SOLO_VI_MOT_CTRL 0x0264 -#define SOLO_VI_MOTION_FRAME_COUNT(n) ((n)<<24) -#define SOLO_VI_MOTION_SAMPLE_LENGTH(n) ((n)<<16) -#define SOLO_VI_MOTION_INTR_START_STOP (1<<15) -#define SOLO_VI_MOTION_FREEZE_DATA (1<<14) -#define SOLO_VI_MOTION_SAMPLE_COUNT(n) ((n)<<0) -#define SOLO_VI_MOT_CLEAR 0x0268 -#define SOLO_VI_MOT_STATUS 0x026C -#define SOLO_VI_MOTION_CNT(n) ((n)<<0) -#define SOLO_VI_MOTION_BORDER 0x0270 -#define SOLO_VI_MOTION_BAR 0x0274 -#define SOLO_VI_MOTION_Y_SET (1<<29) -#define SOLO_VI_MOTION_Y_ADD (1<<28) -#define SOLO_VI_MOTION_CB_SET (1<<27) -#define SOLO_VI_MOTION_CB_ADD (1<<26) -#define SOLO_VI_MOTION_CR_SET (1<<25) -#define SOLO_VI_MOTION_CR_ADD (1<<24) -#define SOLO_VI_MOTION_Y_VALUE(v) ((v)<<16) -#define SOLO_VI_MOTION_CB_VALUE(v) ((v)<<8) -#define SOLO_VI_MOTION_CR_VALUE(v) ((v)<<0) - -#define SOLO_VO_FMT_ENC 0x0300 -#define SOLO_VO_SCAN_MODE_PROGRESSIVE (1<<31) -#define SOLO_VO_FMT_TYPE_PAL (1<<30) -#define SOLO_VO_FMT_TYPE_NTSC 0 -#define SOLO_VO_USER_SET (1<<29) - -#define SOLO_VO_FI_CHANGE (1<<20) -#define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19) -#define SOLO_VO_USER_COLOR_SET_HSYNC (1<<18) -#define SOLO_VO_USER_COLOR_SET_NAH (1<<17) -#define SOLO_VO_USER_COLOR_SET_NAV (1<<16) -#define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8) -#define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4) -#define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0) - -#define SOLO_VO_ACT_H 0x0304 -#define SOLO_VO_H_BLANK(n) ((n)<<22) -#define SOLO_VO_H_START(n) ((n)<<11) -#define SOLO_VO_H_STOP(n) ((n)<<0) - -#define SOLO_VO_ACT_V 0x0308 -#define SOLO_VO_V_BLANK(n) ((n)<<22) -#define SOLO_VO_V_START(n) ((n)<<11) -#define SOLO_VO_V_STOP(n) ((n)<<0) - -#define SOLO_VO_RANGE_HV 0x030C -#define SOLO_VO_SYNC_INVERT (1<<24) -#define SOLO_VO_HSYNC_INVERT (1<<23) -#define SOLO_VO_VSYNC_INVERT (1<<22) -#define SOLO_VO_H_LEN(n) ((n)<<11) -#define SOLO_VO_V_LEN(n) ((n)<<0) - -#define SOLO_VO_DISP_CTRL 0x0310 -#define SOLO_VO_DISP_ON (1<<31) -#define SOLO_VO_DISP_ERASE_COUNT(n) ((n&0xf)<<24) -#define SOLO_VO_DISP_DOUBLE_SCAN (1<<22) -#define SOLO_VO_DISP_SINGLE_PAGE (1<<21) -#define SOLO_VO_DISP_BASE(n) (((n)>>16) & 0xffff) - -#define SOLO_VO_DISP_ERASE 0x0314 -#define SOLO_VO_DISP_ERASE_ON (1<<0) - -#define SOLO_VO_ZOOM_CTRL 0x0318 -#define SOLO_VO_ZOOM_VER_ON (1<<24) -#define SOLO_VO_ZOOM_HOR_ON (1<<23) -#define SOLO_VO_ZOOM_V_COMP (1<<22) -#define SOLO_VO_ZOOM_SX(h) (((h)/2)<<11) -#define SOLO_VO_ZOOM_SY(v) (((v)/2)<<0) - -#define SOLO_VO_FREEZE_CTRL 0x031C -#define SOLO_VO_FREEZE_ON (1<<1) -#define SOLO_VO_FREEZE_INTERPOLATION (1<<0) - -#define SOLO_VO_BKG_COLOR 0x0320 -#define SOLO_BG_Y(y) ((y)<<16) -#define SOLO_BG_U(u) ((u)<<8) -#define SOLO_BG_V(v) ((v)<<0) - -#define SOLO_VO_DEINTERLACE 0x0324 -#define SOLO_VO_DEINTERLACE_THRESHOLD(n) ((n)<<8) -#define SOLO_VO_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) - -#define SOLO_VO_BORDER_LINE_COLOR 0x0330 -#define SOLO_VO_BORDER_FILL_COLOR 0x0334 -#define SOLO_VO_BORDER_LINE_MASK 0x0338 -#define SOLO_VO_BORDER_FILL_MASK 0x033c - -#define SOLO_VO_BORDER_X(n) (0x0340+((n)*4)) -#define SOLO_VO_BORDER_Y(n) (0x0354+((n)*4)) - -#define SOLO_VO_CELL_EXT_SET 0x0368 -#define SOLO_VO_CELL_EXT_START 0x036c -#define SOLO_VO_CELL_EXT_STOP 0x0370 - -#define SOLO_VO_CELL_EXT_SET2 0x0374 -#define SOLO_VO_CELL_EXT_START2 0x0378 -#define SOLO_VO_CELL_EXT_STOP2 0x037c - -#define SOLO_VO_RECTANGLE_CTRL(n) (0x0368+((n)*12)) -#define SOLO_VO_RECTANGLE_START(n) (0x036c+((n)*12)) -#define SOLO_VO_RECTANGLE_STOP(n) (0x0370+((n)*12)) - -#define SOLO_VO_CURSOR_POS (0x0380) -#define SOLO_VO_CURSOR_CLR (0x0384) -#define SOLO_VO_CURSOR_CLR2 (0x0388) -#define SOLO_VO_CURSOR_MASK(id) (0x0390+((id)*4)) - -#define SOLO_VO_EXPANSION(id) (0x0250+((id)*4)) - -#define SOLO_OSG_CONFIG 0x03E0 -#define SOLO_VO_OSG_ON (1<<31) -#define SOLO_VO_OSG_COLOR_MUTE (1<<28) -#define SOLO_VO_OSG_ALPHA_RATE(n) ((n)<<22) -#define SOLO_VO_OSG_ALPHA_BG_RATE(n) ((n)<<16) -#define SOLO_VO_OSG_BASE(offset) (((offset)>>16)&0xffff) - -#define SOLO_OSG_ERASE 0x03E4 -#define SOLO_OSG_ERASE_ON (0x80) -#define SOLO_OSG_ERASE_OFF (0x00) - -#define SOLO_VO_OSG_BLINK 0x03E8 -#define SOLO_VO_OSG_BLINK_ON (1<<1) -#define SOLO_VO_OSG_BLINK_INTREVAL18 (1<<0) - -#define SOLO_CAP_BASE 0x0400 -#define SOLO_CAP_MAX_PAGE(n) ((n)<<16) -#define SOLO_CAP_BASE_ADDR(n) ((n)<<0) -#define SOLO_CAP_BTW 0x0404 -#define SOLO_CAP_PROG_BANDWIDTH(n) ((n)<<8) -#define SOLO_CAP_MAX_BANDWIDTH(n) ((n)<<0) - -#define SOLO_DIM_SCALE1 0x0408 -#define SOLO_DIM_SCALE2 0x040C -#define SOLO_DIM_SCALE3 0x0410 -#define SOLO_DIM_SCALE4 0x0414 -#define SOLO_DIM_SCALE5 0x0418 -#define SOLO_DIM_V_MB_NUM_FRAME(n) ((n)<<16) -#define SOLO_DIM_V_MB_NUM_FIELD(n) ((n)<<8) -#define SOLO_DIM_H_MB_NUM(n) ((n)<<0) - -#define SOLO_DIM_PROG 0x041C -#define SOLO_CAP_STATUS 0x0420 - -#define SOLO_CAP_CH_SCALE(ch) (0x0440+((ch)*4)) -#define SOLO_CAP_CH_COMP_ENA_E(ch) (0x0480+((ch)*4)) -#define SOLO_CAP_CH_INTV(ch) (0x04C0+((ch)*4)) -#define SOLO_CAP_CH_INTV_E(ch) (0x0500+((ch)*4)) - - -#define SOLO_VE_CFG0 0x0610 -#define SOLO_VE_TWO_PAGE_MODE (1<<31) -#define SOLO_VE_INTR_CTRL(n) ((n)<<24) -#define SOLO_VE_BLOCK_SIZE(n) ((n)<<16) -#define SOLO_VE_BLOCK_BASE(n) ((n)<<0) - -#define SOLO_VE_CFG1 0x0614 -#define SOLO_VE_BYTE_ALIGN(n) ((n)<<24) -#define SOLO_VE_INSERT_INDEX (1<<18) -#define SOLO_VE_MOTION_MODE(n) ((n)<<16) -#define SOLO_VE_MOTION_BASE(n) ((n)<<0) -#define SOLO_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 Only */ -#define SOLO_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 Only */ -#define SOLO_VE_INSERT_INDEX_JPEG (1<<19) /* 6110 Only */ - -#define SOLO_VE_WMRK_POLY 0x061C -#define SOLO_VE_VMRK_INIT_KEY 0x0620 -#define SOLO_VE_WMRK_STRL 0x0624 -#define SOLO_VE_ENCRYP_POLY 0x0628 -#define SOLO_VE_ENCRYP_INIT 0x062C -#define SOLO_VE_ATTR 0x0630 -#define SOLO_VE_LITTLE_ENDIAN (1<<31) -#define SOLO_COMP_ATTR_RN (1<<30) -#define SOLO_COMP_ATTR_FCODE(n) ((n)<<27) -#define SOLO_COMP_TIME_INC(n) ((n)<<25) -#define SOLO_COMP_TIME_WIDTH(n) ((n)<<21) -#define SOLO_DCT_INTERVAL(n) ((n)<<16) -#define SOLO_VE_COMPT_MOT 0x0634 /* 6110 Only */ - -#define SOLO_VE_STATE(n) (0x0640+((n)*4)) - -#define SOLO_VE_JPEG_QP_TBL 0x0670 -#define SOLO_VE_JPEG_QP_CH_L 0x0674 -#define SOLO_VE_JPEG_QP_CH_H 0x0678 -#define SOLO_VE_JPEG_CFG 0x067C -#define SOLO_VE_JPEG_CTRL 0x0680 -#define SOLO_VE_CODE_ENCRYPT 0x0684 /* 6110 Only */ -#define SOLO_VE_JPEG_CFG1 0x0688 /* 6110 Only */ -#define SOLO_VE_WMRK_ENABLE 0x068C /* 6110 Only */ -#define SOLO_VE_OSD_CH 0x0690 -#define SOLO_VE_OSD_BASE 0x0694 -#define SOLO_VE_OSD_CLR 0x0698 -#define SOLO_VE_OSD_OPT 0x069C -#define SOLO_VE_OSD_V_DOUBLE (1<<16) /* 6110 Only */ -#define SOLO_VE_OSD_H_SHADOW (1<<15) -#define SOLO_VE_OSD_V_SHADOW (1<<14) -#define SOLO_VE_OSD_H_OFFSET(n) ((n & 0x7f)<<7) -#define SOLO_VE_OSD_V_OFFSET(n) (n & 0x7f) - -#define SOLO_VE_CH_INTL(ch) (0x0700+((ch)*4)) -#define SOLO_VE_CH_MOT(ch) (0x0740+((ch)*4)) -#define SOLO_VE_CH_QP(ch) (0x0780+((ch)*4)) -#define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4)) -#define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4)) -#define SOLO_VE_CH_GOP_E(ch) (0x0840+((ch)*4)) -#define SOLO_VE_CH_REF_BASE(ch) (0x0880+((ch)*4)) -#define SOLO_VE_CH_REF_BASE_E(ch) (0x08C0+((ch)*4)) - -#define SOLO_VE_MPEG4_QUE(n) (0x0A00+((n)*8)) -#define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8)) - -#define SOLO_VD_CFG0 0x0900 -#define SOLO_VD_CFG_NO_WRITE_NO_WINDOW (1<<24) -#define SOLO_VD_CFG_BUSY_WIAT_CODE (1<<23) -#define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22) -#define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21) -#define SOLO_VD_CFG_BUSY_WIAT_MS (1<<20) -#define SOLO_VD_CFG_SINGLE_MODE (1<<18) -#define SOLO_VD_CFG_SCAL_MANUAL (1<<17) -#define SOLO_VD_CFG_USER_PAGE_CTRL (1<<16) -#define SOLO_VD_CFG_LITTLE_ENDIAN (1<<15) -#define SOLO_VD_CFG_START_FI (1<<14) -#define SOLO_VD_CFG_ERR_LOCK (1<<13) -#define SOLO_VD_CFG_ERR_INT_ENA (1<<12) -#define SOLO_VD_CFG_TIME_WIDTH(n) ((n)<<8) -#define SOLO_VD_CFG_DCT_INTERVAL(n) ((n)<<0) - -#define SOLO_VD_CFG1 0x0904 - -#define SOLO_VD_DEINTERLACE 0x0908 -#define SOLO_VD_DEINTERLACE_THRESHOLD(n) ((n)<<8) -#define SOLO_VD_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) - -#define SOLO_VD_CODE_ADR 0x090C - -#define SOLO_VD_CTRL 0x0910 -#define SOLO_VD_OPER_ON (1<<31) -#define SOLO_VD_MAX_ITEM(n) ((n)<<0) - -#define SOLO_VD_STATUS0 0x0920 -#define SOLO_VD_STATUS0_INTR_ACK (1<<22) -#define SOLO_VD_STATUS0_INTR_EMPTY (1<<21) -#define SOLO_VD_STATUS0_INTR_ERR (1<<20) - -#define SOLO_VD_STATUS1 0x0924 - -#define SOLO_VD_IDX0 0x0930 -#define SOLO_VD_IDX_INTERLACE (1<<30) -#define SOLO_VD_IDX_CHANNEL(n) ((n)<<24) -#define SOLO_VD_IDX_SIZE(n) ((n)<<0) - -#define SOLO_VD_IDX1 0x0934 -#define SOLO_VD_IDX_SRC_SCALE(n) ((n)<<28) -#define SOLO_VD_IDX_WINDOW(n) ((n)<<24) -#define SOLO_VD_IDX_DEINTERLACE (1<<16) -#define SOLO_VD_IDX_H_BLOCK(n) ((n)<<8) -#define SOLO_VD_IDX_V_BLOCK(n) ((n)<<0) - -#define SOLO_VD_IDX2 0x0938 -#define SOLO_VD_IDX_REF_BASE_SIDE (1<<31) -#define SOLO_VD_IDX_REF_BASE(n) (((n)>>16)&0xffff) - -#define SOLO_VD_IDX3 0x093C -#define SOLO_VD_IDX_DISP_SCALE(n) ((n)<<28) -#define SOLO_VD_IDX_INTERLACE_WR (1<<27) -#define SOLO_VD_IDX_INTERPOL (1<<26) -#define SOLO_VD_IDX_HOR2X (1<<25) -#define SOLO_VD_IDX_OFFSET_X(n) ((n)<<12) -#define SOLO_VD_IDX_OFFSET_Y(n) ((n)<<0) - -#define SOLO_VD_IDX4 0x0940 -#define SOLO_VD_IDX_DEC_WR_PAGE(n) ((n)<<8) -#define SOLO_VD_IDX_DISP_RD_PAGE(n) ((n)<<0) - -#define SOLO_VD_WR_PAGE(n) (0x03F0 + ((n) * 4)) - - -#define SOLO_GPIO_CONFIG_0 0x0B00 -#define SOLO_GPIO_CONFIG_1 0x0B04 -#define SOLO_GPIO_DATA_OUT 0x0B08 -#define SOLO_GPIO_DATA_IN 0x0B0C -#define SOLO_GPIO_INT_ACK_STA 0x0B10 -#define SOLO_GPIO_INT_ENA 0x0B14 -#define SOLO_GPIO_INT_CFG_0 0x0B18 -#define SOLO_GPIO_INT_CFG_1 0x0B1C - - -#define SOLO_IIC_CFG 0x0B20 -#define SOLO_IIC_ENABLE (1<<8) -#define SOLO_IIC_PRESCALE(n) ((n)<<0) - -#define SOLO_IIC_CTRL 0x0B24 -#define SOLO_IIC_AUTO_CLEAR (1<<20) -#define SOLO_IIC_STATE_RX_ACK (1<<19) -#define SOLO_IIC_STATE_BUSY (1<<18) -#define SOLO_IIC_STATE_SIG_ERR (1<<17) -#define SOLO_IIC_STATE_TRNS (1<<16) -#define SOLO_IIC_CH_SET(n) ((n)<<5) -#define SOLO_IIC_ACK_EN (1<<4) -#define SOLO_IIC_START (1<<3) -#define SOLO_IIC_STOP (1<<2) -#define SOLO_IIC_READ (1<<1) -#define SOLO_IIC_WRITE (1<<0) - -#define SOLO_IIC_TXD 0x0B28 -#define SOLO_IIC_RXD 0x0B2C - -/* - * UART REGISTER - */ -#define SOLO_UART_CONTROL(n) (0x0BA0 + ((n)*0x20)) -#define SOLO_UART_CLK_DIV(n) ((n)<<24) -#define SOLO_MODEM_CTRL_EN (1<<20) -#define SOLO_PARITY_ERROR_DROP (1<<18) -#define SOLO_IRQ_ERR_EN (1<<17) -#define SOLO_IRQ_RX_EN (1<<16) -#define SOLO_IRQ_TX_EN (1<<15) -#define SOLO_RX_EN (1<<14) -#define SOLO_TX_EN (1<<13) -#define SOLO_UART_HALF_DUPLEX (1<<12) -#define SOLO_UART_LOOPBACK (1<<11) - -#define SOLO_BAUDRATE_230400 ((0<<9)|(0<<6)) -#define SOLO_BAUDRATE_115200 ((0<<9)|(1<<6)) -#define SOLO_BAUDRATE_57600 ((0<<9)|(2<<6)) -#define SOLO_BAUDRATE_38400 ((0<<9)|(3<<6)) -#define SOLO_BAUDRATE_19200 ((0<<9)|(4<<6)) -#define SOLO_BAUDRATE_9600 ((0<<9)|(5<<6)) -#define SOLO_BAUDRATE_4800 ((0<<9)|(6<<6)) -#define SOLO_BAUDRATE_2400 ((1<<9)|(6<<6)) -#define SOLO_BAUDRATE_1200 ((2<<9)|(6<<6)) -#define SOLO_BAUDRATE_300 ((3<<9)|(6<<6)) - -#define SOLO_UART_DATA_BIT_8 (3<<4) -#define SOLO_UART_DATA_BIT_7 (2<<4) -#define SOLO_UART_DATA_BIT_6 (1<<4) -#define SOLO_UART_DATA_BIT_5 (0<<4) - -#define SOLO_UART_STOP_BIT_1 (0<<2) -#define SOLO_UART_STOP_BIT_2 (1<<2) - -#define SOLO_UART_PARITY_NONE (0<<0) -#define SOLO_UART_PARITY_EVEN (2<<0) -#define SOLO_UART_PARITY_ODD (3<<0) - -#define SOLO_UART_STATUS(n) (0x0BA4 + ((n)*0x20)) -#define SOLO_UART_CTS (1<<15) -#define SOLO_UART_RX_BUSY (1<<14) -#define SOLO_UART_OVERRUN (1<<13) -#define SOLO_UART_FRAME_ERR (1<<12) -#define SOLO_UART_PARITY_ERR (1<<11) -#define SOLO_UART_TX_BUSY (1<<5) - -#define SOLO_UART_RX_BUFF_CNT(stat) (((stat)>>6) & 0x1f) -#define SOLO_UART_RX_BUFF_SIZE 8 -#define SOLO_UART_TX_BUFF_CNT(stat) (((stat)>>0) & 0x1f) -#define SOLO_UART_TX_BUFF_SIZE 8 - -#define SOLO_UART_TX_DATA(n) (0x0BA8 + ((n)*0x20)) -#define SOLO_UART_TX_DATA_PUSH (1<<8) -#define SOLO_UART_RX_DATA(n) (0x0BAC + ((n)*0x20)) -#define SOLO_UART_RX_DATA_POP (1<<8) - -#define SOLO_TIMER_CLOCK_NUM 0x0be0 -#define SOLO_TIMER_USEC 0x0be8 -#define SOLO_TIMER_SEC 0x0bec -#define SOLO_TIMER_USEC_LSB 0x0d20 /* 6110 Only */ - -#define SOLO_AUDIO_CONTROL 0x0D00 -#define SOLO_AUDIO_ENABLE (1<<31) -#define SOLO_AUDIO_MASTER_MODE (1<<30) -#define SOLO_AUDIO_I2S_MODE (1<<29) -#define SOLO_AUDIO_I2S_LR_SWAP (1<<27) -#define SOLO_AUDIO_I2S_8BIT (1<<26) -#define SOLO_AUDIO_I2S_MULTI(n) ((n)<<24) -#define SOLO_AUDIO_MIX_9TO0 (1<<23) -#define SOLO_AUDIO_DEC_9TO0_VOL(n) ((n)<<20) -#define SOLO_AUDIO_MIX_19TO10 (1<<19) -#define SOLO_AUDIO_DEC_19TO10_VOL(n) ((n)<<16) -#define SOLO_AUDIO_MODE(n) ((n)<<0) -#define SOLO_AUDIO_SAMPLE 0x0D04 -#define SOLO_AUDIO_EE_MODE_ON (1<<30) -#define SOLO_AUDIO_EE_ENC_CH(ch) ((ch)<<25) -#define SOLO_AUDIO_BITRATE(n) ((n)<<16) -#define SOLO_AUDIO_CLK_DIV(n) ((n)<<0) -#define SOLO_AUDIO_FDMA_INTR 0x0D08 -#define SOLO_AUDIO_FDMA_INTERVAL(n) ((n)<<19) -#define SOLO_AUDIO_INTR_ORDER(n) ((n)<<16) -#define SOLO_AUDIO_FDMA_BASE(n) ((n)<<0) -#define SOLO_AUDIO_EVOL_0 0x0D0C -#define SOLO_AUDIO_EVOL_1 0x0D10 -#define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10)) -#define SOLO_AUDIO_STA 0x0D14 - -/* - * Watchdog configuration - */ -#define SOLO_WATCHDOG 0x0be4 -#define SOLO_WATCHDOG_SET(status, sec) (status << 8 | (sec & 0xff)) - -#endif /* __SOLO6X10_REGISTERS_H */ diff --git a/drivers/staging/media/solo6x10/solo6x10-tw28.c b/drivers/staging/media/solo6x10/solo6x10-tw28.c deleted file mode 100644 index edd0781..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-tw28.c +++ /dev/null @@ -1,871 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 -#include - -#include "solo6x10.h" -#include "solo6x10-tw28.h" - -#define DEFAULT_HDELAY_NTSC (32 - 8) -#define DEFAULT_HACTIVE_NTSC (720 + 16) -#define DEFAULT_VDELAY_NTSC (7 - 2) -#define DEFAULT_VACTIVE_NTSC (240 + 4) - -#define DEFAULT_HDELAY_PAL (32 + 4) -#define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL) -#define DEFAULT_VDELAY_PAL (6) -#define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL) - - -static const u8 tbl_tw2864_ntsc_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ - 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ - 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ - 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ - 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, - 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01, - 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, - 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, - 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81, - 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, -}; - -static const u8 tbl_tw2864_pal_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ - 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ - 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ - 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ - 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, - 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01, - 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, - 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, - 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81, - 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, -}; - -static const u8 tbl_tw2865_ntsc_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */ - 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, - 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ - 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, - 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, - 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, - 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ - 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8, - 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, - 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, - 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, -}; - -static const u8 tbl_tw2865_pal_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */ - 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, - 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ - 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, - 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, - 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, - 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ - 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8, - 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, - 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, - 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, -}; - -#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id))) - -static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off, - u8 tw_off) -{ - if (is_tw286x(solo_dev, chip_id)) - return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw6x_off); - else - return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw_off); -} - -static void tw_writebyte(struct solo_dev *solo_dev, int chip_id, - u8 tw6x_off, u8 tw_off, u8 val) -{ - if (is_tw286x(solo_dev, chip_id)) - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw6x_off, val); - else - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw_off, val); -} - -static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off, - u8 val) -{ - int i; - - for (i = 0; i < 5; i++) { - u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off); - - if (rval == val) - return; - - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val); - msleep_interruptible(1); - } - -/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */ -/* addr, off, val); */ -} - -static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_tw2865_common[256]; - int i; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) - memcpy(tbl_tw2865_common, tbl_tw2865_pal_template, - sizeof(tbl_tw2865_common)); - else - memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template, - sizeof(tbl_tw2865_common)); - - /* ALINK Mode */ - if (solo_dev->nr_chans == 4) { - tbl_tw2865_common[0xd2] = 0x01; - tbl_tw2865_common[0xcf] = 0x00; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2865_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2865_common[0xcf] = 0x80; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2865_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2865_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2865_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2865_common[0xcf] = 0x80; - } - - for (i = 0; i < 0xff; i++) { - /* Skip read only registers */ - switch (i) { - case 0xb8 ... 0xc1: - case 0xc4 ... 0xc7: - case 0xfd: - continue; - } - switch (i & ~0x30) { - case 0x00: - case 0x0c ... 0x0d: - continue; - } - - tw_write_and_verify(solo_dev, dev_addr, i, - tbl_tw2865_common[i]); - } - - return 0; -} - -static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_tw2864_common[256]; - int i; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) - memcpy(tbl_tw2864_common, tbl_tw2864_pal_template, - sizeof(tbl_tw2864_common)); - else - memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template, - sizeof(tbl_tw2864_common)); - - if (solo_dev->tw2865 == 0) { - /* IRQ Mode */ - if (solo_dev->nr_chans == 4) { - tbl_tw2864_common[0xd2] = 0x01; - tbl_tw2864_common[0xcf] = 0x00; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2864_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x40; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2864_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2864_common[0xcf] = 0x40; - } - } else { - /* ALINK Mode. Assumes that the first tw28xx is a - * 2865 and these are in cascade. */ - for (i = 0; i <= 4; i++) - tbl_tw2864_common[0x08 | i << 4] = 0x12; - - if (solo_dev->nr_chans == 8) { - tbl_tw2864_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x80; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2864_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2864_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2864_common[0xcf] = 0x80; - } - } - - for (i = 0; i < 0xff; i++) { - /* Skip read only registers */ - switch (i) { - case 0xb8 ... 0xc1: - case 0xfd: - continue; - } - switch (i & ~0x30) { - case 0x00: - case 0x0c: - case 0x0d: - continue; - } - - tw_write_and_verify(solo_dev, dev_addr, i, - tbl_tw2864_common[i]); - } - - return 0; -} - -static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_ntsc_tw2815_common[] = { - 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80, - 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11, - }; - - u8 tbl_pal_tw2815_common[] = { - 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80, - 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11, - }; - - u8 tbl_tw2815_sfr[] = { - 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */ - 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */ - 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00, - 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */ - 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88, - 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */ - }; - u8 *tbl_tw2815_common; - int i; - int ch; - - tbl_ntsc_tw2815_common[0x06] = 0; - - /* Horizontal Delay Control */ - tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8); - - /* Horizontal Active Control */ - tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2); - - /* Vertical Delay Control */ - tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4); - - /* Vertical Active Control */ - tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5); - - tbl_pal_tw2815_common[0x06] = 0; - - /* Horizontal Delay Control */ - tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8); - - /* Horizontal Active Control */ - tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2); - - /* Vertical Delay Control */ - tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4); - - /* Vertical Active Control */ - tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5); - - tbl_tw2815_common = - (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ? - tbl_ntsc_tw2815_common : tbl_pal_tw2815_common; - - /* Dual ITU-R BT.656 format */ - tbl_tw2815_common[0x0d] |= 0x04; - - /* Audio configuration */ - tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6); - - if (solo_dev->nr_chans == 4) { - tbl_tw2815_sfr[0x63 - 0x40] |= 1; - tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2815_sfr[0x63 - 0x40] |= 2; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2815_sfr[0x63 - 0x40] |= 3; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; - } - - /* Output mode of R_ADATM pin (0 mixing, 1 record) */ - /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */ - - /* 8KHz, used to be 16KHz, but changed for remote client compat */ - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2; - tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2; - - /* Playback of right channel */ - tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5; - - /* Reserved value (XXX ??) */ - tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5; - - /* Analog output gain and mix ratio playback on full */ - tbl_tw2815_sfr[0x70 - 0x40] |= 0xff; - /* Select playback audio and mute all except */ - tbl_tw2815_sfr[0x71 - 0x40] |= 0x10; - tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f; - - /* End of audio configuration */ - - for (ch = 0; ch < 4; ch++) { - tbl_tw2815_common[0x0d] &= ~3; - switch (ch) { - case 0: - tbl_tw2815_common[0x0d] |= 0x21; - break; - case 1: - tbl_tw2815_common[0x0d] |= 0x20; - break; - case 2: - tbl_tw2815_common[0x0d] |= 0x23; - break; - case 3: - tbl_tw2815_common[0x0d] |= 0x22; - break; - } - - for (i = 0; i < 0x0f; i++) { - if (i == 0x00) - continue; /* read-only */ - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - dev_addr, (ch * 0x10) + i, - tbl_tw2815_common[i]); - } - } - - for (i = 0x40; i < 0x76; i++) { - /* Skip read-only and nop registers */ - if (i == 0x40 || i == 0x59 || i == 0x5a || - i == 0x5d || i == 0x5e || i == 0x5f) - continue; - - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i, - tbl_tw2815_sfr[i - 0x40]); - } - - return 0; -} - -#define FIRST_ACTIVE_LINE 0x0008 -#define LAST_ACTIVE_LINE 0x0102 - -static void saa712x_write_regs(struct solo_dev *dev, const uint8_t *vals, - int start, int n) -{ - for (; start < n; start++, vals++) { - /* Skip read-only registers */ - switch (start) { - /* case 0x00 ... 0x25: */ - case 0x2e ... 0x37: - case 0x60: - case 0x7d: - continue; - } - solo_i2c_writebyte(dev, SOLO_I2C_SAA, 0x46, start, *vals); - } -} - -#define SAA712x_reg7c (0x80 | ((LAST_ACTIVE_LINE & 0x100) >> 2) \ - | ((FIRST_ACTIVE_LINE & 0x100) >> 4)) - -static void saa712x_setup(struct solo_dev *dev) -{ - const int reg_start = 0x26; - const uint8_t saa7128_regs_ntsc[] = { - /* :0x26 */ - 0x0d, 0x00, - /* :0x28 */ - 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, - /* :0x2e XXX: read-only */ - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* :0x38 */ - 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - /* :0x40 */ - 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18, - 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f, - /* :0x50 */ - 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06, - 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e, - /* :0x60 */ - 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77, - 0x41, 0x88, 0x41, 0x52, 0xed, 0x10, 0x10, 0x00, - /* :0x70 */ - 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00, - 0x00, 0x00, FIRST_ACTIVE_LINE, LAST_ACTIVE_LINE & 0xff, - SAA712x_reg7c, 0x00, 0xff, 0xff, - }, saa7128_regs_pal[] = { - /* :0x26 */ - 0x0d, 0x00, - /* :0x28 */ - 0xe1, 0x1d, 0x75, 0x3f, 0x06, 0x3f, - /* :0x2e XXX: read-only */ - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* :0x38 */ - 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - /* :0x40 */ - 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18, - 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f, - /* :0x50 */ - 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06, - 0x02, 0x80, 0x0f, 0x77, 0xa7, 0x67, 0x66, 0x2e, - /* :0x60 */ - 0x7b, 0x02, 0x35, 0xcb, 0x8a, 0x09, 0x2a, 0x77, - 0x41, 0x88, 0x41, 0x52, 0xf1, 0x10, 0x20, 0x00, - /* :0x70 */ - 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x12, 0x30, - SAA712x_reg7c | 0x40, 0x00, 0xff, 0xff, - }; - - if (dev->video_type == SOLO_VO_FMT_TYPE_PAL) - saa712x_write_regs(dev, saa7128_regs_pal, reg_start, - sizeof(saa7128_regs_pal)); - else - saa712x_write_regs(dev, saa7128_regs_ntsc, reg_start, - sizeof(saa7128_regs_ntsc)); -} - -int solo_tw28_init(struct solo_dev *solo_dev) -{ - int i; - u8 value; - - solo_dev->tw28_cnt = 0; - - /* Detect techwell chip type(s) */ - for (i = 0; i < solo_dev->nr_chans / 4; i++) { - value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(i), 0xFF); - - switch (value >> 3) { - case 0x18: - solo_dev->tw2865 |= 1 << i; - solo_dev->tw28_cnt++; - break; - case 0x0c: - solo_dev->tw2864 |= 1 << i; - solo_dev->tw28_cnt++; - break; - default: - value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(i), - 0x59); - if ((value >> 3) == 0x04) { - solo_dev->tw2815 |= 1 << i; - solo_dev->tw28_cnt++; - } - } - } - - if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) { - dev_err(&solo_dev->pdev->dev, - "Could not initialize any techwell chips\n"); - return -EINVAL; - } - - saa712x_setup(solo_dev); - - for (i = 0; i < solo_dev->tw28_cnt; i++) { - if ((solo_dev->tw2865 & (1 << i))) - tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - else if ((solo_dev->tw2864 & (1 << i))) - tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - else - tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - } - - return 0; -} - -/* - * We accessed the video status signal in the Techwell chip through - * iic/i2c because the video status reported by register REG_VI_STATUS1 - * (address 0x012C) of the SOLO6010 chip doesn't give the correct video - * status signal values. - */ -int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch) -{ - u8 val, chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR, - TW_AV_STAT_ADDR) & 0x0f; - - return val & (1 << ch) ? 1 : 0; -} - -#if 0 -/* Status of audio from up to 4 techwell chips are combined into 1 variable. - * See techwell datasheet for details. */ -u16 tw28_get_audio_status(struct solo_dev *solo_dev) -{ - u8 val; - u16 status = 0; - int i; - - for (i = 0; i < solo_dev->tw28_cnt; i++) { - val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR, - TW_AV_STAT_ADDR) & 0xf0) >> 4; - status |= val << (i * 4); - } - - return status; -} -#endif - -bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch) -{ - return is_tw286x(solo_dev, ch / 4); -} - -int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, - s32 val) -{ - char sval; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - if (val > 255 || val < 0) - return -ERANGE; - - switch (ctrl) { - case V4L2_CID_SHARPNESS: - /* Only 286x has sharpness */ - if (is_tw286x(solo_dev, chip_num)) { - u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num)); - v &= 0xf0; - v |= val; - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num), v); - } else { - return -EINVAL; - } - break; - - case V4L2_CID_HUE: - if (is_tw286x(solo_dev, chip_num)) - sval = val - 128; - else - sval = (char)val; - tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), - TW_HUE_ADDR(ch), sval); - - break; - - case V4L2_CID_SATURATION: - /* 286x chips have a U and V component for saturation */ - if (is_tw286x(solo_dev, chip_num)) { - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SATURATIONU_ADDR(ch), val); - } - tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch), - TW_SATURATION_ADDR(ch), val); - - break; - - case V4L2_CID_CONTRAST: - tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch), - TW_CONTRAST_ADDR(ch), val); - break; - - case V4L2_CID_BRIGHTNESS: - if (is_tw286x(solo_dev, chip_num)) - sval = val - 128; - else - sval = (char)val; - tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch), - TW_BRIGHTNESS_ADDR(ch), sval); - - break; - default: - return -EINVAL; - } - - return 0; -} - -int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, - s32 *val) -{ - u8 rval, chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - switch (ctrl) { - case V4L2_CID_SHARPNESS: - /* Only 286x has sharpness */ - if (is_tw286x(solo_dev, chip_num)) { - rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num)); - *val = rval & 0x0f; - } else - *val = 0; - break; - case V4L2_CID_HUE: - rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), - TW_HUE_ADDR(ch)); - if (is_tw286x(solo_dev, chip_num)) - *val = (s32)((char)rval) + 128; - else - *val = rval; - break; - case V4L2_CID_SATURATION: - *val = tw_readbyte(solo_dev, chip_num, - TW286x_SATURATIONU_ADDR(ch), - TW_SATURATION_ADDR(ch)); - break; - case V4L2_CID_CONTRAST: - *val = tw_readbyte(solo_dev, chip_num, - TW286x_CONTRAST_ADDR(ch), - TW_CONTRAST_ADDR(ch)); - break; - case V4L2_CID_BRIGHTNESS: - rval = tw_readbyte(solo_dev, chip_num, - TW286x_BRIGHTNESS_ADDR(ch), - TW_BRIGHTNESS_ADDR(ch)); - if (is_tw286x(solo_dev, chip_num)) - *val = (s32)((char)rval) + 128; - else - *val = rval; - break; - default: - return -EINVAL; - } - - return 0; -} - -#if 0 -/* - * For audio output volume, the output channel is only 1. In this case we - * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used - * is the base address of the techwell chip. - */ -void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val) -{ - unsigned int val; - unsigned int chip_num; - - chip_num = (solo_dev->nr_chans - 1) / 4; - - val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, - TW_AUDIO_OUTPUT_VOL_ADDR); - - u_val = (val & 0x0f) | (u_val << 4); - - tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, - TW_AUDIO_OUTPUT_VOL_ADDR, u_val); -} -#endif - -u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch) -{ - u8 val; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - val = tw_readbyte(solo_dev, chip_num, - TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch)); - - return (ch % 2) ? (val >> 4) : (val & 0x0f); -} - -void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val) -{ - u8 old_val; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - old_val = tw_readbyte(solo_dev, chip_num, - TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch)); - - val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) | - ((ch % 2) ? (val << 4) : val); - - tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch), val); -} diff --git a/drivers/staging/media/solo6x10/solo6x10-tw28.h b/drivers/staging/media/solo6x10/solo6x10-tw28.h deleted file mode 100644 index 0966b45..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-tw28.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 __SOLO6X10_TW28_H -#define __SOLO6X10_TW28_H - -#include "solo6x10.h" - -#define TW_NUM_CHIP 4 -#define TW_BASE_ADDR 0x28 -#define TW_CHIP_OFFSET_ADDR(n) (TW_BASE_ADDR + (n)) - -/* tw2815 */ -#define TW_AV_STAT_ADDR 0x5a -#define TW_HUE_ADDR(n) (0x07 | ((n) << 4)) -#define TW_SATURATION_ADDR(n) (0x08 | ((n) << 4)) -#define TW_CONTRAST_ADDR(n) (0x09 | ((n) << 4)) -#define TW_BRIGHTNESS_ADDR(n) (0x0a | ((n) << 4)) -#define TW_AUDIO_OUTPUT_VOL_ADDR 0x70 -#define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0)) - -/* tw286x */ -#define TW286x_AV_STAT_ADDR 0xfd -#define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4)) -#define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4)) -#define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4)) -#define TW286x_CONTRAST_ADDR(n) (0x02 | ((n) << 4)) -#define TW286x_BRIGHTNESS_ADDR(n) (0x01 | ((n) << 4)) -#define TW286x_SHARPNESS(n) (0x03 | ((n) << 4)) -#define TW286x_AUDIO_OUTPUT_VOL_ADDR 0xdf -#define TW286x_AUDIO_INPUT_GAIN_ADDR(n) (0xD0 + ((n > 1) ? 1 : 0)) - -int solo_tw28_init(struct solo_dev *solo_dev); - -int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val); -int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 *val); -bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch); - -u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch); -void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val); -int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch); - -#if 0 -unsigned int tw2815_get_audio_status(struct SOLO *solo); -void tw2815_Set_AudioOutVol(struct SOLO *solo, unsigned int u_val); -#endif - -#endif /* __SOLO6X10_TW28_H */ diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c deleted file mode 100644 index 2e07b49..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ /dev/null @@ -1,1434 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 -#include -#include -#include - -#include -#include -#include -#include - -#include "solo6x10.h" -#include "solo6x10-tw28.h" -#include "solo6x10-jpeg.h" - -#define MIN_VID_BUFFERS 2 -#define FRAME_BUF_SIZE (196 * 1024) -#define MP4_QS 16 -#define DMA_ALIGN 4096 - -/* 6010 M4V */ -static unsigned char vop_6010_ntsc_d1[] = { - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, - 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, - 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, - 0x1f, 0x4c, 0x58, 0x10, 0xf0, 0x71, 0x18, 0x3f, -}; - -static unsigned char vop_6010_ntsc_cif[] = { - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, - 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, - 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, - 0x1f, 0x4c, 0x2c, 0x10, 0x78, 0x51, 0x18, 0x3f, -}; - -static unsigned char vop_6010_pal_d1[] = { - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, - 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, - 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, - 0x1f, 0x4c, 0x58, 0x11, 0x20, 0x71, 0x18, 0x3f, -}; - -static unsigned char vop_6010_pal_cif[] = { - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, - 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, - 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, - 0x1f, 0x4c, 0x2c, 0x10, 0x90, 0x51, 0x18, 0x3f, -}; - -/* 6110 h.264 */ -static unsigned char vop_6110_ntsc_d1[] = { - 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, - 0x9a, 0x74, 0x05, 0x81, 0xec, 0x80, 0x00, 0x00, - 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, -}; - -static unsigned char vop_6110_ntsc_cif[] = { - 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, - 0x9a, 0x74, 0x0b, 0x0f, 0xc8, 0x00, 0x00, 0x00, - 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, -}; - -static unsigned char vop_6110_pal_d1[] = { - 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, - 0x9a, 0x74, 0x05, 0x80, 0x93, 0x20, 0x00, 0x00, - 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, -}; - -static unsigned char vop_6110_pal_cif[] = { - 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, - 0x9a, 0x74, 0x0b, 0x04, 0xb2, 0x00, 0x00, 0x00, - 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, -}; - -typedef __le32 vop_header[16]; - -struct solo_enc_buf { - enum solo_enc_types type; - const vop_header *vh; - int motion; -}; - -static int solo_is_motion_on(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - - return (solo_dev->motion_mask >> solo_enc->ch) & 1; -} - -static int solo_motion_detected(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - unsigned long flags; - u32 ch_mask = 1 << solo_enc->ch; - int ret = 0; - - spin_lock_irqsave(&solo_enc->motion_lock, flags); - if (solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS) & ch_mask) { - solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, ch_mask); - ret = 1; - } - spin_unlock_irqrestore(&solo_enc->motion_lock, flags); - - return ret; -} - -static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - u32 mask = 1 << solo_enc->ch; - unsigned long flags; - - spin_lock_irqsave(&solo_enc->motion_lock, flags); - - if (on) - solo_dev->motion_mask |= mask; - else - solo_dev->motion_mask &= ~mask; - - solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, mask); - - solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, - SOLO_VI_MOTION_EN(solo_dev->motion_mask) | - (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); - - spin_unlock_irqrestore(&solo_enc->motion_lock, flags); -} - -void solo_update_mode(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - int vop_len; - unsigned char *vop; - - solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; - solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); - - if (solo_enc->mode == SOLO_ENC_MODE_CIF) { - solo_enc->width = solo_dev->video_hsize >> 1; - solo_enc->height = solo_dev->video_vsize; - if (solo_dev->type == SOLO_DEV_6110) { - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - vop = vop_6110_ntsc_cif; - vop_len = sizeof(vop_6110_ntsc_cif); - } else { - vop = vop_6110_pal_cif; - vop_len = sizeof(vop_6110_pal_cif); - } - } else { - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - vop = vop_6010_ntsc_cif; - vop_len = sizeof(vop_6010_ntsc_cif); - } else { - vop = vop_6010_pal_cif; - vop_len = sizeof(vop_6010_pal_cif); - } - } - } else { - solo_enc->width = solo_dev->video_hsize; - solo_enc->height = solo_dev->video_vsize << 1; - solo_enc->bw_weight <<= 2; - if (solo_dev->type == SOLO_DEV_6110) { - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - vop = vop_6110_ntsc_d1; - vop_len = sizeof(vop_6110_ntsc_d1); - } else { - vop = vop_6110_pal_d1; - vop_len = sizeof(vop_6110_pal_d1); - } - } else { - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - vop = vop_6010_ntsc_d1; - vop_len = sizeof(vop_6010_ntsc_d1); - } else { - vop = vop_6010_pal_d1; - vop_len = sizeof(vop_6010_pal_d1); - } - } - } - - memcpy(solo_enc->vop, vop, vop_len); - - /* Some fixups for 6010/M4V */ - if (solo_dev->type == SOLO_DEV_6010) { - u16 fps = solo_dev->fps * 1000; - u16 interval = solo_enc->interval * 1000; - - vop = solo_enc->vop; - - /* Frame rate and interval */ - vop[22] = fps >> 4; - vop[23] = ((fps << 4) & 0xf0) | 0x0c - | ((interval >> 13) & 0x3); - vop[24] = (interval >> 5) & 0xff; - vop[25] = ((interval << 3) & 0xf8) | 0x04; - } - - solo_enc->vop_len = vop_len; - - /* Now handle the jpeg header */ - vop = solo_enc->jpeg_header; - vop[SOF0_START + 5] = 0xff & (solo_enc->height >> 8); - vop[SOF0_START + 6] = 0xff & solo_enc->height; - vop[SOF0_START + 7] = 0xff & (solo_enc->width >> 8); - vop[SOF0_START + 8] = 0xff & solo_enc->width; - - memcpy(vop + DQT_START, - jpeg_dqt[solo_g_jpeg_qp(solo_dev, solo_enc->ch)], DQT_LEN); -} - -static int solo_enc_on(struct solo_enc_dev *solo_enc) -{ - u8 ch = solo_enc->ch; - struct solo_dev *solo_dev = solo_enc->solo_dev; - u8 interval; - - solo_update_mode(solo_enc); - - /* Make sure to do a bandwidth check */ - if (solo_enc->bw_weight > solo_dev->enc_bw_remain) - return -EBUSY; - solo_enc->sequence = 0; - solo_enc->motion_last_state = false; - solo_enc->frames_since_last_motion = 0; - solo_dev->enc_bw_remain -= solo_enc->bw_weight; - - if (solo_enc->type == SOLO_ENC_TYPE_EXT) - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1); - - /* Disable all encoding for this channel */ - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0); - - /* Common for both std and ext encoding */ - solo_reg_write(solo_dev, SOLO_VE_CH_INTL(ch), - solo_enc->interlaced ? 1 : 0); - - if (solo_enc->interlaced) - interval = solo_enc->interval - 1; - else - interval = solo_enc->interval; - - /* Standard encoding only */ - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_QP(ch), solo_enc->qp); - solo_reg_write(solo_dev, SOLO_CAP_CH_INTV(ch), interval); - - /* Extended encoding only */ - solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(ch), solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(ch), solo_enc->qp); - solo_reg_write(solo_dev, SOLO_CAP_CH_INTV_E(ch), interval); - - /* Enables the standard encoder */ - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode); - - return 0; -} - -static void solo_enc_off(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - - solo_dev->enc_bw_remain += solo_enc->bw_weight; - - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0); -} - -static int enc_get_mpeg_dma(struct solo_dev *solo_dev, dma_addr_t dma, - unsigned int off, unsigned int size) -{ - int ret; - - if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) - return -EINVAL; - - /* Single shot */ - if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { - return solo_p2m_dma_t(solo_dev, 0, dma, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, size, - 0, 0); - } - - /* Buffer wrap */ - ret = solo_p2m_dma_t(solo_dev, 0, dma, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, - SOLO_MP4E_EXT_SIZE(solo_dev) - off, 0, 0); - - if (!ret) { - ret = solo_p2m_dma_t(solo_dev, 0, - dma + SOLO_MP4E_EXT_SIZE(solo_dev) - off, - SOLO_MP4E_EXT_ADDR(solo_dev), - size + off - SOLO_MP4E_EXT_SIZE(solo_dev), 0, 0); - } - - return ret; -} - -/* Build a descriptor queue out of an SG list and send it to the P2M for - * processing. */ -static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip, - struct sg_table *vbuf, int off, int size, - unsigned int base, unsigned int base_size) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct scatterlist *sg; - int i; - int ret; - - if (WARN_ON_ONCE(size > FRAME_BUF_SIZE)) - return -EINVAL; - - solo_enc->desc_count = 1; - - for_each_sg(vbuf->sgl, sg, vbuf->nents, i) { - struct solo_p2m_desc *desc; - dma_addr_t dma; - int len; - int left = base_size - off; - - desc = &solo_enc->desc_items[solo_enc->desc_count++]; - dma = sg_dma_address(sg); - len = sg_dma_len(sg); - - /* We assume this is smaller than the scatter size */ - BUG_ON(skip >= len); - if (skip) { - len -= skip; - dma += skip; - size -= skip; - skip = 0; - } - - len = min(len, size); - - if (len <= left) { - /* Single descriptor */ - solo_p2m_fill_desc(desc, 0, dma, base + off, - len, 0, 0); - } else { - /* Buffer wrap */ - /* XXX: Do these as separate DMA requests, to avoid - timeout errors triggered by awkwardly sized - descriptors. See - - */ - ret = solo_p2m_dma_t(solo_dev, 0, dma, base + off, - left, 0, 0); - if (ret) - return ret; - - ret = solo_p2m_dma_t(solo_dev, 0, dma + left, base, - len - left, 0, 0); - if (ret) - return ret; - - solo_enc->desc_count--; - } - - size -= len; - if (size <= 0) - break; - - off += len; - if (off >= base_size) - off -= base_size; - - /* Because we may use two descriptors per loop */ - if (solo_enc->desc_count >= (solo_enc->desc_nelts - 1)) { - ret = solo_p2m_dma_desc(solo_dev, solo_enc->desc_items, - solo_enc->desc_dma, - solo_enc->desc_count - 1); - if (ret) - return ret; - solo_enc->desc_count = 1; - } - } - - if (solo_enc->desc_count <= 1) - return 0; - - return solo_p2m_dma_desc(solo_dev, solo_enc->desc_items, - solo_enc->desc_dma, solo_enc->desc_count - 1); -} - -/* Extract values from VOP header - VE_STATUSxx */ -static inline int vop_interlaced(const vop_header *vh) -{ - return (__le32_to_cpu((*vh)[0]) >> 30) & 1; -} - -static inline u8 vop_channel(const vop_header *vh) -{ - return (__le32_to_cpu((*vh)[0]) >> 24) & 0x1F; -} - -static inline u8 vop_type(const vop_header *vh) -{ - return (__le32_to_cpu((*vh)[0]) >> 22) & 3; -} - -static inline u32 vop_mpeg_size(const vop_header *vh) -{ - return __le32_to_cpu((*vh)[0]) & 0xFFFFF; -} - -static inline u8 vop_hsize(const vop_header *vh) -{ - return (__le32_to_cpu((*vh)[1]) >> 8) & 0xFF; -} - -static inline u8 vop_vsize(const vop_header *vh) -{ - return __le32_to_cpu((*vh)[1]) & 0xFF; -} - -static inline u32 vop_mpeg_offset(const vop_header *vh) -{ - return __le32_to_cpu((*vh)[2]); -} - -static inline u32 vop_jpeg_offset(const vop_header *vh) -{ - return __le32_to_cpu((*vh)[3]); -} - -static inline u32 vop_jpeg_size(const vop_header *vh) -{ - return __le32_to_cpu((*vh)[4]) & 0xFFFFF; -} - -static inline u32 vop_sec(const vop_header *vh) -{ - return __le32_to_cpu((*vh)[5]); -} - -static inline u32 vop_usec(const vop_header *vh) -{ - return __le32_to_cpu((*vh)[6]); -} - -static int solo_fill_jpeg(struct solo_enc_dev *solo_enc, - struct vb2_buffer *vb, const vop_header *vh) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0); - int frame_size; - int ret; - - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; - - if (vb2_plane_size(vb, 0) < vop_jpeg_size(vh) + solo_enc->jpeg_len) - return -EIO; - - frame_size = ALIGN(vop_jpeg_size(vh) + solo_enc->jpeg_len, DMA_ALIGN); - vb2_set_plane_payload(vb, 0, vop_jpeg_size(vh) + solo_enc->jpeg_len); - - /* may discard all previous data in vbuf->sgl */ - if (!dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, - DMA_FROM_DEVICE)) - return -ENOMEM; - ret = solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf, - vop_jpeg_offset(vh) - SOLO_JPEG_EXT_ADDR(solo_dev), - frame_size, SOLO_JPEG_EXT_ADDR(solo_dev), - SOLO_JPEG_EXT_SIZE(solo_dev)); - dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, - DMA_FROM_DEVICE); - - /* add the header only after dma_unmap_sg() */ - sg_copy_from_buffer(vbuf->sgl, vbuf->nents, - solo_enc->jpeg_header, solo_enc->jpeg_len); - - return ret; -} - -static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, - struct vb2_buffer *vb, const vop_header *vh) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0); - int frame_off, frame_size; - int skip = 0; - int ret; - - if (vb2_plane_size(vb, 0) < vop_mpeg_size(vh)) - return -EIO; - - /* If this is a key frame, add extra header */ - vb->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | - V4L2_BUF_FLAG_BFRAME); - if (!vop_type(vh)) { - skip = solo_enc->vop_len; - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; - vb2_set_plane_payload(vb, 0, vop_mpeg_size(vh) + - solo_enc->vop_len); - } else { - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; - vb2_set_plane_payload(vb, 0, vop_mpeg_size(vh)); - } - - /* Now get the actual mpeg payload */ - frame_off = (vop_mpeg_offset(vh) - SOLO_MP4E_EXT_ADDR(solo_dev) + - sizeof(*vh)) % SOLO_MP4E_EXT_SIZE(solo_dev); - frame_size = ALIGN(vop_mpeg_size(vh) + skip, DMA_ALIGN); - - /* may discard all previous data in vbuf->sgl */ - if (!dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, - DMA_FROM_DEVICE)) - return -ENOMEM; - ret = solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size, - SOLO_MP4E_EXT_ADDR(solo_dev), - SOLO_MP4E_EXT_SIZE(solo_dev)); - dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents, - DMA_FROM_DEVICE); - - /* add the header only after dma_unmap_sg() */ - if (!vop_type(vh)) - sg_copy_from_buffer(vbuf->sgl, vbuf->nents, - solo_enc->vop, solo_enc->vop_len); - return ret; -} - -static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, - struct vb2_buffer *vb, struct solo_enc_buf *enc_buf) -{ - const vop_header *vh = enc_buf->vh; - int ret; - - switch (solo_enc->fmt) { - case V4L2_PIX_FMT_MPEG4: - case V4L2_PIX_FMT_H264: - ret = solo_fill_mpeg(solo_enc, vb, vh); - break; - default: /* V4L2_PIX_FMT_MJPEG */ - ret = solo_fill_jpeg(solo_enc, vb, vh); - break; - } - - if (!ret) { - bool send_event = false; - - vb->v4l2_buf.sequence = solo_enc->sequence++; - vb->v4l2_buf.timestamp.tv_sec = vop_sec(vh); - vb->v4l2_buf.timestamp.tv_usec = vop_usec(vh); - - /* Check for motion flags */ - if (solo_is_motion_on(solo_enc)) { - /* It takes a few frames for the hardware to detect - * motion. Once it does it clears the motion detection - * register and it takes again a few frames before - * motion is seen. This means in practice that when the - * motion field is 1, it will go back to 0 for the next - * frame. This leads to motion detection event being - * sent all the time, which is not what we want. - * Instead wait a few frames before deciding that the - * motion has halted. After some experimentation it - * turns out that waiting for 5 frames works well. - */ - if (enc_buf->motion == 0 && - solo_enc->motion_last_state && - solo_enc->frames_since_last_motion++ > 5) - send_event = true; - else if (enc_buf->motion) { - solo_enc->frames_since_last_motion = 0; - send_event = !solo_enc->motion_last_state; - } - } - - if (send_event) { - struct v4l2_event ev = { - .type = V4L2_EVENT_MOTION_DET, - .u.motion_det = { - .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, - .frame_sequence = vb->v4l2_buf.sequence, - .region_mask = enc_buf->motion ? 1 : 0, - }, - }; - - solo_enc->motion_last_state = enc_buf->motion; - solo_enc->frames_since_last_motion = 0; - v4l2_event_queue(solo_enc->vfd, &ev); - } - } - - vb2_buffer_done(vb, ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - - return ret; -} - -static void solo_enc_handle_one(struct solo_enc_dev *solo_enc, - struct solo_enc_buf *enc_buf) -{ - struct solo_vb2_buf *vb; - unsigned long flags; - - mutex_lock(&solo_enc->lock); - if (solo_enc->type != enc_buf->type) - goto unlock; - - spin_lock_irqsave(&solo_enc->av_lock, flags); - if (list_empty(&solo_enc->vidq_active)) { - spin_unlock_irqrestore(&solo_enc->av_lock, flags); - goto unlock; - } - vb = list_first_entry(&solo_enc->vidq_active, struct solo_vb2_buf, - list); - list_del(&vb->list); - spin_unlock_irqrestore(&solo_enc->av_lock, flags); - - solo_enc_fillbuf(solo_enc, &vb->vb, enc_buf); -unlock: - mutex_unlock(&solo_enc->lock); -} - -void solo_enc_v4l2_isr(struct solo_dev *solo_dev) -{ - wake_up_interruptible_all(&solo_dev->ring_thread_wait); -} - -static void solo_handle_ring(struct solo_dev *solo_dev) -{ - for (;;) { - struct solo_enc_dev *solo_enc; - struct solo_enc_buf enc_buf; - u32 mpeg_current, off; - u8 ch; - u8 cur_q; - - /* Check if the hardware has any new ones in the queue */ - cur_q = solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xff; - if (cur_q == solo_dev->enc_idx) - break; - - mpeg_current = solo_reg_read(solo_dev, - SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); - solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS; - - ch = (mpeg_current >> 24) & 0x1f; - off = mpeg_current & 0x00ffffff; - - if (ch >= SOLO_MAX_CHANNELS) { - ch -= SOLO_MAX_CHANNELS; - enc_buf.type = SOLO_ENC_TYPE_EXT; - } else - enc_buf.type = SOLO_ENC_TYPE_STD; - - solo_enc = solo_dev->v4l2_enc[ch]; - if (solo_enc == NULL) { - dev_err(&solo_dev->pdev->dev, - "Got spurious packet for channel %d\n", ch); - continue; - } - - /* FAIL... */ - if (enc_get_mpeg_dma(solo_dev, solo_dev->vh_dma, off, - sizeof(vop_header))) - continue; - - enc_buf.vh = solo_dev->vh_buf; - - /* Sanity check */ - if (vop_mpeg_offset(enc_buf.vh) != - SOLO_MP4E_EXT_ADDR(solo_dev) + off) - continue; - - if (solo_motion_detected(solo_enc)) - enc_buf.motion = 1; - else - enc_buf.motion = 0; - - solo_enc_handle_one(solo_enc, &enc_buf); - } -} - -static int solo_ring_thread(void *data) -{ - struct solo_dev *solo_dev = data; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&solo_dev->ring_thread_wait, &wait); - - for (;;) { - long timeout = schedule_timeout_interruptible(HZ); - - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); - solo_handle_ring(solo_dev); - solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); - try_to_freeze(); - } - - remove_wait_queue(&solo_dev->ring_thread_wait, &wait); - - return 0; -} - -static int solo_enc_queue_setup(struct vb2_queue *q, - const struct v4l2_format *fmt, - unsigned int *num_buffers, - unsigned int *num_planes, unsigned int sizes[], - void *alloc_ctxs[]) -{ - sizes[0] = FRAME_BUF_SIZE; - *num_planes = 1; - - if (*num_buffers < MIN_VID_BUFFERS) - *num_buffers = MIN_VID_BUFFERS; - - return 0; -} - -static void solo_enc_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct solo_enc_dev *solo_enc = vb2_get_drv_priv(vq); - struct solo_vb2_buf *solo_vb = - container_of(vb, struct solo_vb2_buf, vb); - - spin_lock(&solo_enc->av_lock); - list_add_tail(&solo_vb->list, &solo_enc->vidq_active); - spin_unlock(&solo_enc->av_lock); -} - -static int solo_ring_start(struct solo_dev *solo_dev) -{ - solo_dev->ring_thread = kthread_run(solo_ring_thread, solo_dev, - SOLO6X10_NAME "_ring"); - if (IS_ERR(solo_dev->ring_thread)) { - int err = PTR_ERR(solo_dev->ring_thread); - - solo_dev->ring_thread = NULL; - return err; - } - - solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); - - return 0; -} - -static void solo_ring_stop(struct solo_dev *solo_dev) -{ - if (solo_dev->ring_thread) { - kthread_stop(solo_dev->ring_thread); - solo_dev->ring_thread = NULL; - } - - solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); -} - -static int solo_enc_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); - int ret; - - ret = solo_enc_on(solo_enc); - if (ret) - return ret; - return solo_ring_start(solo_enc->solo_dev); -} - -static void solo_enc_stop_streaming(struct vb2_queue *q) -{ - struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); - - solo_enc_off(solo_enc); - INIT_LIST_HEAD(&solo_enc->vidq_active); - solo_ring_stop(solo_enc->solo_dev); -} - -static struct vb2_ops solo_enc_video_qops = { - .queue_setup = solo_enc_queue_setup, - .buf_queue = solo_enc_buf_queue, - .start_streaming = solo_enc_start_streaming, - .stop_streaming = solo_enc_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int solo_enc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - - strcpy(cap->driver, SOLO6X10_NAME); - snprintf(cap->card, sizeof(cap->card), "Softlogic 6x10 Enc %d", - solo_enc->ch); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", - pci_name(solo_dev->pdev)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int solo_enc_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (input->index) - return -EINVAL; - - snprintf(input->name, sizeof(input->name), "Encoder %d", - solo_enc->ch + 1); - input->type = V4L2_INPUT_TYPE_CAMERA; - input->std = solo_enc->vfd->tvnorms; - - if (!tw28_get_video_status(solo_dev, solo_enc->ch)) - input->status = V4L2_IN_ST_NO_SIGNAL; - - return 0; -} - -static int solo_enc_set_input(struct file *file, void *priv, - unsigned int index) -{ - if (index) - return -EINVAL; - - return 0; -} - -static int solo_enc_get_input(struct file *file, void *priv, - unsigned int *index) -{ - *index = 0; - - return 0; -} - -static int solo_enc_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - int dev_type = solo_enc->solo_dev->type; - - switch (f->index) { - case 0: - switch (dev_type) { - case SOLO_DEV_6010: - f->pixelformat = V4L2_PIX_FMT_MPEG4; - strcpy(f->description, "MPEG-4 part 2"); - break; - case SOLO_DEV_6110: - f->pixelformat = V4L2_PIX_FMT_H264; - strcpy(f->description, "H.264"); - break; - } - break; - case 1: - f->pixelformat = V4L2_PIX_FMT_MJPEG; - strcpy(f->description, "MJPEG"); - break; - default: - return -EINVAL; - } - - f->flags = V4L2_FMT_FLAG_COMPRESSED; - - return 0; -} - -static inline int solo_valid_pixfmt(u32 pixfmt, int dev_type) -{ - return (pixfmt == V4L2_PIX_FMT_H264 && dev_type == SOLO_DEV_6110) - || (pixfmt == V4L2_PIX_FMT_MPEG4 && dev_type == SOLO_DEV_6010) - || pixfmt == V4L2_PIX_FMT_MJPEG ? 0 : -EINVAL; -} - -static int solo_enc_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - - if (solo_valid_pixfmt(pix->pixelformat, solo_dev->type)) - return -EINVAL; - - if (pix->width < solo_dev->video_hsize || - pix->height < solo_dev->video_vsize << 1) { - /* Default to CIF 1/2 size */ - pix->width = solo_dev->video_hsize >> 1; - pix->height = solo_dev->video_vsize; - } else { - /* Full frame */ - pix->width = solo_dev->video_hsize; - pix->height = solo_dev->video_vsize << 1; - } - - switch (pix->field) { - case V4L2_FIELD_NONE: - case V4L2_FIELD_INTERLACED: - break; - case V4L2_FIELD_ANY: - default: - pix->field = V4L2_FIELD_INTERLACED; - break; - } - - /* Just set these */ - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->sizeimage = FRAME_BUF_SIZE; - pix->bytesperline = 0; - pix->priv = 0; - - return 0; -} - -static int solo_enc_set_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - if (vb2_is_busy(&solo_enc->vidq)) - return -EBUSY; - - ret = solo_enc_try_fmt_cap(file, priv, f); - if (ret) - return ret; - - if (pix->width == solo_dev->video_hsize) - solo_enc->mode = SOLO_ENC_MODE_D1; - else - solo_enc->mode = SOLO_ENC_MODE_CIF; - - /* This does not change the encoder at all */ - solo_enc->fmt = pix->pixelformat; - - /* - * More information is needed about these 'extended' types. As far - * as I can tell these are basically additional video streams with - * different MPEG encoding attributes that can run in parallel with - * the main stream. If so, then this should be implemented as a - * second video node. Abusing priv like this is certainly not the - * right approach. - if (pix->priv) - solo_enc->type = SOLO_ENC_TYPE_EXT; - */ - solo_update_mode(solo_enc); - return 0; -} - -static int solo_enc_get_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct v4l2_pix_format *pix = &f->fmt.pix; - - pix->width = solo_enc->width; - pix->height = solo_enc->height; - pix->pixelformat = solo_enc->fmt; - pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED : - V4L2_FIELD_NONE; - pix->sizeimage = FRAME_BUF_SIZE; - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->priv = 0; - - return 0; -} - -static int solo_enc_g_std(struct file *file, void *priv, v4l2_std_id *i) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - *i = V4L2_STD_NTSC_M; - else - *i = V4L2_STD_PAL; - return 0; -} - -static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - - return solo_set_video_type(solo_enc->solo_dev, std & V4L2_STD_625_50); -} - -static int solo_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (solo_valid_pixfmt(fsize->pixel_format, solo_dev->type)) - return -EINVAL; - - switch (fsize->index) { - case 0: - fsize->discrete.width = solo_dev->video_hsize >> 1; - fsize->discrete.height = solo_dev->video_vsize; - break; - case 1: - fsize->discrete.width = solo_dev->video_hsize; - fsize->discrete.height = solo_dev->video_vsize << 1; - break; - default: - return -EINVAL; - } - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - - return 0; -} - -static int solo_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fintv) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (solo_valid_pixfmt(fintv->pixel_format, solo_dev->type)) - return -EINVAL; - if (fintv->index) - return -EINVAL; - if ((fintv->width != solo_dev->video_hsize >> 1 || - fintv->height != solo_dev->video_vsize) && - (fintv->width != solo_dev->video_hsize || - fintv->height != solo_dev->video_vsize << 1)) - return -EINVAL; - - fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; - - fintv->stepwise.min.numerator = 1; - fintv->stepwise.min.denominator = solo_dev->fps; - - fintv->stepwise.max.numerator = 15; - fintv->stepwise.max.denominator = solo_dev->fps; - - fintv->stepwise.step.numerator = 1; - fintv->stepwise.step.denominator = solo_dev->fps; - - return 0; -} - -static int solo_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct v4l2_captureparm *cp = &sp->parm.capture; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = solo_enc->interval; - cp->timeperframe.denominator = solo_enc->solo_dev->fps; - cp->capturemode = 0; - /* XXX: Shouldn't we be able to get/set this from videobuf? */ - cp->readbuffers = 2; - - return 0; -} - -static inline int calc_interval(u8 fps, u32 n, u32 d) -{ - if (!n || !d) - return 1; - if (d == fps) - return n; - n *= fps; - return min(15U, n / d + (n % d >= (fps >> 1))); -} - -static int solo_s_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct v4l2_fract *t = &sp->parm.capture.timeperframe; - u8 fps = solo_enc->solo_dev->fps; - - if (vb2_is_streaming(&solo_enc->vidq)) - return -EBUSY; - - solo_enc->interval = calc_interval(fps, t->numerator, t->denominator); - solo_update_mode(solo_enc); - return solo_g_parm(file, priv, sp); -} - -static int solo_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct solo_enc_dev *solo_enc = - container_of(ctrl->handler, struct solo_enc_dev, hdl); - struct solo_dev *solo_dev = solo_enc->solo_dev; - int err; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - case V4L2_CID_SHARPNESS: - return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, - ctrl->val); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - solo_enc->gop = ctrl->val; - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(solo_enc->ch), solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(solo_enc->ch), solo_enc->gop); - return 0; - case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: - solo_enc->qp = ctrl->val; - solo_reg_write(solo_dev, SOLO_VE_CH_QP(solo_enc->ch), solo_enc->qp); - solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(solo_enc->ch), solo_enc->qp); - return 0; - case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: - solo_enc->motion_thresh = ctrl->val << 8; - if (!solo_enc->motion_global || !solo_enc->motion_enabled) - return 0; - return solo_set_motion_threshold(solo_dev, solo_enc->ch, - solo_enc->motion_thresh); - case V4L2_CID_DETECT_MD_MODE: - solo_enc->motion_global = ctrl->val == V4L2_DETECT_MD_MODE_GLOBAL; - solo_enc->motion_enabled = ctrl->val > V4L2_DETECT_MD_MODE_DISABLED; - if (ctrl->val) { - if (solo_enc->motion_global) - solo_set_motion_threshold(solo_dev, solo_enc->ch, - solo_enc->motion_thresh); - else - solo_set_motion_block(solo_dev, solo_enc->ch, - solo_enc->md_thresholds->p_cur.p_u16); - } - solo_motion_toggle(solo_enc, ctrl->val); - return 0; - case V4L2_CID_DETECT_MD_THRESHOLD_GRID: - if (solo_enc->motion_enabled && !solo_enc->motion_global) - return solo_set_motion_block(solo_dev, solo_enc->ch, - solo_enc->md_thresholds->p_new.p_u16); - break; - case V4L2_CID_OSD_TEXT: - strcpy(solo_enc->osd_text, ctrl->p_new.p_char); - err = solo_osd_print(solo_enc); - return err; - default: - return -EINVAL; - } - - return 0; -} - -static int solo_subscribe_event(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - - switch (sub->type) { - case V4L2_EVENT_CTRL: - return v4l2_ctrl_subscribe_event(fh, sub); - case V4L2_EVENT_MOTION_DET: - /* Allow for up to 30 events (1 second for NTSC) to be - * stored. */ - return v4l2_event_subscribe(fh, sub, 30, NULL); - } - return -EINVAL; -} - -static const struct v4l2_file_operations solo_enc_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { - .vidioc_querycap = solo_enc_querycap, - .vidioc_s_std = solo_enc_s_std, - .vidioc_g_std = solo_enc_g_std, - /* Input callbacks */ - .vidioc_enum_input = solo_enc_enum_input, - .vidioc_s_input = solo_enc_set_input, - .vidioc_g_input = solo_enc_get_input, - /* Video capture format callbacks */ - .vidioc_enum_fmt_vid_cap = solo_enc_enum_fmt_cap, - .vidioc_try_fmt_vid_cap = solo_enc_try_fmt_cap, - .vidioc_s_fmt_vid_cap = solo_enc_set_fmt_cap, - .vidioc_g_fmt_vid_cap = solo_enc_get_fmt_cap, - /* Streaming I/O */ - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - /* Frame size and interval */ - .vidioc_enum_framesizes = solo_enum_framesizes, - .vidioc_enum_frameintervals = solo_enum_frameintervals, - /* Video capture parameters */ - .vidioc_s_parm = solo_s_parm, - .vidioc_g_parm = solo_g_parm, - /* Logging and events */ - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = solo_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct video_device solo_enc_template = { - .name = SOLO6X10_NAME, - .fops = &solo_enc_fops, - .ioctl_ops = &solo_enc_ioctl_ops, - .minor = -1, - .release = video_device_release, - .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, -}; - -static const struct v4l2_ctrl_ops solo_ctrl_ops = { - .s_ctrl = solo_s_ctrl, -}; - -static const struct v4l2_ctrl_config solo_osd_text_ctrl = { - .ops = &solo_ctrl_ops, - .id = V4L2_CID_OSD_TEXT, - .name = "OSD Text", - .type = V4L2_CTRL_TYPE_STRING, - .max = OSD_TEXT_MAX, - .step = 1, -}; - -/* Motion Detection Threshold matrix */ -static const struct v4l2_ctrl_config solo_md_thresholds = { - .ops = &solo_ctrl_ops, - .id = V4L2_CID_DETECT_MD_THRESHOLD_GRID, - .dims = { SOLO_MOTION_SZ, SOLO_MOTION_SZ }, - .def = SOLO_DEF_MOT_THRESH, - .max = 65535, - .step = 1, -}; - -static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, - u8 ch, unsigned nr) -{ - struct solo_enc_dev *solo_enc; - struct v4l2_ctrl_handler *hdl; - int ret; - - solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL); - if (!solo_enc) - return ERR_PTR(-ENOMEM); - - hdl = &solo_enc->hdl; - v4l2_ctrl_handler_init(hdl, 10); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_HUE, 0, 255, 1, 128); - if (tw28_has_sharpness(solo_dev, ch)) - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 15, 1, 0); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, solo_dev->fps); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 31, 1, SOLO_DEFAULT_QP); - v4l2_ctrl_new_std_menu(hdl, &solo_ctrl_ops, - V4L2_CID_DETECT_MD_MODE, - V4L2_DETECT_MD_MODE_THRESHOLD_GRID, 0, - V4L2_DETECT_MD_MODE_DISABLED); - v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, - V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD, 0, 0xff, 1, - SOLO_DEF_MOT_THRESH >> 8); - v4l2_ctrl_new_custom(hdl, &solo_osd_text_ctrl, NULL); - solo_enc->md_thresholds = - v4l2_ctrl_new_custom(hdl, &solo_md_thresholds, NULL); - if (hdl->error) { - ret = hdl->error; - goto hdl_free; - } - - solo_enc->solo_dev = solo_dev; - solo_enc->ch = ch; - mutex_init(&solo_enc->lock); - spin_lock_init(&solo_enc->av_lock); - INIT_LIST_HEAD(&solo_enc->vidq_active); - solo_enc->fmt = (solo_dev->type == SOLO_DEV_6010) ? - V4L2_PIX_FMT_MPEG4 : V4L2_PIX_FMT_H264; - solo_enc->type = SOLO_ENC_TYPE_STD; - - solo_enc->qp = SOLO_DEFAULT_QP; - solo_enc->gop = solo_dev->fps; - solo_enc->interval = 1; - solo_enc->mode = SOLO_ENC_MODE_CIF; - solo_enc->motion_global = true; - solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; - solo_enc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - solo_enc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - solo_enc->vidq.ops = &solo_enc_video_qops; - solo_enc->vidq.mem_ops = &vb2_dma_sg_memops; - solo_enc->vidq.drv_priv = solo_enc; - solo_enc->vidq.gfp_flags = __GFP_DMA32; - solo_enc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - solo_enc->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); - solo_enc->vidq.lock = &solo_enc->lock; - ret = vb2_queue_init(&solo_enc->vidq); - if (ret) - goto hdl_free; - solo_update_mode(solo_enc); - - spin_lock_init(&solo_enc->motion_lock); - - /* Initialize this per encoder */ - solo_enc->jpeg_len = sizeof(jpeg_header); - memcpy(solo_enc->jpeg_header, jpeg_header, solo_enc->jpeg_len); - - solo_enc->desc_nelts = 32; - solo_enc->desc_items = pci_alloc_consistent(solo_dev->pdev, - sizeof(struct solo_p2m_desc) * - solo_enc->desc_nelts, - &solo_enc->desc_dma); - ret = -ENOMEM; - if (solo_enc->desc_items == NULL) - goto hdl_free; - - solo_enc->vfd = video_device_alloc(); - if (!solo_enc->vfd) - goto pci_free; - - *solo_enc->vfd = solo_enc_template; - solo_enc->vfd->v4l2_dev = &solo_dev->v4l2_dev; - solo_enc->vfd->ctrl_handler = hdl; - solo_enc->vfd->queue = &solo_enc->vidq; - solo_enc->vfd->lock = &solo_enc->lock; - video_set_drvdata(solo_enc->vfd, solo_enc); - ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr); - if (ret < 0) - goto vdev_release; - - snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name), - "%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num, - solo_enc->vfd->num); - - return solo_enc; - -vdev_release: - video_device_release(solo_enc->vfd); -pci_free: - pci_free_consistent(solo_enc->solo_dev->pdev, - sizeof(struct solo_p2m_desc) * solo_enc->desc_nelts, - solo_enc->desc_items, solo_enc->desc_dma); -hdl_free: - v4l2_ctrl_handler_free(hdl); - kfree(solo_enc); - return ERR_PTR(ret); -} - -static void solo_enc_free(struct solo_enc_dev *solo_enc) -{ - if (solo_enc == NULL) - return; - - video_unregister_device(solo_enc->vfd); - v4l2_ctrl_handler_free(&solo_enc->hdl); - kfree(solo_enc); -} - -int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr) -{ - int i; - - init_waitqueue_head(&solo_dev->ring_thread_wait); - - solo_dev->vh_size = sizeof(vop_header); - solo_dev->vh_buf = pci_alloc_consistent(solo_dev->pdev, - solo_dev->vh_size, - &solo_dev->vh_dma); - if (solo_dev->vh_buf == NULL) - return -ENOMEM; - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i, nr); - if (IS_ERR(solo_dev->v4l2_enc[i])) - break; - } - - if (i != solo_dev->nr_chans) { - int ret = PTR_ERR(solo_dev->v4l2_enc[i]); - - while (i--) - solo_enc_free(solo_dev->v4l2_enc[i]); - pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, - solo_dev->vh_buf, solo_dev->vh_dma); - solo_dev->vh_buf = NULL; - return ret; - } - - if (solo_dev->type == SOLO_DEV_6010) - solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4; - else - solo_dev->enc_bw_remain = solo_dev->fps * 4 * 5; - - dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n", - solo_dev->v4l2_enc[0]->vfd->num, - solo_dev->v4l2_enc[solo_dev->nr_chans - 1]->vfd->num); - - return 0; -} - -void solo_enc_v4l2_exit(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) - solo_enc_free(solo_dev->v4l2_enc[i]); - - if (solo_dev->vh_buf) - pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, - solo_dev->vh_buf, solo_dev->vh_dma); -} diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2.c b/drivers/staging/media/solo6x10/solo6x10-v4l2.c deleted file mode 100644 index 63ae8a6..0000000 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2.c +++ /dev/null @@ -1,733 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 -#include -#include -#include - -#include -#include -#include -#include - -#include "solo6x10.h" -#include "solo6x10-tw28.h" - -/* Image size is two fields, SOLO_HW_BPL is one horizontal line in hardware */ -#define SOLO_HW_BPL 2048 -#define solo_vlines(__solo) (__solo->video_vsize * 2) -#define solo_image_size(__solo) (solo_bytesperline(__solo) * \ - solo_vlines(__solo)) -#define solo_bytesperline(__solo) (__solo->video_hsize * 2) - -#define MIN_VID_BUFFERS 2 - -static inline void erase_on(struct solo_dev *solo_dev) -{ - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); - solo_dev->erasing = 1; - solo_dev->frame_blank = 0; -} - -static inline int erase_off(struct solo_dev *solo_dev) -{ - if (!solo_dev->erasing) - return 0; - - /* First time around, assert erase off */ - if (!solo_dev->frame_blank) - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0); - /* Keep the erasing flag on for 8 frames minimum */ - if (solo_dev->frame_blank++ >= 8) - solo_dev->erasing = 0; - - return 1; -} - -void solo_video_in_isr(struct solo_dev *solo_dev) -{ - wake_up_interruptible_all(&solo_dev->disp_thread_wait); -} - -static void solo_win_setup(struct solo_dev *solo_dev, u8 ch, - int sx, int sy, int ex, int ey, int scale) -{ - if (ch >= solo_dev->nr_chans) - return; - - /* Here, we just keep window/channel the same */ - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch), - SOLO_VI_WIN_CHANNEL(ch) | - SOLO_VI_WIN_SX(sx) | - SOLO_VI_WIN_EX(ex) | - SOLO_VI_WIN_SCALE(scale)); - - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch), - SOLO_VI_WIN_SY(sy) | - SOLO_VI_WIN_EY(ey)); -} - -static int solo_v4l2_ch_ext_4up(struct solo_dev *solo_dev, u8 idx, int on) -{ - u8 ch = idx * 4; - - if (ch >= solo_dev->nr_chans) - return -EINVAL; - - if (!on) { - u8 i; - - for (i = ch; i < ch + 4; i++) - solo_win_setup(solo_dev, i, solo_dev->video_hsize, - solo_vlines(solo_dev), - solo_dev->video_hsize, - solo_vlines(solo_dev), 0); - return 0; - } - - /* Row 1 */ - solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2, - solo_vlines(solo_dev) / 2, 3); - solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0, - solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3); - /* Row 2 */ - solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2, - solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3); - solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2, - solo_vlines(solo_dev) / 2, solo_dev->video_hsize, - solo_vlines(solo_dev), 3); - - return 0; -} - -static int solo_v4l2_ch_ext_16up(struct solo_dev *solo_dev, int on) -{ - int sy, ysize, hsize, i; - - if (!on) { - for (i = 0; i < 16; i++) - solo_win_setup(solo_dev, i, solo_dev->video_hsize, - solo_vlines(solo_dev), - solo_dev->video_hsize, - solo_vlines(solo_dev), 0); - return 0; - } - - ysize = solo_vlines(solo_dev) / 4; - hsize = solo_dev->video_hsize / 4; - - for (sy = 0, i = 0; i < 4; i++, sy += ysize) { - solo_win_setup(solo_dev, i * 4, 0, sy, hsize, - sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy, - hsize * 2, sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy, - hsize * 3, sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy, - solo_dev->video_hsize, sy + ysize, 5); - } - - return 0; -} - -static int solo_v4l2_ch(struct solo_dev *solo_dev, u8 ch, int on) -{ - u8 ext_ch; - - if (ch < solo_dev->nr_chans) { - solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize, - on ? 0 : solo_vlines(solo_dev), - solo_dev->video_hsize, solo_vlines(solo_dev), - on ? 1 : 0); - return 0; - } - - if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) - return -EINVAL; - - ext_ch = ch - solo_dev->nr_chans; - - /* 4up's first */ - if (ext_ch < 4) - return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on); - - /* Remaining case is 16up for 16-port */ - return solo_v4l2_ch_ext_16up(solo_dev, on); -} - -static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch) -{ - if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) - return -EINVAL; - - erase_on(solo_dev); - - solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0); - solo_v4l2_ch(solo_dev, ch, 1); - - solo_dev->cur_disp_ch = ch; - - return 0; -} - -static void solo_fillbuf(struct solo_dev *solo_dev, - struct vb2_buffer *vb) -{ - dma_addr_t vbuf; - unsigned int fdma_addr; - int error = -1; - int i; - - vbuf = vb2_dma_contig_plane_dma_addr(vb, 0); - if (!vbuf) - goto finish_buf; - - if (erase_off(solo_dev)) { - void *p = vb2_plane_vaddr(vb, 0); - int image_size = solo_image_size(solo_dev); - - for (i = 0; i < image_size; i += 2) { - ((u8 *)p)[i] = 0x80; - ((u8 *)p)[i + 1] = 0x00; - } - error = 0; - } else { - fdma_addr = SOLO_DISP_EXT_ADDR + (solo_dev->old_write * - (SOLO_HW_BPL * solo_vlines(solo_dev))); - - error = solo_p2m_dma_t(solo_dev, 0, vbuf, fdma_addr, - solo_bytesperline(solo_dev), - solo_vlines(solo_dev), SOLO_HW_BPL); - } - -finish_buf: - if (!error) { - vb2_set_plane_payload(vb, 0, - solo_vlines(solo_dev) * solo_bytesperline(solo_dev)); - vb->v4l2_buf.sequence = solo_dev->sequence++; - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - } - - vb2_buffer_done(vb, error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); -} - -static void solo_thread_try(struct solo_dev *solo_dev) -{ - struct solo_vb2_buf *vb; - - /* Only "break" from this loop if slock is held, otherwise - * just return. */ - for (;;) { - unsigned int cur_write; - - cur_write = SOLO_VI_STATUS0_PAGE( - solo_reg_read(solo_dev, SOLO_VI_STATUS0)); - if (cur_write == solo_dev->old_write) - return; - - spin_lock(&solo_dev->slock); - - if (list_empty(&solo_dev->vidq_active)) - break; - - vb = list_first_entry(&solo_dev->vidq_active, struct solo_vb2_buf, - list); - - solo_dev->old_write = cur_write; - list_del(&vb->list); - - spin_unlock(&solo_dev->slock); - - solo_fillbuf(solo_dev, &vb->vb); - } - - assert_spin_locked(&solo_dev->slock); - spin_unlock(&solo_dev->slock); -} - -static int solo_thread(void *data) -{ - struct solo_dev *solo_dev = data; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&solo_dev->disp_thread_wait, &wait); - - for (;;) { - long timeout = schedule_timeout_interruptible(HZ); - - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - solo_thread_try(solo_dev); - try_to_freeze(); - } - - remove_wait_queue(&solo_dev->disp_thread_wait, &wait); - - return 0; -} - -static int solo_start_thread(struct solo_dev *solo_dev) -{ - int ret = 0; - - solo_dev->kthread = kthread_run(solo_thread, solo_dev, SOLO6X10_NAME "_disp"); - - if (IS_ERR(solo_dev->kthread)) { - ret = PTR_ERR(solo_dev->kthread); - solo_dev->kthread = NULL; - return ret; - } - solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN); - - return ret; -} - -static void solo_stop_thread(struct solo_dev *solo_dev) -{ - if (!solo_dev->kthread) - return; - - solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN); - kthread_stop(solo_dev->kthread); - solo_dev->kthread = NULL; -} - -static int solo_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, - unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct solo_dev *solo_dev = vb2_get_drv_priv(q); - - sizes[0] = solo_image_size(solo_dev); - alloc_ctxs[0] = solo_dev->alloc_ctx; - *num_planes = 1; - - if (*num_buffers < MIN_VID_BUFFERS) - *num_buffers = MIN_VID_BUFFERS; - - return 0; -} - -static int solo_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct solo_dev *solo_dev = vb2_get_drv_priv(q); - - solo_dev->sequence = 0; - return solo_start_thread(solo_dev); -} - -static void solo_stop_streaming(struct vb2_queue *q) -{ - struct solo_dev *solo_dev = vb2_get_drv_priv(q); - - solo_stop_thread(solo_dev); - INIT_LIST_HEAD(&solo_dev->vidq_active); -} - -static void solo_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct solo_dev *solo_dev = vb2_get_drv_priv(vq); - struct solo_vb2_buf *solo_vb = - container_of(vb, struct solo_vb2_buf, vb); - - spin_lock(&solo_dev->slock); - list_add_tail(&solo_vb->list, &solo_dev->vidq_active); - spin_unlock(&solo_dev->slock); - wake_up_interruptible(&solo_dev->disp_thread_wait); -} - -static const struct vb2_ops solo_video_qops = { - .queue_setup = solo_queue_setup, - .buf_queue = solo_buf_queue, - .start_streaming = solo_start_streaming, - .stop_streaming = solo_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int solo_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - strcpy(cap->driver, SOLO6X10_NAME); - strcpy(cap->card, "Softlogic 6x10"); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", - pci_name(solo_dev->pdev)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int solo_enum_ext_input(struct solo_dev *solo_dev, - struct v4l2_input *input) -{ - static const char * const dispnames_1[] = { "4UP" }; - static const char * const dispnames_2[] = { "4UP-1", "4UP-2" }; - static const char * const dispnames_5[] = { - "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" - }; - const char * const *dispnames; - - if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext)) - return -EINVAL; - - if (solo_dev->nr_ext == 5) - dispnames = dispnames_5; - else if (solo_dev->nr_ext == 2) - dispnames = dispnames_2; - else - dispnames = dispnames_1; - - snprintf(input->name, sizeof(input->name), "Multi %s", - dispnames[input->index - solo_dev->nr_chans]); - - return 0; -} - -static int solo_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - if (input->index >= solo_dev->nr_chans) { - int ret = solo_enum_ext_input(solo_dev, input); - - if (ret < 0) - return ret; - } else { - snprintf(input->name, sizeof(input->name), "Camera %d", - input->index + 1); - - /* We can only check this for normal inputs */ - if (!tw28_get_video_status(solo_dev, input->index)) - input->status = V4L2_IN_ST_NO_SIGNAL; - } - - input->type = V4L2_INPUT_TYPE_CAMERA; - input->std = solo_dev->vfd->tvnorms; - return 0; -} - -static int solo_set_input(struct file *file, void *priv, unsigned int index) -{ - struct solo_dev *solo_dev = video_drvdata(file); - int ret = solo_v4l2_set_ch(solo_dev, index); - - if (!ret) { - while (erase_off(solo_dev)) - /* Do nothing */; - } - - return ret; -} - -static int solo_get_input(struct file *file, void *priv, unsigned int *index) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - *index = solo_dev->cur_disp_ch; - - return 0; -} - -static int solo_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index) - return -EINVAL; - - f->pixelformat = V4L2_PIX_FMT_UYVY; - strlcpy(f->description, "UYUV 4:2:2 Packed", sizeof(f->description)); - - return 0; -} - -static int solo_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_dev *solo_dev = video_drvdata(file); - struct v4l2_pix_format *pix = &f->fmt.pix; - int image_size = solo_image_size(solo_dev); - - if (pix->pixelformat != V4L2_PIX_FMT_UYVY) - return -EINVAL; - - pix->width = solo_dev->video_hsize; - pix->height = solo_vlines(solo_dev); - pix->sizeimage = image_size; - pix->field = V4L2_FIELD_INTERLACED; - pix->pixelformat = V4L2_PIX_FMT_UYVY; - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->priv = 0; - return 0; -} - -static int solo_set_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - if (vb2_is_busy(&solo_dev->vidq)) - return -EBUSY; - - /* For right now, if it doesn't match our running config, - * then fail */ - return solo_try_fmt_cap(file, priv, f); -} - -static int solo_get_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_dev *solo_dev = video_drvdata(file); - struct v4l2_pix_format *pix = &f->fmt.pix; - - pix->width = solo_dev->video_hsize; - pix->height = solo_vlines(solo_dev); - pix->pixelformat = V4L2_PIX_FMT_UYVY; - pix->field = V4L2_FIELD_INTERLACED; - pix->sizeimage = solo_image_size(solo_dev); - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->bytesperline = solo_bytesperline(solo_dev); - pix->priv = 0; - - return 0; -} - -static int solo_g_std(struct file *file, void *priv, v4l2_std_id *i) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - *i = V4L2_STD_NTSC_M; - else - *i = V4L2_STD_PAL; - return 0; -} - -int solo_set_video_type(struct solo_dev *solo_dev, bool is_50hz) -{ - int i; - - /* Make sure all video nodes are idle */ - if (vb2_is_busy(&solo_dev->vidq)) - return -EBUSY; - for (i = 0; i < solo_dev->nr_chans; i++) - if (vb2_is_busy(&solo_dev->v4l2_enc[i]->vidq)) - return -EBUSY; - solo_dev->video_type = is_50hz ? SOLO_VO_FMT_TYPE_PAL : - SOLO_VO_FMT_TYPE_NTSC; - /* Reconfigure for the new standard */ - solo_disp_init(solo_dev); - solo_enc_init(solo_dev); - solo_tw28_init(solo_dev); - for (i = 0; i < solo_dev->nr_chans; i++) - solo_update_mode(solo_dev->v4l2_enc[i]); - return solo_v4l2_set_ch(solo_dev, solo_dev->cur_disp_ch); -} - -static int solo_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct solo_dev *solo_dev = video_drvdata(file); - - return solo_set_video_type(solo_dev, std & V4L2_STD_625_50); -} - -static int solo_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct solo_dev *solo_dev = - container_of(ctrl->handler, struct solo_dev, disp_hdl); - - switch (ctrl->id) { - case V4L2_CID_MOTION_TRACE: - if (ctrl->val) { - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, - SOLO_VI_MOTION_Y_ADD | - SOLO_VI_MOTION_Y_VALUE(0x20) | - SOLO_VI_MOTION_CB_VALUE(0x10) | - SOLO_VI_MOTION_CR_VALUE(0x10)); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, - SOLO_VI_MOTION_CR_ADD | - SOLO_VI_MOTION_Y_VALUE(0x10) | - SOLO_VI_MOTION_CB_VALUE(0x80) | - SOLO_VI_MOTION_CR_VALUE(0x10)); - } else { - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); - } - return 0; - default: - break; - } - return -EINVAL; -} - -static const struct v4l2_file_operations solo_v4l2_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = { - .vidioc_querycap = solo_querycap, - .vidioc_s_std = solo_s_std, - .vidioc_g_std = solo_g_std, - /* Input callbacks */ - .vidioc_enum_input = solo_enum_input, - .vidioc_s_input = solo_set_input, - .vidioc_g_input = solo_get_input, - /* Video capture format callbacks */ - .vidioc_enum_fmt_vid_cap = solo_enum_fmt_cap, - .vidioc_try_fmt_vid_cap = solo_try_fmt_cap, - .vidioc_s_fmt_vid_cap = solo_set_fmt_cap, - .vidioc_g_fmt_vid_cap = solo_get_fmt_cap, - /* Streaming I/O */ - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - /* Logging and events */ - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static struct video_device solo_v4l2_template = { - .name = SOLO6X10_NAME, - .fops = &solo_v4l2_fops, - .ioctl_ops = &solo_v4l2_ioctl_ops, - .minor = -1, - .release = video_device_release, - .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, -}; - -static const struct v4l2_ctrl_ops solo_ctrl_ops = { - .s_ctrl = solo_s_ctrl, -}; - -static const struct v4l2_ctrl_config solo_motion_trace_ctrl = { - .ops = &solo_ctrl_ops, - .id = V4L2_CID_MOTION_TRACE, - .name = "Motion Detection Trace", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .step = 1, -}; - -int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr) -{ - int ret; - int i; - - init_waitqueue_head(&solo_dev->disp_thread_wait); - spin_lock_init(&solo_dev->slock); - mutex_init(&solo_dev->lock); - INIT_LIST_HEAD(&solo_dev->vidq_active); - - solo_dev->vfd = video_device_alloc(); - if (!solo_dev->vfd) - return -ENOMEM; - - *solo_dev->vfd = solo_v4l2_template; - solo_dev->vfd->v4l2_dev = &solo_dev->v4l2_dev; - solo_dev->vfd->queue = &solo_dev->vidq; - solo_dev->vfd->lock = &solo_dev->lock; - v4l2_ctrl_handler_init(&solo_dev->disp_hdl, 1); - v4l2_ctrl_new_custom(&solo_dev->disp_hdl, &solo_motion_trace_ctrl, NULL); - if (solo_dev->disp_hdl.error) { - ret = solo_dev->disp_hdl.error; - goto fail; - } - solo_dev->vfd->ctrl_handler = &solo_dev->disp_hdl; - - video_set_drvdata(solo_dev->vfd, solo_dev); - - solo_dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - solo_dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - solo_dev->vidq.ops = &solo_video_qops; - solo_dev->vidq.mem_ops = &vb2_dma_contig_memops; - solo_dev->vidq.drv_priv = solo_dev; - solo_dev->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - solo_dev->vidq.gfp_flags = __GFP_DMA32; - solo_dev->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); - solo_dev->vidq.lock = &solo_dev->lock; - ret = vb2_queue_init(&solo_dev->vidq); - if (ret < 0) - goto fail; - - solo_dev->alloc_ctx = vb2_dma_contig_init_ctx(&solo_dev->pdev->dev); - if (IS_ERR(solo_dev->alloc_ctx)) { - dev_err(&solo_dev->pdev->dev, "Can't allocate buffer context"); - return PTR_ERR(solo_dev->alloc_ctx); - } - - /* Cycle all the channels and clear */ - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_v4l2_set_ch(solo_dev, i); - while (erase_off(solo_dev)) - /* Do nothing */; - } - - /* Set the default display channel */ - solo_v4l2_set_ch(solo_dev, 0); - while (erase_off(solo_dev)) - /* Do nothing */; - - ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr); - if (ret < 0) - goto fail; - - snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)", - SOLO6X10_NAME, solo_dev->vfd->num); - - dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with " - "%d inputs (%d extended)\n", solo_dev->vfd->num, - solo_dev->nr_chans, solo_dev->nr_ext); - - return 0; - -fail: - video_device_release(solo_dev->vfd); - vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); - v4l2_ctrl_handler_free(&solo_dev->disp_hdl); - solo_dev->vfd = NULL; - return ret; -} - -void solo_v4l2_exit(struct solo_dev *solo_dev) -{ - if (solo_dev->vfd == NULL) - return; - - video_unregister_device(solo_dev->vfd); - vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); - v4l2_ctrl_handler_free(&solo_dev->disp_hdl); - solo_dev->vfd = NULL; -} diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h deleted file mode 100644 index c6154b0..0000000 --- a/drivers/staging/media/solo6x10/solo6x10.h +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (C) 2010-2013 Bluecherry, LLC - * - * Original author: - * Ben Collins - * - * Additional work by: - * John Brooks - * - * 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 __SOLO6X10_H -#define __SOLO6X10_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "solo6x10-regs.h" - -#ifndef PCI_VENDOR_ID_SOFTLOGIC -#define PCI_VENDOR_ID_SOFTLOGIC 0x9413 -#define PCI_DEVICE_ID_SOLO6010 0x6010 -#define PCI_DEVICE_ID_SOLO6110 0x6110 -#endif - -#ifndef PCI_VENDOR_ID_BLUECHERRY -#define PCI_VENDOR_ID_BLUECHERRY 0x1BB3 -/* Neugent Softlogic 6010 based cards */ -#define PCI_DEVICE_ID_NEUSOLO_4 0x4304 -#define PCI_DEVICE_ID_NEUSOLO_9 0x4309 -#define PCI_DEVICE_ID_NEUSOLO_16 0x4310 -/* Bluecherry Softlogic 6010 based cards */ -#define PCI_DEVICE_ID_BC_SOLO_4 0x4E04 -#define PCI_DEVICE_ID_BC_SOLO_9 0x4E09 -#define PCI_DEVICE_ID_BC_SOLO_16 0x4E10 -/* Bluecherry Softlogic 6110 based cards */ -#define PCI_DEVICE_ID_BC_6110_4 0x5304 -#define PCI_DEVICE_ID_BC_6110_8 0x5308 -#define PCI_DEVICE_ID_BC_6110_16 0x5310 -#endif /* Bluecherry */ - -/* Used in pci_device_id, and solo_dev->type */ -#define SOLO_DEV_6010 0 -#define SOLO_DEV_6110 1 - -#define SOLO6X10_NAME "solo6x10" - -#define SOLO_MAX_CHANNELS 16 - -#define SOLO6X10_VERSION "3.0.0" - -/* - * The SOLO6x10 actually has 8 i2c channels, but we only use 2. - * 0 - Techwell chip(s) - * 1 - SAA7128 - */ -#define SOLO_I2C_ADAPTERS 2 -#define SOLO_I2C_TW 0 -#define SOLO_I2C_SAA 1 - -/* DMA Engine setup */ -#define SOLO_NR_P2M 4 -#define SOLO_NR_P2M_DESC 256 -#define SOLO_P2M_DESC_SIZE (SOLO_NR_P2M_DESC * 16) - -/* Encoder standard modes */ -#define SOLO_ENC_MODE_CIF 2 -#define SOLO_ENC_MODE_HD1 1 -#define SOLO_ENC_MODE_D1 9 - -#define SOLO_DEFAULT_QP 3 - -#define SOLO_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) -#define V4L2_CID_MOTION_TRACE (SOLO_CID_CUSTOM_BASE+2) -#define V4L2_CID_OSD_TEXT (SOLO_CID_CUSTOM_BASE+3) - -/* - * Motion thresholds are in a table of 64x64 samples, with - * each sample representing 16x16 pixels of the source. In - * effect, 44x30 samples are used for NTSC, and 44x36 for PAL. - * The 5th sample on the 10th row is (10*64)+5 = 645. - * - * Internally it is stored as a 45x45 array (45*16 = 720, which is the - * maximum PAL/NTSC width). - */ -#define SOLO_MOTION_SZ (45) - -enum SOLO_I2C_STATE { - IIC_STATE_IDLE, - IIC_STATE_START, - IIC_STATE_READ, - IIC_STATE_WRITE, - IIC_STATE_STOP -}; - -/* Defined in Table 4-16, Page 68-69 of the 6010 Datasheet */ -struct solo_p2m_desc { - u32 ctrl; - u32 cfg; - u32 dma_addr; - u32 ext_addr; -}; - -struct solo_p2m_dev { - struct mutex mutex; - struct completion completion; - int desc_count; - int desc_idx; - struct solo_p2m_desc *descs; - int error; -}; - -#define OSD_TEXT_MAX 44 - -struct solo_vb2_buf { - struct vb2_buffer vb; - struct list_head list; -}; - -enum solo_enc_types { - SOLO_ENC_TYPE_STD, - SOLO_ENC_TYPE_EXT, -}; - -struct solo_enc_dev { - struct solo_dev *solo_dev; - /* V4L2 Items */ - struct v4l2_ctrl_handler hdl; - struct v4l2_ctrl *md_thresholds; - struct video_device *vfd; - /* General accounting */ - struct mutex lock; - spinlock_t motion_lock; - u8 ch; - u8 mode, gop, qp, interlaced, interval; - u8 bw_weight; - u16 motion_thresh; - bool motion_global; - bool motion_enabled; - bool motion_last_state; - u8 frames_since_last_motion; - u16 width; - u16 height; - - /* OSD buffers */ - char osd_text[OSD_TEXT_MAX + 1]; - u8 osd_buf[SOLO_EOSD_EXT_SIZE_MAX] - __aligned(4); - - /* VOP stuff */ - unsigned char vop[64]; - int vop_len; - unsigned char jpeg_header[1024]; - int jpeg_len; - - u32 fmt; - enum solo_enc_types type; - u32 sequence; - struct vb2_queue vidq; - struct list_head vidq_active; - int desc_count; - int desc_nelts; - struct solo_p2m_desc *desc_items; - dma_addr_t desc_dma; - spinlock_t av_lock; -}; - -/* The SOLO6x10 PCI Device */ -struct solo_dev { - /* General stuff */ - struct pci_dev *pdev; - int type; - unsigned int time_sync; - unsigned int usec_lsb; - unsigned int clock_mhz; - u8 __iomem *reg_base; - int nr_chans; - int nr_ext; - u32 irq_mask; - u32 motion_mask; - spinlock_t reg_io_lock; - struct v4l2_device v4l2_dev; - - /* tw28xx accounting */ - u8 tw2865, tw2864, tw2815; - u8 tw28_cnt; - - /* i2c related items */ - struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS]; - enum SOLO_I2C_STATE i2c_state; - struct mutex i2c_mutex; - int i2c_id; - wait_queue_head_t i2c_wait; - struct i2c_msg *i2c_msg; - unsigned int i2c_msg_num; - unsigned int i2c_msg_ptr; - - /* P2M DMA Engine */ - struct solo_p2m_dev p2m_dev[SOLO_NR_P2M]; - atomic_t p2m_count; - int p2m_jiffies; - unsigned int p2m_timeouts; - - /* V4L2 Display items */ - struct video_device *vfd; - unsigned int erasing; - unsigned int frame_blank; - u8 cur_disp_ch; - wait_queue_head_t disp_thread_wait; - struct v4l2_ctrl_handler disp_hdl; - - /* V4L2 Encoder items */ - struct solo_enc_dev *v4l2_enc[SOLO_MAX_CHANNELS]; - u16 enc_bw_remain; - /* IDX into hw mp4 encoder */ - u8 enc_idx; - - /* Current video settings */ - u32 video_type; - u16 video_hsize, video_vsize; - u16 vout_hstart, vout_vstart; - u16 vin_hstart, vin_vstart; - u8 fps; - - /* JPEG Qp setting */ - spinlock_t jpeg_qp_lock; - u32 jpeg_qp[2]; - - /* Audio components */ - struct snd_card *snd_card; - struct snd_pcm *snd_pcm; - atomic_t snd_users; - int g723_hw_idx; - - /* sysfs stuffs */ - struct device dev; - int sdram_size; - struct bin_attribute sdram_attr; - unsigned int sys_config; - - /* Ring thread */ - struct task_struct *ring_thread; - wait_queue_head_t ring_thread_wait; - - /* VOP_HEADER handling */ - void *vh_buf; - dma_addr_t vh_dma; - int vh_size; - - /* Buffer handling */ - struct vb2_queue vidq; - struct vb2_alloc_ctx *alloc_ctx; - u32 sequence; - struct task_struct *kthread; - struct mutex lock; - spinlock_t slock; - int old_write; - struct list_head vidq_active; -}; - -static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg) -{ - unsigned long flags; - u32 ret; - u16 val; - - spin_lock_irqsave(&solo_dev->reg_io_lock, flags); - - ret = readl(solo_dev->reg_base + reg); - rmb(); - pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); - rmb(); - - spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); - - return ret; -} - -static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, - u32 data) -{ - unsigned long flags; - u16 val; - - spin_lock_irqsave(&solo_dev->reg_io_lock, flags); - - writel(data, solo_dev->reg_base + reg); - wmb(); - pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); - rmb(); - - spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); -} - -static inline void solo_irq_on(struct solo_dev *dev, u32 mask) -{ - dev->irq_mask |= mask; - solo_reg_write(dev, SOLO_IRQ_MASK, dev->irq_mask); -} - -static inline void solo_irq_off(struct solo_dev *dev, u32 mask) -{ - dev->irq_mask &= ~mask; - solo_reg_write(dev, SOLO_IRQ_MASK, dev->irq_mask); -} - -/* Init/exit routines for subsystems */ -int solo_disp_init(struct solo_dev *solo_dev); -void solo_disp_exit(struct solo_dev *solo_dev); - -int solo_gpio_init(struct solo_dev *solo_dev); -void solo_gpio_exit(struct solo_dev *solo_dev); - -int solo_i2c_init(struct solo_dev *solo_dev); -void solo_i2c_exit(struct solo_dev *solo_dev); - -int solo_p2m_init(struct solo_dev *solo_dev); -void solo_p2m_exit(struct solo_dev *solo_dev); - -int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr); -void solo_v4l2_exit(struct solo_dev *solo_dev); - -int solo_enc_init(struct solo_dev *solo_dev); -void solo_enc_exit(struct solo_dev *solo_dev); - -int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr); -void solo_enc_v4l2_exit(struct solo_dev *solo_dev); - -int solo_g723_init(struct solo_dev *solo_dev); -void solo_g723_exit(struct solo_dev *solo_dev); - -/* ISR's */ -int solo_i2c_isr(struct solo_dev *solo_dev); -void solo_p2m_isr(struct solo_dev *solo_dev, int id); -void solo_p2m_error_isr(struct solo_dev *solo_dev); -void solo_enc_v4l2_isr(struct solo_dev *solo_dev); -void solo_g723_isr(struct solo_dev *solo_dev); -void solo_motion_isr(struct solo_dev *solo_dev); -void solo_video_in_isr(struct solo_dev *solo_dev); - -/* i2c read/write */ -u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off); -void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off, - u8 data); - -/* P2M DMA */ -int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size, - int repeat, u32 ext_size); -int solo_p2m_dma(struct solo_dev *solo_dev, int wr, - void *sys_addr, u32 ext_addr, u32 size, - int repeat, u32 ext_size); -void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size, - int repeat, u32 ext_size); -int solo_p2m_dma_desc(struct solo_dev *solo_dev, - struct solo_p2m_desc *desc, dma_addr_t desc_dma, - int desc_cnt); - -/* Global s_std ioctl */ -int solo_set_video_type(struct solo_dev *solo_dev, bool is_50hz); -void solo_update_mode(struct solo_enc_dev *solo_enc); - -/* Set the threshold for motion detection */ -int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val); -int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, - const u16 *thresholds); -#define SOLO_DEF_MOT_THRESH 0x0300 - -/* Write text on OSD */ -int solo_osd_print(struct solo_enc_dev *solo_enc); - -/* EEPROM commands */ -unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en); -unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc); -int solo_eeprom_write(struct solo_dev *solo_dev, int loc, - unsigned short data); - -/* JPEG Qp functions */ -void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch, - unsigned int qp); -int solo_g_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch); - -#define CHK_FLAGS(v, flags) (((v) & (flags)) == (flags)) - -#endif /* __SOLO6X10_H */ -- cgit v0.10.2 From 7955f03d18d14d18188f94581a4ea336c94b1e2d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Jul 2014 06:21:37 +0200 Subject: [media] go7007: move out of staging into drivers/media/usb. Now that the custom motion detection API in this driver has been replaced with a standard API there is no reason anymore to keep it in staging. So (finally!) move it to drivers/media/usb. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index fa67519..94d51e0 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -27,6 +27,7 @@ source "drivers/media/usb/hdpvr/Kconfig" source "drivers/media/usb/tlg2300/Kconfig" source "drivers/media/usb/usbvision/Kconfig" source "drivers/media/usb/stk1160/Kconfig" +source "drivers/media/usb/go7007/Kconfig" endif if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 712a6b1..f438eff 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBTV) += usbtv/ +obj-$(CONFIG_VIDEO_GO7007) += go7007/ diff --git a/drivers/media/usb/go7007/Kconfig b/drivers/media/usb/go7007/Kconfig new file mode 100644 index 0000000..95a3af6 --- /dev/null +++ b/drivers/media/usb/go7007/Kconfig @@ -0,0 +1,51 @@ +config VIDEO_GO7007 + tristate "WIS GO7007 MPEG encoder support" + depends on VIDEO_DEV && I2C + depends on SND && USB + select VIDEOBUF2_VMALLOC + select VIDEO_TUNER + select CYPRESS_FIRMWARE + select SND_PCM + select VIDEO_SONY_BTF_MPX if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TW2804 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TW9903 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TW9906 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_OV7640 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_UDA1342 if MEDIA_SUBDRV_AUTOSELECT + ---help--- + This is a video4linux driver for the WIS GO7007 MPEG + encoder chip. + + To compile this driver as a module, choose M here: the + module will be called go7007. + +config VIDEO_GO7007_USB + tristate "WIS GO7007 USB support" + depends on VIDEO_GO7007 && USB + ---help--- + This is a video4linux driver for the WIS GO7007 MPEG + encoder chip over USB. + + To compile this driver as a module, choose M here: the + module will be called go7007-usb. + +config VIDEO_GO7007_LOADER + tristate "WIS GO7007 Loader support" + depends on VIDEO_GO7007 + default y + ---help--- + This is a go7007 firmware loader driver for the WIS GO7007 + MPEG encoder chip over USB. + + To compile this driver as a module, choose M here: the + module will be called go7007-loader. + +config VIDEO_GO7007_USB_S2250_BOARD + tristate "Sensoray 2250/2251 support" + depends on VIDEO_GO7007_USB && USB + ---help--- + This is a video4linux driver for the Sensoray 2250/2251 device. + + To compile this driver as a module, choose M here: the + module will be called s2250. diff --git a/drivers/media/usb/go7007/Makefile b/drivers/media/usb/go7007/Makefile new file mode 100644 index 0000000..e99287c --- /dev/null +++ b/drivers/media/usb/go7007/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_VIDEO_GO7007) += go7007.o +obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o +obj-$(CONFIG_VIDEO_GO7007_LOADER) += go7007-loader.o +obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o + +go7007-y := go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \ + snd-go7007.o + +s2250-y := s2250-board.o + +ccflags-$(CONFIG_VIDEO_GO7007_LOADER:m=y) += -Idrivers/media/common diff --git a/drivers/media/usb/go7007/go7007-driver.c b/drivers/media/usb/go7007/go7007-driver.c new file mode 100644 index 0000000..95cffb7 --- /dev/null +++ b/drivers/media/usb/go7007/go7007-driver.c @@ -0,0 +1,766 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007-priv.h" + +/* + * Wait for an interrupt to be delivered from the GO7007SB and return + * the associated value and data. + * + * Must be called with the hw_lock held. + */ +int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data) +{ + go->interrupt_available = 0; + go->hpi_ops->read_interrupt(go); + if (wait_event_timeout(go->interrupt_waitq, + go->interrupt_available, 5*HZ) < 0) { + v4l2_err(&go->v4l2_dev, "timeout waiting for read interrupt\n"); + return -1; + } + if (!go->interrupt_available) + return -1; + go->interrupt_available = 0; + *value = go->interrupt_value & 0xfffe; + *data = go->interrupt_data; + return 0; +} +EXPORT_SYMBOL(go7007_read_interrupt); + +/* + * Read a register/address on the GO7007SB. + * + * Must be called with the hw_lock held. + */ +int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data) +{ + int count = 100; + u16 value; + + if (go7007_write_interrupt(go, 0x0010, addr) < 0) + return -EIO; + while (count-- > 0) { + if (go7007_read_interrupt(go, &value, data) == 0 && + value == 0xa000) + return 0; + } + return -EIO; +} +EXPORT_SYMBOL(go7007_read_addr); + +/* + * Send the boot firmware to the encoder, which just wakes it up and lets + * us talk to the GPIO pins and on-board I2C adapter. + * + * Must be called with the hw_lock held. + */ +static int go7007_load_encoder(struct go7007 *go) +{ + const struct firmware *fw_entry; + char fw_name[] = "go7007/go7007fw.bin"; + void *bounce; + int fw_len, rv = 0; + u16 intr_val, intr_data; + + if (go->boot_fw == NULL) { + if (request_firmware(&fw_entry, fw_name, go->dev)) { + v4l2_err(go, "unable to load firmware from file \"%s\"\n", fw_name); + return -1; + } + if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) { + v4l2_err(go, "file \"%s\" does not appear to be go7007 firmware\n", fw_name); + release_firmware(fw_entry); + return -1; + } + fw_len = fw_entry->size - 16; + bounce = kmemdup(fw_entry->data + 16, fw_len, GFP_KERNEL); + if (bounce == NULL) { + v4l2_err(go, "unable to allocate %d bytes for firmware transfer\n", fw_len); + release_firmware(fw_entry); + return -1; + } + release_firmware(fw_entry); + go->boot_fw_len = fw_len; + go->boot_fw = bounce; + } + if (go7007_interface_reset(go) < 0 || + go7007_send_firmware(go, go->boot_fw, go->boot_fw_len) < 0 || + go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || + (intr_val & ~0x1) != 0x5a5a) { + v4l2_err(go, "error transferring firmware\n"); + rv = -1; + } + return rv; +} + +MODULE_FIRMWARE("go7007/go7007fw.bin"); + +/* + * Boot the encoder and register the I2C adapter if requested. Do the + * minimum initialization necessary, since the board-specific code may + * still need to probe the board ID. + * + * Must NOT be called with the hw_lock held. + */ +int go7007_boot_encoder(struct go7007 *go, int init_i2c) +{ + int ret; + + mutex_lock(&go->hw_lock); + ret = go7007_load_encoder(go); + mutex_unlock(&go->hw_lock); + if (ret < 0) + return -1; + if (!init_i2c) + return 0; + if (go7007_i2c_init(go) < 0) + return -1; + go->i2c_adapter_online = 1; + return 0; +} +EXPORT_SYMBOL(go7007_boot_encoder); + +/* + * Configure any hardware-related registers in the GO7007, such as GPIO + * pins and bus parameters, which are board-specific. This assumes + * the boot firmware has already been downloaded. + * + * Must be called with the hw_lock held. + */ +static int go7007_init_encoder(struct go7007 *go) +{ + if (go->board_info->audio_flags & GO7007_AUDIO_I2S_MASTER) { + go7007_write_addr(go, 0x1000, 0x0811); + go7007_write_addr(go, 0x1000, 0x0c11); + } + switch (go->board_id) { + case GO7007_BOARDID_MATRIX_REV: + /* Set GPIO pin 0 to be an output (audio clock control) */ + go7007_write_addr(go, 0x3c82, 0x0001); + go7007_write_addr(go, 0x3c80, 0x00fe); + break; + case GO7007_BOARDID_ADLINK_MPG24: + /* set GPIO5 to be an output, currently low */ + go7007_write_addr(go, 0x3c82, 0x0000); + go7007_write_addr(go, 0x3c80, 0x00df); + break; + case GO7007_BOARDID_ADS_USBAV_709: + /* GPIO pin 0: audio clock control */ + /* pin 2: TW9906 reset */ + /* pin 3: capture LED */ + go7007_write_addr(go, 0x3c82, 0x000d); + go7007_write_addr(go, 0x3c80, 0x00f2); + break; + } + return 0; +} + +/* + * Send the boot firmware to the GO7007 and configure the registers. This + * is the only way to stop the encoder once it has started streaming video. + * + * Must be called with the hw_lock held. + */ +int go7007_reset_encoder(struct go7007 *go) +{ + if (go7007_load_encoder(go) < 0) + return -1; + return go7007_init_encoder(go); +} + +/* + * Attempt to instantiate an I2C client by ID, probably loading a module. + */ +static int init_i2c_module(struct i2c_adapter *adapter, const struct go_i2c *const i2c) +{ + struct go7007 *go = i2c_get_adapdata(adapter); + struct v4l2_device *v4l2_dev = &go->v4l2_dev; + struct v4l2_subdev *sd; + struct i2c_board_info info; + + memset(&info, 0, sizeof(info)); + strlcpy(info.type, i2c->type, sizeof(info.type)); + info.addr = i2c->addr; + info.flags = i2c->flags; + + sd = v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, NULL); + if (sd) { + if (i2c->is_video) + go->sd_video = sd; + if (i2c->is_audio) + go->sd_audio = sd; + return 0; + } + + pr_info("go7007: probing for module i2c:%s failed\n", i2c->type); + return -EINVAL; +} + +/* + * Detach and unregister the encoder. The go7007 struct won't be freed + * until v4l2 finishes releasing its resources and all associated fds are + * closed by applications. + */ +static void go7007_remove(struct v4l2_device *v4l2_dev) +{ + struct go7007 *go = container_of(v4l2_dev, struct go7007, v4l2_dev); + + v4l2_device_unregister(v4l2_dev); + if (go->hpi_ops->release) + go->hpi_ops->release(go); + if (go->i2c_adapter_online) { + i2c_del_adapter(&go->i2c_adapter); + go->i2c_adapter_online = 0; + } + + kfree(go->boot_fw); + go7007_v4l2_remove(go); + kfree(go); +} + +/* + * Finalize the GO7007 hardware setup, register the on-board I2C adapter + * (if used on this board), load the I2C client driver for the sensor + * (SAA7115 or whatever) and other devices, and register the ALSA and V4L2 + * interfaces. + * + * Must NOT be called with the hw_lock held. + */ +int go7007_register_encoder(struct go7007 *go, unsigned num_i2c_devs) +{ + int i, ret; + + dev_info(go->dev, "go7007: registering new %s\n", go->name); + + go->v4l2_dev.release = go7007_remove; + ret = v4l2_device_register(go->dev, &go->v4l2_dev); + if (ret < 0) + return ret; + + mutex_lock(&go->hw_lock); + ret = go7007_init_encoder(go); + mutex_unlock(&go->hw_lock); + if (ret < 0) + return ret; + + ret = go7007_v4l2_ctrl_init(go); + if (ret < 0) + return ret; + + if (!go->i2c_adapter_online && + go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) { + ret = go7007_i2c_init(go); + if (ret < 0) + return ret; + go->i2c_adapter_online = 1; + } + if (go->i2c_adapter_online) { + if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) { + /* Reset the TW9906 */ + go7007_write_addr(go, 0x3c82, 0x0009); + msleep(50); + go7007_write_addr(go, 0x3c82, 0x000d); + } + for (i = 0; i < num_i2c_devs; ++i) + init_i2c_module(&go->i2c_adapter, &go->board_info->i2c_devs[i]); + + if (go->tuner_type >= 0) { + struct tuner_setup setup = { + .addr = ADDR_UNSET, + .type = go->tuner_type, + .mode_mask = T_ANALOG_TV, + }; + + v4l2_device_call_all(&go->v4l2_dev, 0, tuner, + s_type_addr, &setup); + } + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) + v4l2_subdev_call(go->sd_video, video, s_routing, + 0, 0, go->channel_number + 1); + } + + ret = go7007_v4l2_init(go); + if (ret < 0) + return ret; + + if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) { + go->audio_enabled = 1; + go7007_snd_init(go); + } + return 0; +} +EXPORT_SYMBOL(go7007_register_encoder); + +/* + * Send the encode firmware to the encoder, which will cause it + * to immediately start delivering the video and audio streams. + * + * Must be called with the hw_lock held. + */ +int go7007_start_encoder(struct go7007 *go) +{ + u8 *fw; + int fw_len, rv = 0, i, x, y; + u16 intr_val, intr_data; + + go->modet_enable = 0; + for (i = 0; i < 4; i++) + go->modet[i].enable = 0; + + switch (v4l2_ctrl_g_ctrl(go->modet_mode)) { + case V4L2_DETECT_MD_MODE_GLOBAL: + memset(go->modet_map, 0, sizeof(go->modet_map)); + go->modet[0].enable = 1; + go->modet_enable = 1; + break; + case V4L2_DETECT_MD_MODE_REGION_GRID: + for (y = 0; y < go->height / 16; y++) { + for (x = 0; x < go->width / 16; x++) { + int idx = y * go->width / 16 + x; + + go->modet[go->modet_map[idx]].enable = 1; + } + } + go->modet_enable = 1; + break; + } + + if (go->dvd_mode) + go->modet_enable = 0; + + if (go7007_construct_fw_image(go, &fw, &fw_len) < 0) + return -1; + + if (go7007_send_firmware(go, fw, fw_len) < 0 || + go7007_read_interrupt(go, &intr_val, &intr_data) < 0) { + v4l2_err(&go->v4l2_dev, "error transferring firmware\n"); + rv = -1; + goto start_error; + } + + go->state = STATE_DATA; + go->parse_length = 0; + go->seen_frame = 0; + if (go7007_stream_start(go) < 0) { + v4l2_err(&go->v4l2_dev, "error starting stream transfer\n"); + rv = -1; + goto start_error; + } + +start_error: + kfree(fw); + return rv; +} + +/* + * Store a byte in the current video buffer, if there is one. + */ +static inline void store_byte(struct go7007_buffer *vb, u8 byte) +{ + if (vb && vb->vb.v4l2_planes[0].bytesused < GO7007_BUF_SIZE) { + u8 *ptr = vb2_plane_vaddr(&vb->vb, 0); + + ptr[vb->vb.v4l2_planes[0].bytesused++] = byte; + } +} + +static void go7007_set_motion_regions(struct go7007 *go, struct go7007_buffer *vb, + u32 motion_regions) +{ + if (motion_regions != go->modet_event_status) { + struct v4l2_event ev = { + .type = V4L2_EVENT_MOTION_DET, + .u.motion_det = { + .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, + .frame_sequence = vb->vb.v4l2_buf.sequence, + .region_mask = motion_regions, + }, + }; + + v4l2_event_queue(&go->vdev, &ev); + go->modet_event_status = motion_regions; + } +} + +/* + * Determine regions with motion and send a motion detection event + * in case of changes. + */ +static void go7007_motion_regions(struct go7007 *go, struct go7007_buffer *vb) +{ + u32 *bytesused = &vb->vb.v4l2_planes[0].bytesused; + unsigned motion[4] = { 0, 0, 0, 0 }; + u32 motion_regions = 0; + unsigned stride = (go->width + 7) >> 3; + unsigned x, y; + int i; + + for (i = 0; i < 216; ++i) + store_byte(vb, go->active_map[i]); + for (y = 0; y < go->height / 16; y++) { + for (x = 0; x < go->width / 16; x++) { + if (!(go->active_map[y * stride + (x >> 3)] & (1 << (x & 7)))) + continue; + motion[go->modet_map[y * (go->width / 16) + x]]++; + } + } + motion_regions = ((motion[0] > 0) << 0) | + ((motion[1] > 0) << 1) | + ((motion[2] > 0) << 2) | + ((motion[3] > 0) << 3); + *bytesused -= 216; + go7007_set_motion_regions(go, vb, motion_regions); +} + +/* + * Deliver the last video buffer and get a new one to start writing to. + */ +static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buffer *vb) +{ + u32 *bytesused = &vb->vb.v4l2_planes[0].bytesused; + struct go7007_buffer *vb_tmp = NULL; + + if (vb == NULL) { + spin_lock(&go->spinlock); + if (!list_empty(&go->vidq_active)) + vb = go->active_buf = + list_first_entry(&go->vidq_active, struct go7007_buffer, list); + spin_unlock(&go->spinlock); + go->next_seq++; + return vb; + } + + vb->vb.v4l2_buf.sequence = go->next_seq++; + if (vb->modet_active && *bytesused + 216 < GO7007_BUF_SIZE) + go7007_motion_regions(go, vb); + else + go7007_set_motion_regions(go, vb, 0); + + v4l2_get_timestamp(&vb->vb.v4l2_buf.timestamp); + vb_tmp = vb; + spin_lock(&go->spinlock); + list_del(&vb->list); + if (list_empty(&go->vidq_active)) + vb = NULL; + else + vb = list_first_entry(&go->vidq_active, struct go7007_buffer, list); + go->active_buf = vb; + spin_unlock(&go->spinlock); + vb2_buffer_done(&vb_tmp->vb, VB2_BUF_STATE_DONE); + return vb; +} + +static void write_bitmap_word(struct go7007 *go) +{ + int x, y, i, stride = ((go->width >> 4) + 7) >> 3; + + for (i = 0; i < 16; ++i) { + y = (((go->parse_length - 1) << 3) + i) / (go->width >> 4); + x = (((go->parse_length - 1) << 3) + i) % (go->width >> 4); + if (stride * y + (x >> 3) < sizeof(go->active_map)) + go->active_map[stride * y + (x >> 3)] |= + (go->modet_word & 1) << (x & 0x7); + go->modet_word >>= 1; + } +} + +/* + * Parse a chunk of the video stream into frames. The frames are not + * delimited by the hardware, so we have to parse the frame boundaries + * based on the type of video stream we're receiving. + */ +void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) +{ + struct go7007_buffer *vb = go->active_buf; + int i, seq_start_code = -1, gop_start_code = -1, frame_start_code = -1; + + switch (go->format) { + case V4L2_PIX_FMT_MPEG4: + seq_start_code = 0xB0; + gop_start_code = 0xB3; + frame_start_code = 0xB6; + break; + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: + seq_start_code = 0xB3; + gop_start_code = 0xB8; + frame_start_code = 0x00; + break; + } + + for (i = 0; i < length; ++i) { + if (vb && vb->vb.v4l2_planes[0].bytesused >= GO7007_BUF_SIZE - 3) { + v4l2_info(&go->v4l2_dev, "dropping oversized frame\n"); + vb->vb.v4l2_planes[0].bytesused = 0; + vb->frame_offset = 0; + vb->modet_active = 0; + vb = go->active_buf = NULL; + } + + switch (go->state) { + case STATE_DATA: + switch (buf[i]) { + case 0x00: + go->state = STATE_00; + break; + case 0xFF: + go->state = STATE_FF; + break; + default: + store_byte(vb, buf[i]); + break; + } + break; + case STATE_00: + switch (buf[i]) { + case 0x00: + go->state = STATE_00_00; + break; + case 0xFF: + store_byte(vb, 0x00); + go->state = STATE_FF; + break; + default: + store_byte(vb, 0x00); + store_byte(vb, buf[i]); + go->state = STATE_DATA; + break; + } + break; + case STATE_00_00: + switch (buf[i]) { + case 0x00: + store_byte(vb, 0x00); + /* go->state remains STATE_00_00 */ + break; + case 0x01: + go->state = STATE_00_00_01; + break; + case 0xFF: + store_byte(vb, 0x00); + store_byte(vb, 0x00); + go->state = STATE_FF; + break; + default: + store_byte(vb, 0x00); + store_byte(vb, 0x00); + store_byte(vb, buf[i]); + go->state = STATE_DATA; + break; + } + break; + case STATE_00_00_01: + if (buf[i] == 0xF8 && go->modet_enable == 0) { + /* MODET start code, but MODET not enabled */ + store_byte(vb, 0x00); + store_byte(vb, 0x00); + store_byte(vb, 0x01); + store_byte(vb, 0xF8); + go->state = STATE_DATA; + break; + } + /* If this is the start of a new MPEG frame, + * get a new buffer */ + if ((go->format == V4L2_PIX_FMT_MPEG1 || + go->format == V4L2_PIX_FMT_MPEG2 || + go->format == V4L2_PIX_FMT_MPEG4) && + (buf[i] == seq_start_code || + buf[i] == gop_start_code || + buf[i] == frame_start_code)) { + if (vb == NULL || go->seen_frame) + vb = frame_boundary(go, vb); + go->seen_frame = buf[i] == frame_start_code; + if (vb && go->seen_frame) + vb->frame_offset = vb->vb.v4l2_planes[0].bytesused; + } + /* Handle any special chunk types, or just write the + * start code to the (potentially new) buffer */ + switch (buf[i]) { + case 0xF5: /* timestamp */ + go->parse_length = 12; + go->state = STATE_UNPARSED; + break; + case 0xF6: /* vbi */ + go->state = STATE_VBI_LEN_A; + break; + case 0xF8: /* MD map */ + go->parse_length = 0; + memset(go->active_map, 0, + sizeof(go->active_map)); + go->state = STATE_MODET_MAP; + break; + case 0xFF: /* Potential JPEG start code */ + store_byte(vb, 0x00); + store_byte(vb, 0x00); + store_byte(vb, 0x01); + go->state = STATE_FF; + break; + default: + store_byte(vb, 0x00); + store_byte(vb, 0x00); + store_byte(vb, 0x01); + store_byte(vb, buf[i]); + go->state = STATE_DATA; + break; + } + break; + case STATE_FF: + switch (buf[i]) { + case 0x00: + store_byte(vb, 0xFF); + go->state = STATE_00; + break; + case 0xFF: + store_byte(vb, 0xFF); + /* go->state remains STATE_FF */ + break; + case 0xD8: + if (go->format == V4L2_PIX_FMT_MJPEG) + vb = frame_boundary(go, vb); + /* fall through */ + default: + store_byte(vb, 0xFF); + store_byte(vb, buf[i]); + go->state = STATE_DATA; + break; + } + break; + case STATE_VBI_LEN_A: + go->parse_length = buf[i] << 8; + go->state = STATE_VBI_LEN_B; + break; + case STATE_VBI_LEN_B: + go->parse_length |= buf[i]; + if (go->parse_length > 0) + go->state = STATE_UNPARSED; + else + go->state = STATE_DATA; + break; + case STATE_MODET_MAP: + if (go->parse_length < 204) { + if (go->parse_length & 1) { + go->modet_word |= buf[i]; + write_bitmap_word(go); + } else + go->modet_word = buf[i] << 8; + } else if (go->parse_length == 207 && vb) { + vb->modet_active = buf[i]; + } + if (++go->parse_length == 208) + go->state = STATE_DATA; + break; + case STATE_UNPARSED: + if (--go->parse_length == 0) + go->state = STATE_DATA; + break; + } + } +} +EXPORT_SYMBOL(go7007_parse_video_stream); + +/* + * Allocate a new go7007 struct. Used by the hardware-specific probe. + */ +struct go7007 *go7007_alloc(const struct go7007_board_info *board, + struct device *dev) +{ + struct go7007 *go; + int i; + + go = kzalloc(sizeof(struct go7007), GFP_KERNEL); + if (go == NULL) + return NULL; + go->dev = dev; + go->board_info = board; + go->board_id = 0; + go->tuner_type = -1; + go->channel_number = 0; + go->name[0] = 0; + mutex_init(&go->hw_lock); + init_waitqueue_head(&go->frame_waitq); + spin_lock_init(&go->spinlock); + go->status = STATUS_INIT; + memset(&go->i2c_adapter, 0, sizeof(go->i2c_adapter)); + go->i2c_adapter_online = 0; + go->interrupt_available = 0; + init_waitqueue_head(&go->interrupt_waitq); + go->input = 0; + go7007_update_board(go); + go->encoder_h_halve = 0; + go->encoder_v_halve = 0; + go->encoder_subsample = 0; + go->format = V4L2_PIX_FMT_MJPEG; + go->bitrate = 1500000; + go->fps_scale = 1; + go->pali = 0; + go->aspect_ratio = GO7007_RATIO_1_1; + go->gop_size = 0; + go->ipb = 0; + go->closed_gop = 0; + go->repeat_seqhead = 0; + go->seq_header_enable = 0; + go->gop_header_enable = 0; + go->dvd_mode = 0; + go->interlace_coding = 0; + for (i = 0; i < 4; ++i) + go->modet[i].enable = 0; + for (i = 0; i < 1624; ++i) + go->modet_map[i] = 0; + go->audio_deliver = NULL; + go->audio_enabled = 0; + + return go; +} +EXPORT_SYMBOL(go7007_alloc); + +void go7007_update_board(struct go7007 *go) +{ + const struct go7007_board_info *board = go->board_info; + + if (board->sensor_flags & GO7007_SENSOR_TV) { + go->standard = GO7007_STD_NTSC; + go->std = V4L2_STD_NTSC_M; + go->width = 720; + go->height = 480; + go->sensor_framerate = 30000; + } else { + go->standard = GO7007_STD_OTHER; + go->width = board->sensor_width; + go->height = board->sensor_height; + go->sensor_framerate = board->sensor_framerate; + } + go->encoder_v_offset = board->sensor_v_offset; + go->encoder_h_offset = board->sensor_h_offset; +} +EXPORT_SYMBOL(go7007_update_board); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/usb/go7007/go7007-fw.c b/drivers/media/usb/go7007/go7007-fw.c new file mode 100644 index 0000000..5f4c9b9 --- /dev/null +++ b/drivers/media/usb/go7007/go7007-fw.c @@ -0,0 +1,1628 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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. + */ + +/* + * This file contains code to generate a firmware image for the GO7007SB + * encoder. Much of the firmware is read verbatim from a file, but some of + * it concerning bitrate control and other things that can be configured at + * run-time are generated dynamically. Note that the format headers + * generated here do not affect the functioning of the encoder; they are + * merely parroted back to the host at the start of each frame. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007-priv.h" + +#define GO7007_FW_NAME "go7007/go7007tv.bin" + +/* Constants used in the source firmware image to describe code segments */ + +#define FLAG_MODE_MJPEG (1) +#define FLAG_MODE_MPEG1 (1<<1) +#define FLAG_MODE_MPEG2 (1<<2) +#define FLAG_MODE_MPEG4 (1<<3) +#define FLAG_MODE_H263 (1<<4) +#define FLAG_MODE_ALL (FLAG_MODE_MJPEG | FLAG_MODE_MPEG1 | \ + FLAG_MODE_MPEG2 | FLAG_MODE_MPEG4 | \ + FLAG_MODE_H263) +#define FLAG_SPECIAL (1<<8) + +#define SPECIAL_FRM_HEAD 0 +#define SPECIAL_BRC_CTRL 1 +#define SPECIAL_CONFIG 2 +#define SPECIAL_SEQHEAD 3 +#define SPECIAL_AV_SYNC 4 +#define SPECIAL_FINAL 5 +#define SPECIAL_AUDIO 6 +#define SPECIAL_MODET 7 + +/* Little data class for creating MPEG headers bit-by-bit */ + +struct code_gen { + unsigned char *p; /* destination */ + u32 a; /* collects bits at the top of the variable */ + int b; /* bit position of most recently-written bit */ + int len; /* written out so far */ +}; + +#define CODE_GEN(name, dest) struct code_gen name = { dest, 0, 32, 0 } + +#define CODE_ADD(name, val, length) do { \ + name.b -= (length); \ + name.a |= (val) << name.b; \ + while (name.b <= 24) { \ + *name.p = name.a >> 24; \ + ++name.p; \ + name.a <<= 8; \ + name.b += 8; \ + name.len += 8; \ + } \ +} while (0) + +#define CODE_LENGTH(name) (name.len + (32 - name.b)) + +/* Tables for creating the bitrate control data */ + +static const s16 converge_speed_ip[101] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, + 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, + 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, + 19, 20, 22, 23, 25, 27, 30, 32, 35, 38, + 41, 45, 49, 53, 58, 63, 69, 76, 83, 91, + 100 +}; + +static const s16 converge_speed_ipb[101] = { + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, + 9, 9, 10, 10, 11, 12, 12, 13, 14, 14, + 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, + 28, 30, 32, 34, 37, 40, 42, 46, 49, 53, + 57, 61, 66, 71, 77, 83, 90, 97, 106, 115, + 125, 135, 147, 161, 175, 191, 209, 228, 249, 273, + 300 +}; + +static const s16 LAMBDA_table[4][101] = { + { 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, + 19, 19, 19, 20, 20, 20, 21, 21, 22, 22, + 22, 23, 23, 24, 24, 25, 25, 25, 26, 26, + 27, 27, 28, 28, 29, 29, 30, 31, 31, 32, + 32, 33, 33, 34, 35, 35, 36, 37, 37, 38, + 39, 39, 40, 41, 42, 42, 43, 44, 45, 46, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 67, 68, 69, 70, 72, 73, 74, 76, 77, 78, + 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, + 96 + }, + { + 20, 20, 20, 21, 21, 21, 22, 22, 23, 23, + 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, + 28, 29, 29, 30, 30, 31, 31, 32, 33, 33, + 34, 34, 35, 36, 36, 37, 38, 38, 39, 40, + 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, + 70, 71, 72, 73, 75, 76, 78, 79, 80, 82, + 83, 85, 86, 88, 90, 91, 93, 95, 96, 98, + 100, 102, 103, 105, 107, 109, 111, 113, 115, 117, + 120 + }, + { + 24, 24, 24, 25, 25, 26, 26, 27, 27, 28, + 28, 29, 29, 30, 30, 31, 31, 32, 33, 33, + 34, 34, 35, 36, 36, 37, 38, 38, 39, 40, + 41, 41, 42, 43, 44, 44, 45, 46, 47, 48, + 49, 50, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 62, 63, 64, 65, 66, 67, 69, + 70, 71, 72, 74, 75, 76, 78, 79, 81, 82, + 84, 85, 87, 88, 90, 92, 93, 95, 97, 98, + 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, + 120, 122, 124, 127, 129, 131, 134, 136, 138, 141, + 144 + }, + { + 32, 32, 33, 33, 34, 34, 35, 36, 36, 37, + 38, 38, 39, 40, 41, 41, 42, 43, 44, 44, + 45, 46, 47, 48, 49, 50, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 62, 63, 64, + 65, 66, 67, 69, 70, 71, 72, 74, 75, 76, + 78, 79, 81, 82, 84, 85, 87, 88, 90, 92, + 93, 95, 97, 98, 100, 102, 104, 106, 108, 110, + 112, 114, 116, 118, 120, 122, 124, 127, 129, 131, + 134, 136, 139, 141, 144, 146, 149, 152, 154, 157, + 160, 163, 166, 169, 172, 175, 178, 181, 185, 188, + 192 + } +}; + +/* MPEG blank frame generation tables */ + +enum mpeg_frame_type { + PFRAME, + BFRAME_PRE, + BFRAME_POST, + BFRAME_BIDIR, + BFRAME_EMPTY +}; + +static const u32 addrinctab[33][2] = { + { 0x01, 1 }, { 0x03, 3 }, { 0x02, 3 }, { 0x03, 4 }, + { 0x02, 4 }, { 0x03, 5 }, { 0x02, 5 }, { 0x07, 7 }, + { 0x06, 7 }, { 0x0b, 8 }, { 0x0a, 8 }, { 0x09, 8 }, + { 0x08, 8 }, { 0x07, 8 }, { 0x06, 8 }, { 0x17, 10 }, + { 0x16, 10 }, { 0x15, 10 }, { 0x14, 10 }, { 0x13, 10 }, + { 0x12, 10 }, { 0x23, 11 }, { 0x22, 11 }, { 0x21, 11 }, + { 0x20, 11 }, { 0x1f, 11 }, { 0x1e, 11 }, { 0x1d, 11 }, + { 0x1c, 11 }, { 0x1b, 11 }, { 0x1a, 11 }, { 0x19, 11 }, + { 0x18, 11 } +}; + +/* Standard JPEG tables */ + +static const u8 default_intra_quant_table[] = { + 8, 16, 19, 22, 26, 27, 29, 34, + 16, 16, 22, 24, 27, 29, 34, 37, + 19, 22, 26, 27, 29, 34, 34, 38, + 22, 22, 26, 27, 29, 34, 37, 40, + 22, 26, 27, 29, 32, 35, 40, 48, + 26, 27, 29, 32, 35, 40, 48, 58, + 26, 27, 29, 34, 38, 46, 56, 69, + 27, 29, 35, 38, 46, 56, 69, 83 +}; + +static const u8 bits_dc_luminance[] = { + 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 +}; + +static const u8 val_dc_luminance[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +static const u8 bits_dc_chrominance[] = { + 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 +}; + +static const u8 val_dc_chrominance[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +static const u8 bits_ac_luminance[] = { + 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d +}; + +static const u8 val_ac_luminance[] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +static const u8 bits_ac_chrominance[] = { + 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 +}; + +static const u8 val_ac_chrominance[] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +/* Zig-zag mapping for quant table + * + * OK, let's do this mapping on the actual table above so it doesn't have + * to be done on the fly. + */ +static const int zz[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 +}; + +static int copy_packages(__le16 *dest, u16 *src, int pkg_cnt, int space) +{ + int i, cnt = pkg_cnt * 32; + + if (space < cnt) + return -1; + + for (i = 0; i < cnt; ++i) + dest[i] = cpu_to_le16p(src + i); + + return cnt; +} + +static int mjpeg_frame_header(struct go7007 *go, unsigned char *buf, int q) +{ + int i, p = 0; + + buf[p++] = 0xff; + buf[p++] = 0xd8; + buf[p++] = 0xff; + buf[p++] = 0xdb; + buf[p++] = 0; + buf[p++] = 2 + 65; + buf[p++] = 0; + buf[p++] = default_intra_quant_table[0]; + for (i = 1; i < 64; ++i) + /* buf[p++] = (default_intra_quant_table[i] * q) >> 3; */ + buf[p++] = (default_intra_quant_table[zz[i]] * q) >> 3; + buf[p++] = 0xff; + buf[p++] = 0xc0; + buf[p++] = 0; + buf[p++] = 17; + buf[p++] = 8; + buf[p++] = go->height >> 8; + buf[p++] = go->height & 0xff; + buf[p++] = go->width >> 8; + buf[p++] = go->width & 0xff; + buf[p++] = 3; + buf[p++] = 1; + buf[p++] = 0x22; + buf[p++] = 0; + buf[p++] = 2; + buf[p++] = 0x11; + buf[p++] = 0; + buf[p++] = 3; + buf[p++] = 0x11; + buf[p++] = 0; + buf[p++] = 0xff; + buf[p++] = 0xc4; + buf[p++] = 418 >> 8; + buf[p++] = 418 & 0xff; + buf[p++] = 0x00; + memcpy(buf + p, bits_dc_luminance + 1, 16); + p += 16; + memcpy(buf + p, val_dc_luminance, sizeof(val_dc_luminance)); + p += sizeof(val_dc_luminance); + buf[p++] = 0x01; + memcpy(buf + p, bits_dc_chrominance + 1, 16); + p += 16; + memcpy(buf + p, val_dc_chrominance, sizeof(val_dc_chrominance)); + p += sizeof(val_dc_chrominance); + buf[p++] = 0x10; + memcpy(buf + p, bits_ac_luminance + 1, 16); + p += 16; + memcpy(buf + p, val_ac_luminance, sizeof(val_ac_luminance)); + p += sizeof(val_ac_luminance); + buf[p++] = 0x11; + memcpy(buf + p, bits_ac_chrominance + 1, 16); + p += 16; + memcpy(buf + p, val_ac_chrominance, sizeof(val_ac_chrominance)); + p += sizeof(val_ac_chrominance); + buf[p++] = 0xff; + buf[p++] = 0xda; + buf[p++] = 0; + buf[p++] = 12; + buf[p++] = 3; + buf[p++] = 1; + buf[p++] = 0x00; + buf[p++] = 2; + buf[p++] = 0x11; + buf[p++] = 3; + buf[p++] = 0x11; + buf[p++] = 0; + buf[p++] = 63; + buf[p++] = 0; + return p; +} + +static int gen_mjpeghdr_to_package(struct go7007 *go, __le16 *code, int space) +{ + u8 *buf; + u16 mem = 0x3e00; + unsigned int addr = 0x19; + int size = 0, i, off = 0, chunk; + + buf = kzalloc(4096, GFP_KERNEL); + if (buf == NULL) + return -1; + + for (i = 1; i < 32; ++i) { + mjpeg_frame_header(go, buf + size, i); + size += 80; + } + chunk = mjpeg_frame_header(go, buf + size, 1); + memmove(buf + size, buf + size + 80, chunk - 80); + size += chunk - 80; + + for (i = 0; i < size; i += chunk * 2) { + if (space - off < 32) { + off = -1; + goto done; + } + + code[off + 1] = __cpu_to_le16(0x8000 | mem); + + chunk = 28; + if (mem + chunk > 0x4000) + chunk = 0x4000 - mem; + if (i + 2 * chunk > size) + chunk = (size - i) / 2; + + if (chunk < 28) { + code[off] = __cpu_to_le16(0x4000 | chunk); + code[off + 31] = __cpu_to_le16(addr++); + mem = 0x3e00; + } else { + code[off] = __cpu_to_le16(0x1000 | 28); + code[off + 31] = 0; + mem += 28; + } + + memcpy(&code[off + 2], buf + i, chunk * 2); + off += 32; + } +done: + kfree(buf); + return off; +} + +static int mpeg1_frame_header(struct go7007 *go, unsigned char *buf, + int modulo, int pict_struct, enum mpeg_frame_type frame) +{ + int i, j, mb_code, mb_len; + int rows = go->interlace_coding ? go->height / 32 : go->height / 16; + CODE_GEN(c, buf + 6); + + switch (frame) { + case PFRAME: + mb_code = 0x1; + mb_len = 3; + break; + case BFRAME_PRE: + mb_code = 0x2; + mb_len = 4; + break; + case BFRAME_POST: + mb_code = 0x2; + mb_len = 3; + break; + case BFRAME_BIDIR: + mb_code = 0x2; + mb_len = 2; + break; + default: /* keep the compiler happy */ + mb_code = mb_len = 0; + break; + } + + CODE_ADD(c, frame == PFRAME ? 0x2 : 0x3, 13); + CODE_ADD(c, 0xffff, 16); + CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 0x7 : 0x4, 4); + if (frame != PFRAME) + CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 0x7 : 0x4, 4); + else + CODE_ADD(c, 0, 4); /* Is this supposed to be here?? */ + CODE_ADD(c, 0, 3); /* What is this?? */ + /* Byte-align with zeros */ + j = 8 - (CODE_LENGTH(c) % 8); + if (j != 8) + CODE_ADD(c, 0, j); + + if (go->format == V4L2_PIX_FMT_MPEG2) { + CODE_ADD(c, 0x1, 24); + CODE_ADD(c, 0xb5, 8); + CODE_ADD(c, 0x844, 12); + CODE_ADD(c, frame == PFRAME ? 0xff : 0x44, 8); + if (go->interlace_coding) { + CODE_ADD(c, pict_struct, 4); + if (go->dvd_mode) + CODE_ADD(c, 0x000, 11); + else + CODE_ADD(c, 0x200, 11); + } else { + CODE_ADD(c, 0x3, 4); + CODE_ADD(c, 0x20c, 11); + } + /* Byte-align with zeros */ + j = 8 - (CODE_LENGTH(c) % 8); + if (j != 8) + CODE_ADD(c, 0, j); + } + + for (i = 0; i < rows; ++i) { + CODE_ADD(c, 1, 24); + CODE_ADD(c, i + 1, 8); + CODE_ADD(c, 0x2, 6); + CODE_ADD(c, 0x1, 1); + CODE_ADD(c, mb_code, mb_len); + if (go->interlace_coding) { + CODE_ADD(c, 0x1, 2); + CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); + } + if (frame == BFRAME_BIDIR) { + CODE_ADD(c, 0x3, 2); + if (go->interlace_coding) + CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); + } + CODE_ADD(c, 0x3, 2); + for (j = (go->width >> 4) - 2; j >= 33; j -= 33) + CODE_ADD(c, 0x8, 11); + CODE_ADD(c, addrinctab[j][0], addrinctab[j][1]); + CODE_ADD(c, mb_code, mb_len); + if (go->interlace_coding) { + CODE_ADD(c, 0x1, 2); + CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); + } + if (frame == BFRAME_BIDIR) { + CODE_ADD(c, 0x3, 2); + if (go->interlace_coding) + CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); + } + CODE_ADD(c, 0x3, 2); + + /* Byte-align with zeros */ + j = 8 - (CODE_LENGTH(c) % 8); + if (j != 8) + CODE_ADD(c, 0, j); + } + + i = CODE_LENGTH(c) + 4 * 8; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x01; + buf[5] = 0x00; + return i; +} + +static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext) +{ + int i, aspect_ratio, picture_rate; + CODE_GEN(c, buf + 6); + + if (go->format == V4L2_PIX_FMT_MPEG1) { + switch (go->aspect_ratio) { + case GO7007_RATIO_4_3: + aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2; + break; + case GO7007_RATIO_16_9: + aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4; + break; + default: + aspect_ratio = 1; + break; + } + } else { + switch (go->aspect_ratio) { + case GO7007_RATIO_4_3: + aspect_ratio = 2; + break; + case GO7007_RATIO_16_9: + aspect_ratio = 3; + break; + default: + aspect_ratio = 1; + break; + } + } + switch (go->sensor_framerate) { + case 24000: + picture_rate = 1; + break; + case 24024: + picture_rate = 2; + break; + case 25025: + picture_rate = go->interlace_coding ? 6 : 3; + break; + case 30000: + picture_rate = go->interlace_coding ? 7 : 4; + break; + case 30030: + picture_rate = go->interlace_coding ? 8 : 5; + break; + default: + picture_rate = 5; /* 30 fps seems like a reasonable default */ + break; + } + + CODE_ADD(c, go->width, 12); + CODE_ADD(c, go->height, 12); + CODE_ADD(c, aspect_ratio, 4); + CODE_ADD(c, picture_rate, 4); + CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 20000 : 0x3ffff, 18); + CODE_ADD(c, 1, 1); + CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 112 : 20, 10); + CODE_ADD(c, 0, 3); + + /* Byte-align with zeros */ + i = 8 - (CODE_LENGTH(c) % 8); + if (i != 8) + CODE_ADD(c, 0, i); + + if (go->format == V4L2_PIX_FMT_MPEG2) { + CODE_ADD(c, 0x1, 24); + CODE_ADD(c, 0xb5, 8); + CODE_ADD(c, 0x148, 12); + if (go->interlace_coding) + CODE_ADD(c, 0x20001, 20); + else + CODE_ADD(c, 0xa0001, 20); + CODE_ADD(c, 0, 16); + + /* Byte-align with zeros */ + i = 8 - (CODE_LENGTH(c) % 8); + if (i != 8) + CODE_ADD(c, 0, i); + + if (ext) { + CODE_ADD(c, 0x1, 24); + CODE_ADD(c, 0xb52, 12); + CODE_ADD(c, go->standard == GO7007_STD_NTSC ? 2 : 1, 3); + CODE_ADD(c, 0x105, 9); + CODE_ADD(c, 0x505, 16); + CODE_ADD(c, go->width, 14); + CODE_ADD(c, 1, 1); + CODE_ADD(c, go->height, 14); + + /* Byte-align with zeros */ + i = 8 - (CODE_LENGTH(c) % 8); + if (i != 8) + CODE_ADD(c, 0, i); + } + } + + i = CODE_LENGTH(c) + 4 * 8; + buf[0] = i & 0xff; + buf[1] = i >> 8; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x01; + buf[5] = 0xb3; + return i; +} + +static int gen_mpeg1hdr_to_package(struct go7007 *go, + __le16 *code, int space, int *framelen) +{ + u8 *buf; + u16 mem = 0x3e00; + unsigned int addr = 0x19; + int i, off = 0, chunk; + + buf = kzalloc(5120, GFP_KERNEL); + if (buf == NULL) + return -1; + + framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME); + if (go->interlace_coding) + framelen[0] += mpeg1_frame_header(go, buf + framelen[0] / 8, + 0, 2, PFRAME); + buf[0] = framelen[0] & 0xff; + buf[1] = framelen[0] >> 8; + i = 368; + framelen[1] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_PRE); + if (go->interlace_coding) + framelen[1] += mpeg1_frame_header(go, buf + i + framelen[1] / 8, + 0, 2, BFRAME_PRE); + buf[i] = framelen[1] & 0xff; + buf[i + 1] = framelen[1] >> 8; + i += 1632; + framelen[2] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_POST); + if (go->interlace_coding) + framelen[2] += mpeg1_frame_header(go, buf + i + framelen[2] / 8, + 0, 2, BFRAME_POST); + buf[i] = framelen[2] & 0xff; + buf[i + 1] = framelen[2] >> 8; + i += 1432; + framelen[3] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_BIDIR); + if (go->interlace_coding) + framelen[3] += mpeg1_frame_header(go, buf + i + framelen[3] / 8, + 0, 2, BFRAME_BIDIR); + buf[i] = framelen[3] & 0xff; + buf[i + 1] = framelen[3] >> 8; + i += 1632 + 16; + mpeg1_sequence_header(go, buf + i, 0); + i += 40; + for (i = 0; i < 5120; i += chunk * 2) { + if (space - off < 32) { + off = -1; + goto done; + } + + code[off + 1] = __cpu_to_le16(0x8000 | mem); + + chunk = 28; + if (mem + chunk > 0x4000) + chunk = 0x4000 - mem; + if (i + 2 * chunk > 5120) + chunk = (5120 - i) / 2; + + if (chunk < 28) { + code[off] = __cpu_to_le16(0x4000 | chunk); + code[off + 31] = __cpu_to_le16(addr); + if (mem + chunk == 0x4000) { + mem = 0x3e00; + ++addr; + } + } else { + code[off] = __cpu_to_le16(0x1000 | 28); + code[off + 31] = 0; + mem += 28; + } + + memcpy(&code[off + 2], buf + i, chunk * 2); + off += 32; + } +done: + kfree(buf); + return off; +} + +static int vti_bitlen(struct go7007 *go) +{ + unsigned int i, max_time_incr = go->sensor_framerate / go->fps_scale; + + for (i = 31; (max_time_incr & ((1 << i) - 1)) == max_time_incr; --i) + ; + return i + 1; +} + +static int mpeg4_frame_header(struct go7007 *go, unsigned char *buf, + int modulo, enum mpeg_frame_type frame) +{ + int i; + CODE_GEN(c, buf + 6); + int mb_count = (go->width >> 4) * (go->height >> 4); + + CODE_ADD(c, frame == PFRAME ? 0x1 : 0x2, 2); + if (modulo) + CODE_ADD(c, 0x1, 1); + CODE_ADD(c, 0x1, 2); + CODE_ADD(c, 0, vti_bitlen(go)); + CODE_ADD(c, 0x3, 2); + if (frame == PFRAME) + CODE_ADD(c, 0, 1); + CODE_ADD(c, 0xc, 11); + if (frame != PFRAME) + CODE_ADD(c, 0x4, 3); + if (frame != BFRAME_EMPTY) { + for (i = 0; i < mb_count; ++i) { + switch (frame) { + case PFRAME: + CODE_ADD(c, 0x1, 1); + break; + case BFRAME_PRE: + CODE_ADD(c, 0x47, 8); + break; + case BFRAME_POST: + CODE_ADD(c, 0x27, 7); + break; + case BFRAME_BIDIR: + CODE_ADD(c, 0x5f, 8); + break; + case BFRAME_EMPTY: /* keep compiler quiet */ + break; + } + } + } + + /* Byte-align with a zero followed by ones */ + i = 8 - (CODE_LENGTH(c) % 8); + CODE_ADD(c, 0, 1); + CODE_ADD(c, (1 << (i - 1)) - 1, i - 1); + + i = CODE_LENGTH(c) + 4 * 8; + buf[0] = i & 0xff; + buf[1] = i >> 8; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x01; + buf[5] = 0xb6; + return i; +} + +static int mpeg4_sequence_header(struct go7007 *go, unsigned char *buf, int ext) +{ + const unsigned char head[] = { 0x00, 0x00, 0x01, 0xb0, go->pali, + 0x00, 0x00, 0x01, 0xb5, 0x09, + 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x20, }; + int i, aspect_ratio; + int fps = go->sensor_framerate / go->fps_scale; + CODE_GEN(c, buf + 2 + sizeof(head)); + + switch (go->aspect_ratio) { + case GO7007_RATIO_4_3: + aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2; + break; + case GO7007_RATIO_16_9: + aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4; + break; + default: + aspect_ratio = 1; + break; + } + + memcpy(buf + 2, head, sizeof(head)); + CODE_ADD(c, 0x191, 17); + CODE_ADD(c, aspect_ratio, 4); + CODE_ADD(c, 0x1, 4); + CODE_ADD(c, fps, 16); + CODE_ADD(c, 0x3, 2); + CODE_ADD(c, 1001, vti_bitlen(go)); + CODE_ADD(c, 1, 1); + CODE_ADD(c, go->width, 13); + CODE_ADD(c, 1, 1); + CODE_ADD(c, go->height, 13); + CODE_ADD(c, 0x2830, 14); + + /* Byte-align */ + i = 8 - (CODE_LENGTH(c) % 8); + CODE_ADD(c, 0, 1); + CODE_ADD(c, (1 << (i - 1)) - 1, i - 1); + + i = CODE_LENGTH(c) + sizeof(head) * 8; + buf[0] = i & 0xff; + buf[1] = i >> 8; + return i; +} + +static int gen_mpeg4hdr_to_package(struct go7007 *go, + __le16 *code, int space, int *framelen) +{ + u8 *buf; + u16 mem = 0x3e00; + unsigned int addr = 0x19; + int i, off = 0, chunk; + + buf = kzalloc(5120, GFP_KERNEL); + if (buf == NULL) + return -1; + + framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME); + i = 368; + framelen[1] = mpeg4_frame_header(go, buf + i, 0, BFRAME_PRE); + i += 1632; + framelen[2] = mpeg4_frame_header(go, buf + i, 0, BFRAME_POST); + i += 1432; + framelen[3] = mpeg4_frame_header(go, buf + i, 0, BFRAME_BIDIR); + i += 1632; + mpeg4_frame_header(go, buf + i, 0, BFRAME_EMPTY); + i += 16; + mpeg4_sequence_header(go, buf + i, 0); + i += 40; + for (i = 0; i < 5120; i += chunk * 2) { + if (space - off < 32) { + off = -1; + goto done; + } + + code[off + 1] = __cpu_to_le16(0x8000 | mem); + + chunk = 28; + if (mem + chunk > 0x4000) + chunk = 0x4000 - mem; + if (i + 2 * chunk > 5120) + chunk = (5120 - i) / 2; + + if (chunk < 28) { + code[off] = __cpu_to_le16(0x4000 | chunk); + code[off + 31] = __cpu_to_le16(addr); + if (mem + chunk == 0x4000) { + mem = 0x3e00; + ++addr; + } + } else { + code[off] = __cpu_to_le16(0x1000 | 28); + code[off + 31] = 0; + mem += 28; + } + + memcpy(&code[off + 2], buf + i, chunk * 2); + off += 32; + } + mem = 0x3e00; + addr = go->ipb ? 0x14f9 : 0x0af9; + memset(buf, 0, 5120); + framelen[4] = mpeg4_frame_header(go, buf, 1, PFRAME); + i = 368; + framelen[5] = mpeg4_frame_header(go, buf + i, 1, BFRAME_PRE); + i += 1632; + framelen[6] = mpeg4_frame_header(go, buf + i, 1, BFRAME_POST); + i += 1432; + framelen[7] = mpeg4_frame_header(go, buf + i, 1, BFRAME_BIDIR); + i += 1632; + mpeg4_frame_header(go, buf + i, 1, BFRAME_EMPTY); + i += 16; + for (i = 0; i < 5120; i += chunk * 2) { + if (space - off < 32) { + off = -1; + goto done; + } + + code[off + 1] = __cpu_to_le16(0x8000 | mem); + + chunk = 28; + if (mem + chunk > 0x4000) + chunk = 0x4000 - mem; + if (i + 2 * chunk > 5120) + chunk = (5120 - i) / 2; + + if (chunk < 28) { + code[off] = __cpu_to_le16(0x4000 | chunk); + code[off + 31] = __cpu_to_le16(addr); + if (mem + chunk == 0x4000) { + mem = 0x3e00; + ++addr; + } + } else { + code[off] = __cpu_to_le16(0x1000 | 28); + code[off + 31] = 0; + mem += 28; + } + + memcpy(&code[off + 2], buf + i, chunk * 2); + off += 32; + } +done: + kfree(buf); + return off; +} + +static int brctrl_to_package(struct go7007 *go, + __le16 *code, int space, int *framelen) +{ + int converge_speed = 0; + int lambda = (go->format == V4L2_PIX_FMT_MJPEG || go->dvd_mode) ? + 100 : 0; + int peak_rate = 6 * go->bitrate / 5; + int vbv_buffer = go->format == V4L2_PIX_FMT_MJPEG ? + go->bitrate : + (go->dvd_mode ? 900000 : peak_rate); + int fps = go->sensor_framerate / go->fps_scale; + int q = 0; + /* Bizarre math below depends on rounding errors in division */ + u32 sgop_expt_addr = go->bitrate / 32 * (go->ipb ? 3 : 1) * 1001 / fps; + u32 sgop_peak_addr = peak_rate / 32 * 1001 / fps; + u32 total_expt_addr = go->bitrate / 32 * 1000 / fps * (fps / 1000); + u32 vbv_alert_addr = vbv_buffer * 3 / (4 * 32); + u32 cplx[] = { + q > 0 ? sgop_expt_addr * q : + 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, + q > 0 ? sgop_expt_addr * q : + 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, + q > 0 ? sgop_expt_addr * q : + 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, + q > 0 ? sgop_expt_addr * q : + 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, + }; + u32 calc_q = q > 0 ? q : cplx[0] / sgop_expt_addr; + u16 pack[] = { + 0x200e, 0x0000, + 0xBF20, go->ipb ? converge_speed_ipb[converge_speed] + : converge_speed_ip[converge_speed], + 0xBF21, go->ipb ? 2 : 0, + 0xBF22, go->ipb ? LAMBDA_table[0][lambda / 2 + 50] + : 32767, + 0xBF23, go->ipb ? LAMBDA_table[1][lambda] : 32767, + 0xBF24, 32767, + 0xBF25, lambda > 99 ? 32767 : LAMBDA_table[3][lambda], + 0xBF26, sgop_expt_addr & 0x0000FFFF, + 0xBF27, sgop_expt_addr >> 16, + 0xBF28, sgop_peak_addr & 0x0000FFFF, + 0xBF29, sgop_peak_addr >> 16, + 0xBF2A, vbv_alert_addr & 0x0000FFFF, + 0xBF2B, vbv_alert_addr >> 16, + 0xBF2C, 0, + 0xBF2D, 0, + 0, 0, + + 0x200e, 0x0000, + 0xBF2E, vbv_alert_addr & 0x0000FFFF, + 0xBF2F, vbv_alert_addr >> 16, + 0xBF30, cplx[0] & 0x0000FFFF, + 0xBF31, cplx[0] >> 16, + 0xBF32, cplx[1] & 0x0000FFFF, + 0xBF33, cplx[1] >> 16, + 0xBF34, cplx[2] & 0x0000FFFF, + 0xBF35, cplx[2] >> 16, + 0xBF36, cplx[3] & 0x0000FFFF, + 0xBF37, cplx[3] >> 16, + 0xBF38, 0, + 0xBF39, 0, + 0xBF3A, total_expt_addr & 0x0000FFFF, + 0xBF3B, total_expt_addr >> 16, + 0, 0, + + 0x200e, 0x0000, + 0xBF3C, total_expt_addr & 0x0000FFFF, + 0xBF3D, total_expt_addr >> 16, + 0xBF3E, 0, + 0xBF3F, 0, + 0xBF48, 0, + 0xBF49, 0, + 0xBF4A, calc_q < 4 ? 4 : (calc_q > 124 ? 124 : calc_q), + 0xBF4B, 4, + 0xBF4C, 0, + 0xBF4D, 0, + 0xBF4E, 0, + 0xBF4F, 0, + 0xBF50, 0, + 0xBF51, 0, + 0, 0, + + 0x200e, 0x0000, + 0xBF40, sgop_expt_addr & 0x0000FFFF, + 0xBF41, sgop_expt_addr >> 16, + 0xBF42, 0, + 0xBF43, 0, + 0xBF44, 0, + 0xBF45, 0, + 0xBF46, (go->width >> 4) * (go->height >> 4), + 0xBF47, 0, + 0xBF64, 0, + 0xBF65, 0, + 0xBF18, framelen[4], + 0xBF19, framelen[5], + 0xBF1A, framelen[6], + 0xBF1B, framelen[7], + 0, 0, + +#if 0 + /* Remove once we don't care about matching */ + 0x200e, 0x0000, + 0xBF56, 4, + 0xBF57, 0, + 0xBF58, 5, + 0xBF59, 0, + 0xBF5A, 6, + 0xBF5B, 0, + 0xBF5C, 8, + 0xBF5D, 0, + 0xBF5E, 1, + 0xBF5F, 0, + 0xBF60, 1, + 0xBF61, 0, + 0xBF62, 0, + 0xBF63, 0, + 0, 0, +#else + 0x2008, 0x0000, + 0xBF56, 4, + 0xBF57, 0, + 0xBF58, 5, + 0xBF59, 0, + 0xBF5A, 6, + 0xBF5B, 0, + 0xBF5C, 8, + 0xBF5D, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, +#endif + + 0x200e, 0x0000, + 0xBF10, 0, + 0xBF11, 0, + 0xBF12, 0, + 0xBF13, 0, + 0xBF14, 0, + 0xBF15, 0, + 0xBF16, 0, + 0xBF17, 0, + 0xBF7E, 0, + 0xBF7F, 1, + 0xBF52, framelen[0], + 0xBF53, framelen[1], + 0xBF54, framelen[2], + 0xBF55, framelen[3], + 0, 0, + }; + + return copy_packages(code, pack, 6, space); +} + +static int config_package(struct go7007 *go, __le16 *code, int space) +{ + int fps = go->sensor_framerate / go->fps_scale / 1000; + int rows = go->interlace_coding ? go->height / 32 : go->height / 16; + int brc_window_size = fps; + int q_min = 2, q_max = 31; + int THACCoeffSet0 = 0; + u16 pack[] = { + 0x200e, 0x0000, + 0xc002, 0x14b4, + 0xc003, 0x28b4, + 0xc004, 0x3c5a, + 0xdc05, 0x2a77, + 0xc6c3, go->format == V4L2_PIX_FMT_MPEG4 ? 0 : + (go->format == V4L2_PIX_FMT_H263 ? 0 : 1), + 0xc680, go->format == V4L2_PIX_FMT_MPEG4 ? 0xf1 : + (go->format == V4L2_PIX_FMT_H263 ? 0x61 : + 0xd3), + 0xc780, 0x0140, + 0xe009, 0x0001, + 0xc60f, 0x0008, + 0xd4ff, 0x0002, + 0xe403, 2340, + 0xe406, 75, + 0xd411, 0x0001, + 0xd410, 0xa1d6, + 0x0001, 0x2801, + + 0x200d, 0x0000, + 0xe402, 0x018b, + 0xe401, 0x8b01, + 0xd472, (go->board_info->sensor_flags & + GO7007_SENSOR_TV) && + (!go->interlace_coding) ? + 0x01b0 : 0x0170, + 0xd475, (go->board_info->sensor_flags & + GO7007_SENSOR_TV) && + (!go->interlace_coding) ? + 0x0008 : 0x0009, + 0xc404, go->interlace_coding ? 0x44 : + (go->format == V4L2_PIX_FMT_MPEG4 ? 0x11 : + (go->format == V4L2_PIX_FMT_MPEG1 ? 0x02 : + (go->format == V4L2_PIX_FMT_MPEG2 ? 0x04 : + (go->format == V4L2_PIX_FMT_H263 ? 0x08 : + 0x20)))), + 0xbf0a, (go->format == V4L2_PIX_FMT_MPEG4 ? 8 : + (go->format == V4L2_PIX_FMT_MPEG1 ? 1 : + (go->format == V4L2_PIX_FMT_MPEG2 ? 2 : + (go->format == V4L2_PIX_FMT_H263 ? 4 : 16)))) | + ((go->repeat_seqhead ? 1 : 0) << 6) | + ((go->dvd_mode ? 1 : 0) << 9) | + ((go->gop_header_enable ? 1 : 0) << 10), + 0xbf0b, 0, + 0xdd5a, go->ipb ? 0x14 : 0x0a, + 0xbf0c, 0, + 0xbf0d, 0, + 0xc683, THACCoeffSet0, + 0xc40a, (go->width << 4) | rows, + 0xe01a, go->board_info->hpi_buffer_cap, + 0, 0, + 0, 0, + + 0x2008, 0, + 0xe402, 0x88, + 0xe401, 0x8f01, + 0xbf6a, 0, + 0xbf6b, 0, + 0xbf6c, 0, + 0xbf6d, 0, + 0xbf6e, 0, + 0xbf6f, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0x200e, 0, + 0xbf66, brc_window_size, + 0xbf67, 0, + 0xbf68, q_min, + 0xbf69, q_max, + 0xbfe0, 0, + 0xbfe1, 0, + 0xbfe2, 0, + 0xbfe3, go->ipb ? 3 : 1, + 0xc031, go->board_info->sensor_flags & + GO7007_SENSOR_VBI ? 1 : 0, + 0xc01c, 0x1f, + 0xdd8c, 0x15, + 0xdd94, 0x15, + 0xdd88, go->ipb ? 0x1401 : 0x0a01, + 0xdd90, go->ipb ? 0x1401 : 0x0a01, + 0, 0, + + 0x200e, 0, + 0xbfe4, 0, + 0xbfe5, 0, + 0xbfe6, 0, + 0xbfe7, fps << 8, + 0xbfe8, 0x3a00, + 0xbfe9, 0, + 0xbfea, 0, + 0xbfeb, 0, + 0xbfec, (go->interlace_coding ? 1 << 15 : 0) | + (go->modet_enable ? 0xa : 0) | + (go->board_info->sensor_flags & + GO7007_SENSOR_VBI ? 1 : 0), + 0xbfed, 0, + 0xbfee, 0, + 0xbfef, 0, + 0xbff0, go->board_info->sensor_flags & + GO7007_SENSOR_TV ? 0xf060 : 0xb060, + 0xbff1, 0, + 0, 0, + }; + + return copy_packages(code, pack, 5, space); +} + +static int seqhead_to_package(struct go7007 *go, __le16 *code, int space, + int (*sequence_header_func)(struct go7007 *go, + unsigned char *buf, int ext)) +{ + int vop_time_increment_bitlength = vti_bitlen(go); + int fps = go->sensor_framerate / go->fps_scale * + (go->interlace_coding ? 2 : 1); + unsigned char buf[40] = { }; + int len = sequence_header_func(go, buf, 1); + u16 pack[] = { + 0x2006, 0, + 0xbf08, fps, + 0xbf09, 0, + 0xbff2, vop_time_increment_bitlength, + 0xbff3, (1 << vop_time_increment_bitlength) - 1, + 0xbfe6, 0, + 0xbfe7, (fps / 1000) << 8, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0x2007, 0, + 0xc800, buf[2] << 8 | buf[3], + 0xc801, buf[4] << 8 | buf[5], + 0xc802, buf[6] << 8 | buf[7], + 0xc803, buf[8] << 8 | buf[9], + 0xc406, 64, + 0xc407, len - 64, + 0xc61b, 1, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0x200e, 0, + 0xc808, buf[10] << 8 | buf[11], + 0xc809, buf[12] << 8 | buf[13], + 0xc80a, buf[14] << 8 | buf[15], + 0xc80b, buf[16] << 8 | buf[17], + 0xc80c, buf[18] << 8 | buf[19], + 0xc80d, buf[20] << 8 | buf[21], + 0xc80e, buf[22] << 8 | buf[23], + 0xc80f, buf[24] << 8 | buf[25], + 0xc810, buf[26] << 8 | buf[27], + 0xc811, buf[28] << 8 | buf[29], + 0xc812, buf[30] << 8 | buf[31], + 0xc813, buf[32] << 8 | buf[33], + 0xc814, buf[34] << 8 | buf[35], + 0xc815, buf[36] << 8 | buf[37], + 0, 0, + 0, 0, + 0, 0, + }; + + return copy_packages(code, pack, 3, space); +} + +static int relative_prime(int big, int little) +{ + int remainder; + + while (little != 0) { + remainder = big % little; + big = little; + little = remainder; + } + return big; +} + +static int avsync_to_package(struct go7007 *go, __le16 *code, int space) +{ + int arate = go->board_info->audio_rate * 1001 * go->fps_scale; + int ratio = arate / go->sensor_framerate; + int adjratio = ratio * 215 / 100; + int rprime = relative_prime(go->sensor_framerate, + arate % go->sensor_framerate); + int f1 = (arate % go->sensor_framerate) / rprime; + int f2 = (go->sensor_framerate - arate % go->sensor_framerate) / rprime; + u16 pack[] = { + 0x200e, 0, + 0xbf98, (u16)((-adjratio) & 0xffff), + 0xbf99, (u16)((-adjratio) >> 16), + 0xbf92, 0, + 0xbf93, 0, + 0xbff4, f1 > f2 ? f1 : f2, + 0xbff5, f1 < f2 ? f1 : f2, + 0xbff6, f1 < f2 ? ratio : ratio + 1, + 0xbff7, f1 > f2 ? ratio : ratio + 1, + 0xbff8, 0, + 0xbff9, 0, + 0xbffa, adjratio & 0xffff, + 0xbffb, adjratio >> 16, + 0xbf94, 0, + 0xbf95, 0, + 0, 0, + }; + + return copy_packages(code, pack, 1, space); +} + +static int final_package(struct go7007 *go, __le16 *code, int space) +{ + int rows = go->interlace_coding ? go->height / 32 : go->height / 16; + u16 pack[] = { + 0x8000, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + ((go->board_info->sensor_flags & GO7007_SENSOR_TV) && + (!go->interlace_coding) ? + (1 << 14) | (1 << 9) : 0) | + ((go->encoder_subsample ? 1 : 0) << 8) | + (go->board_info->sensor_flags & + GO7007_SENSOR_CONFIG_MASK), + ((go->encoder_v_halve ? 1 : 0) << 14) | + (go->encoder_v_halve ? rows << 9 : rows << 8) | + (go->encoder_h_halve ? 1 << 6 : 0) | + (go->encoder_h_halve ? go->width >> 3 : go->width >> 4), + (1 << 15) | (go->encoder_v_offset << 6) | + (1 << 7) | (go->encoder_h_offset >> 2), + (1 << 6), + 0, + 0, + ((go->fps_scale - 1) << 8) | + (go->board_info->sensor_flags & GO7007_SENSOR_TV ? + (1 << 7) : 0) | + 0x41, + go->ipb ? 0xd4c : 0x36b, + (rows << 8) | (go->width >> 4), + go->format == V4L2_PIX_FMT_MPEG4 ? 0x0404 : 0, + (1 << 15) | ((go->interlace_coding ? 1 : 0) << 13) | + ((go->closed_gop ? 1 : 0) << 12) | + ((go->format == V4L2_PIX_FMT_MPEG4 ? 1 : 0) << 11) | + /* (1 << 9) | */ + ((go->ipb ? 3 : 0) << 7) | + ((go->modet_enable ? 1 : 0) << 2) | + ((go->dvd_mode ? 1 : 0) << 1) | 1, + (go->format == V4L2_PIX_FMT_MPEG1 ? 0x89a0 : + (go->format == V4L2_PIX_FMT_MPEG2 ? 0x89a0 : + (go->format == V4L2_PIX_FMT_MJPEG ? 0x89a0 : + (go->format == V4L2_PIX_FMT_MPEG4 ? 0x8920 : + (go->format == V4L2_PIX_FMT_H263 ? 0x8920 : 0))))), + go->ipb ? 0x1f15 : 0x1f0b, + go->ipb ? 0x0015 : 0x000b, + go->ipb ? 0xa800 : 0x5800, + 0xffff, + 0x0020 + 0x034b * 0, + 0x0020 + 0x034b * 1, + 0x0020 + 0x034b * 2, + 0x0020 + 0x034b * 3, + 0x0020 + 0x034b * 4, + 0x0020 + 0x034b * 5, + go->ipb ? (go->gop_size / 3) : go->gop_size, + (go->height >> 4) * (go->width >> 4) * 110 / 100, + }; + + return copy_packages(code, pack, 1, space); +} + +static int audio_to_package(struct go7007 *go, __le16 *code, int space) +{ + int clock_config = ((go->board_info->audio_flags & + GO7007_AUDIO_I2S_MASTER ? 1 : 0) << 11) | + ((go->board_info->audio_flags & + GO7007_AUDIO_OKI_MODE ? 1 : 0) << 8) | + (((go->board_info->audio_bclk_div / 4) - 1) << 4) | + (go->board_info->audio_main_div - 1); + u16 pack[] = { + 0x200d, 0, + 0x9002, 0, + 0x9002, 0, + 0x9031, 0, + 0x9032, 0, + 0x9033, 0, + 0x9034, 0, + 0x9035, 0, + 0x9036, 0, + 0x9037, 0, + 0x9040, 0, + 0x9000, clock_config, + 0x9001, (go->board_info->audio_flags & 0xffff) | + (1 << 9), + 0x9000, ((go->board_info->audio_flags & + GO7007_AUDIO_I2S_MASTER ? + 1 : 0) << 10) | + clock_config, + 0, 0, + 0, 0, + 0x2005, 0, + 0x9041, 0, + 0x9042, 256, + 0x9043, 0, + 0x9044, 16, + 0x9045, 16, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + }; + + return copy_packages(code, pack, 2, space); +} + +static int modet_to_package(struct go7007 *go, __le16 *code, int space) +{ + bool has_modet0 = go->modet[0].enable; + bool has_modet1 = go->modet[1].enable; + bool has_modet2 = go->modet[2].enable; + bool has_modet3 = go->modet[3].enable; + int ret, mb, i, addr, cnt = 0; + u16 pack[32]; + u16 thresholds[] = { + 0x200e, 0, + 0xbf82, has_modet0 ? go->modet[0].pixel_threshold : 32767, + 0xbf83, has_modet1 ? go->modet[1].pixel_threshold : 32767, + 0xbf84, has_modet2 ? go->modet[2].pixel_threshold : 32767, + 0xbf85, has_modet3 ? go->modet[3].pixel_threshold : 32767, + 0xbf86, has_modet0 ? go->modet[0].motion_threshold : 32767, + 0xbf87, has_modet1 ? go->modet[1].motion_threshold : 32767, + 0xbf88, has_modet2 ? go->modet[2].motion_threshold : 32767, + 0xbf89, has_modet3 ? go->modet[3].motion_threshold : 32767, + 0xbf8a, has_modet0 ? go->modet[0].mb_threshold : 32767, + 0xbf8b, has_modet1 ? go->modet[1].mb_threshold : 32767, + 0xbf8c, has_modet2 ? go->modet[2].mb_threshold : 32767, + 0xbf8d, has_modet3 ? go->modet[3].mb_threshold : 32767, + 0xbf8e, 0, + 0xbf8f, 0, + 0, 0, + }; + + ret = copy_packages(code, thresholds, 1, space); + if (ret < 0) + return -1; + cnt += ret; + + addr = 0xbac0; + memset(pack, 0, 64); + i = 0; + for (mb = 0; mb < 1624; ++mb) { + pack[i * 2 + 3] <<= 2; + pack[i * 2 + 3] |= go->modet_map[mb]; + if (mb % 8 != 7) + continue; + pack[i * 2 + 2] = addr++; + ++i; + if (i == 10 || mb == 1623) { + pack[0] = 0x2000 | i; + ret = copy_packages(code + cnt, pack, 1, space - cnt); + if (ret < 0) + return -1; + cnt += ret; + i = 0; + memset(pack, 0, 64); + } + pack[i * 2 + 3] = 0; + } + + memset(pack, 0, 64); + i = 0; + for (addr = 0xbb90; addr < 0xbbfa; ++addr) { + pack[i * 2 + 2] = addr; + pack[i * 2 + 3] = 0; + ++i; + if (i == 10 || addr == 0xbbf9) { + pack[0] = 0x2000 | i; + ret = copy_packages(code + cnt, pack, 1, space - cnt); + if (ret < 0) + return -1; + cnt += ret; + i = 0; + memset(pack, 0, 64); + } + } + return cnt; +} + +static int do_special(struct go7007 *go, u16 type, __le16 *code, int space, + int *framelen) +{ + switch (type) { + case SPECIAL_FRM_HEAD: + switch (go->format) { + case V4L2_PIX_FMT_MJPEG: + return gen_mjpeghdr_to_package(go, code, space); + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: + return gen_mpeg1hdr_to_package(go, code, space, + framelen); + case V4L2_PIX_FMT_MPEG4: + return gen_mpeg4hdr_to_package(go, code, space, + framelen); + } + case SPECIAL_BRC_CTRL: + return brctrl_to_package(go, code, space, framelen); + case SPECIAL_CONFIG: + return config_package(go, code, space); + case SPECIAL_SEQHEAD: + switch (go->format) { + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: + return seqhead_to_package(go, code, space, + mpeg1_sequence_header); + case V4L2_PIX_FMT_MPEG4: + return seqhead_to_package(go, code, space, + mpeg4_sequence_header); + default: + return 0; + } + case SPECIAL_AV_SYNC: + return avsync_to_package(go, code, space); + case SPECIAL_FINAL: + return final_package(go, code, space); + case SPECIAL_AUDIO: + return audio_to_package(go, code, space); + case SPECIAL_MODET: + return modet_to_package(go, code, space); + } + dev_err(go->dev, + "firmware file contains unsupported feature %04x\n", type); + return -1; +} + +int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen) +{ + const struct firmware *fw_entry; + __le16 *code, *src; + int framelen[8] = { }; /* holds the lengths of empty frame templates */ + int codespace = 64 * 1024, i = 0, srclen, chunk_len, chunk_flags; + int mode_flag; + int ret; + + switch (go->format) { + case V4L2_PIX_FMT_MJPEG: + mode_flag = FLAG_MODE_MJPEG; + break; + case V4L2_PIX_FMT_MPEG1: + mode_flag = FLAG_MODE_MPEG1; + break; + case V4L2_PIX_FMT_MPEG2: + mode_flag = FLAG_MODE_MPEG2; + break; + case V4L2_PIX_FMT_MPEG4: + mode_flag = FLAG_MODE_MPEG4; + break; + default: + return -1; + } + if (request_firmware(&fw_entry, GO7007_FW_NAME, go->dev)) { + dev_err(go->dev, + "unable to load firmware from file \"%s\"\n", + GO7007_FW_NAME); + return -1; + } + code = kzalloc(codespace * 2, GFP_KERNEL); + if (code == NULL) + goto fw_failed; + + src = (__le16 *)fw_entry->data; + srclen = fw_entry->size / 2; + while (srclen >= 2) { + chunk_flags = __le16_to_cpu(src[0]); + chunk_len = __le16_to_cpu(src[1]); + if (chunk_len + 2 > srclen) { + dev_err(go->dev, + "firmware file \"%s\" appears to be corrupted\n", + GO7007_FW_NAME); + goto fw_failed; + } + if (chunk_flags & mode_flag) { + if (chunk_flags & FLAG_SPECIAL) { + ret = do_special(go, __le16_to_cpu(src[2]), + &code[i], codespace - i, framelen); + if (ret < 0) { + dev_err(go->dev, + "insufficient memory for firmware construction\n"); + goto fw_failed; + } + i += ret; + } else { + if (codespace - i < chunk_len) { + dev_err(go->dev, + "insufficient memory for firmware construction\n"); + goto fw_failed; + } + memcpy(&code[i], &src[2], chunk_len * 2); + i += chunk_len; + } + } + srclen -= chunk_len + 2; + src += chunk_len + 2; + } + release_firmware(fw_entry); + *fw = (u8 *)code; + *fwlen = i * 2; + return 0; + +fw_failed: + kfree(code); + release_firmware(fw_entry); + return -1; +} + +MODULE_FIRMWARE(GO7007_FW_NAME); diff --git a/drivers/media/usb/go7007/go7007-i2c.c b/drivers/media/usb/go7007/go7007-i2c.c new file mode 100644 index 0000000..55addfa --- /dev/null +++ b/drivers/media/usb/go7007/go7007-i2c.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007-priv.h" + +/********************* Driver for on-board I2C adapter *********************/ + +/* #define GO7007_I2C_DEBUG */ + +#define SPI_I2C_ADDR_BASE 0x1400 +#define STATUS_REG_ADDR (SPI_I2C_ADDR_BASE + 0x2) +#define I2C_CTRL_REG_ADDR (SPI_I2C_ADDR_BASE + 0x6) +#define I2C_DEV_UP_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x7) +#define I2C_LO_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x8) +#define I2C_DATA_REG_ADDR (SPI_I2C_ADDR_BASE + 0x9) +#define I2C_CLKFREQ_REG_ADDR (SPI_I2C_ADDR_BASE + 0xa) + +#define I2C_STATE_MASK 0x0007 +#define I2C_READ_READY_MASK 0x0008 + +/* There is only one I2C port on the TW2804 that feeds all four GO7007 VIPs + * on the Adlink PCI-MPG24, so access is shared between all of them. */ +static DEFINE_MUTEX(adlink_mpg24_i2c_lock); + +static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, + u16 command, int flags, u8 *data) +{ + int i, ret = -EIO; + u16 val; + + if (go->status == STATUS_SHUTDOWN) + return -ENODEV; + +#ifdef GO7007_I2C_DEBUG + if (read) + dev_dbg(go->dev, "go7007-i2c: reading 0x%02x on 0x%02x\n", + command, addr); + else + dev_dbg(go->dev, + "go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n", + *data, command, addr); +#endif + + mutex_lock(&go->hw_lock); + + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { + /* Bridge the I2C port on this GO7007 to the shared bus */ + mutex_lock(&adlink_mpg24_i2c_lock); + go7007_write_addr(go, 0x3c82, 0x0020); + } + + /* Wait for I2C adapter to be ready */ + for (i = 0; i < 10; ++i) { + if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0) + goto i2c_done; + if (!(val & I2C_STATE_MASK)) + break; + msleep(100); + } + if (i == 10) { + dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); + goto i2c_done; + } + + /* Set target register (command) */ + go7007_write_addr(go, I2C_CTRL_REG_ADDR, flags); + go7007_write_addr(go, I2C_LO_ADDR_REG_ADDR, command); + + /* If we're writing, send the data and target address and we're done */ + if (!read) { + go7007_write_addr(go, I2C_DATA_REG_ADDR, *data); + go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR, + (addr << 9) | (command >> 8)); + ret = 0; + goto i2c_done; + } + + /* Otherwise, we're reading. First clear i2c_rx_data_rdy. */ + if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0) + goto i2c_done; + + /* Send the target address plus read flag */ + go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR, + (addr << 9) | 0x0100 | (command >> 8)); + + /* Wait for i2c_rx_data_rdy */ + for (i = 0; i < 10; ++i) { + if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0) + goto i2c_done; + if (val & I2C_READ_READY_MASK) + break; + msleep(100); + } + if (i == 10) { + dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); + goto i2c_done; + } + + /* Retrieve the read byte */ + if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0) + goto i2c_done; + *data = val; + ret = 0; + +i2c_done: + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { + /* Isolate the I2C port on this GO7007 from the shared bus */ + go7007_write_addr(go, 0x3c82, 0x0000); + mutex_unlock(&adlink_mpg24_i2c_lock); + } + mutex_unlock(&go->hw_lock); + return ret; +} + +static int go7007_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + struct go7007 *go = i2c_get_adapdata(adapter); + + if (size != I2C_SMBUS_BYTE_DATA) + return -EIO; + return go7007_i2c_xfer(go, addr, read_write == I2C_SMBUS_READ, command, + flags & I2C_CLIENT_SCCB ? 0x10 : 0x00, &data->byte); +} + +/* VERY LIMITED I2C master xfer function -- only needed because the + * SMBus functions only support 8-bit commands and the SAA7135 uses + * 16-bit commands. The I2C interface on the GO7007, as limited as + * it is, does support this mode. */ + +static int go7007_i2c_master_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct go7007 *go = i2c_get_adapdata(adapter); + int i; + + for (i = 0; i < num; ++i) { + /* We can only do two things here -- write three bytes, or + * write two bytes and read one byte. */ + if (msgs[i].len == 2) { + if (i + 1 == num || msgs[i].addr != msgs[i + 1].addr || + (msgs[i].flags & I2C_M_RD) || + !(msgs[i + 1].flags & I2C_M_RD) || + msgs[i + 1].len != 1) + return -EIO; + if (go7007_i2c_xfer(go, msgs[i].addr, 1, + (msgs[i].buf[0] << 8) | msgs[i].buf[1], + 0x01, &msgs[i + 1].buf[0]) < 0) + return -EIO; + ++i; + } else if (msgs[i].len == 3) { + if (msgs[i].flags & I2C_M_RD) + return -EIO; + if (msgs[i].len != 3) + return -EIO; + if (go7007_i2c_xfer(go, msgs[i].addr, 0, + (msgs[i].buf[0] << 8) | msgs[i].buf[1], + 0x01, &msgs[i].buf[2]) < 0) + return -EIO; + } else + return -EIO; + } + + return num; +} + +static u32 go7007_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_BYTE_DATA; +} + +static struct i2c_algorithm go7007_algo = { + .smbus_xfer = go7007_smbus_xfer, + .master_xfer = go7007_i2c_master_xfer, + .functionality = go7007_functionality, +}; + +static struct i2c_adapter go7007_adap_templ = { + .owner = THIS_MODULE, + .name = "WIS GO7007SB", + .algo = &go7007_algo, +}; + +int go7007_i2c_init(struct go7007 *go) +{ + memcpy(&go->i2c_adapter, &go7007_adap_templ, + sizeof(go7007_adap_templ)); + go->i2c_adapter.dev.parent = go->dev; + i2c_set_adapdata(&go->i2c_adapter, go); + if (i2c_add_adapter(&go->i2c_adapter) < 0) { + dev_err(go->dev, + "go7007-i2c: error: i2c_add_adapter failed\n"); + return -1; + } + return 0; +} diff --git a/drivers/media/usb/go7007/go7007-loader.c b/drivers/media/usb/go7007/go7007-loader.c new file mode 100644 index 0000000..042f78a --- /dev/null +++ b/drivers/media/usb/go7007/go7007-loader.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2008 Sensoray Company Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include + +struct fw_config { + u16 vendor; + u16 product; + const char * const fw_name1; + const char * const fw_name2; +}; + +static struct fw_config fw_configs[] = { + { 0x1943, 0xa250, "go7007/s2250-1.fw", "go7007/s2250-2.fw" }, + { 0x093b, 0xa002, "go7007/px-m402u.fw", NULL }, + { 0x093b, 0xa004, "go7007/px-tv402u.fw", NULL }, + { 0x0eb1, 0x6666, "go7007/lr192.fw", NULL }, + { 0x0eb1, 0x6668, "go7007/wis-startrek.fw", NULL }, + { 0, 0, NULL, NULL } +}; +MODULE_FIRMWARE("go7007/s2250-1.fw"); +MODULE_FIRMWARE("go7007/s2250-2.fw"); +MODULE_FIRMWARE("go7007/px-m402u.fw"); +MODULE_FIRMWARE("go7007/px-tv402u.fw"); +MODULE_FIRMWARE("go7007/lr192.fw"); +MODULE_FIRMWARE("go7007/wis-startrek.fw"); + +static int go7007_loader_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + const struct firmware *fw; + u16 vendor, product; + const char *fw1, *fw2; + int ret; + int i; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + if (!usbdev) + goto failed2; + + if (usbdev->descriptor.bNumConfigurations != 1) { + dev_err(&interface->dev, "can't handle multiple config\n"); + goto failed2; + } + + vendor = le16_to_cpu(usbdev->descriptor.idVendor); + product = le16_to_cpu(usbdev->descriptor.idProduct); + + for (i = 0; fw_configs[i].fw_name1; i++) + if (fw_configs[i].vendor == vendor && + fw_configs[i].product == product) + break; + + /* Should never happen */ + if (fw_configs[i].fw_name1 == NULL) + goto failed2; + + fw1 = fw_configs[i].fw_name1; + fw2 = fw_configs[i].fw_name2; + + dev_info(&interface->dev, "loading firmware %s\n", fw1); + + if (request_firmware(&fw, fw1, &usbdev->dev)) { + dev_err(&interface->dev, + "unable to load firmware from file \"%s\"\n", fw1); + goto failed2; + } + ret = cypress_load_firmware(usbdev, fw, CYPRESS_FX2); + release_firmware(fw); + if (0 != ret) { + dev_err(&interface->dev, "loader download failed\n"); + goto failed2; + } + + if (fw2 == NULL) + return 0; + + if (request_firmware(&fw, fw2, &usbdev->dev)) { + dev_err(&interface->dev, + "unable to load firmware from file \"%s\"\n", fw2); + goto failed2; + } + ret = cypress_load_firmware(usbdev, fw, CYPRESS_FX2); + release_firmware(fw); + if (0 != ret) { + dev_err(&interface->dev, "firmware download failed\n"); + goto failed2; + } + return 0; + +failed2: + usb_put_dev(usbdev); + dev_err(&interface->dev, "probe failed\n"); + return -ENODEV; +} + +static void go7007_loader_disconnect(struct usb_interface *interface) +{ + dev_info(&interface->dev, "disconnect\n"); + usb_put_dev(interface_to_usbdev(interface)); + usb_set_intfdata(interface, NULL); +} + +static const struct usb_device_id go7007_loader_ids[] = { + { USB_DEVICE(0x1943, 0xa250) }, + { USB_DEVICE(0x093b, 0xa002) }, + { USB_DEVICE(0x093b, 0xa004) }, + { USB_DEVICE(0x0eb1, 0x6666) }, + { USB_DEVICE(0x0eb1, 0x6668) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, go7007_loader_ids); + +static struct usb_driver go7007_loader_driver = { + .name = "go7007-loader", + .probe = go7007_loader_probe, + .disconnect = go7007_loader_disconnect, + .id_table = go7007_loader_ids, +}; + +module_usb_driver(go7007_loader_driver); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("firmware loader for go7007-usb"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/usb/go7007/go7007-priv.h b/drivers/media/usb/go7007/go7007-priv.h new file mode 100644 index 0000000..2251c3f --- /dev/null +++ b/drivers/media/usb/go7007/go7007-priv.h @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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. + */ + +/* + * This is the private include file for the go7007 driver. It should not + * be included by anybody but the driver itself, and especially not by + * user-space applications. + */ + +#include +#include +#include +#include + +struct go7007; + +/* IDs to activate board-specific support code */ +#define GO7007_BOARDID_MATRIX_II 0 +#define GO7007_BOARDID_MATRIX_RELOAD 1 +#define GO7007_BOARDID_STAR_TREK 2 +#define GO7007_BOARDID_PCI_VOYAGER 3 +#define GO7007_BOARDID_XMEN 4 +#define GO7007_BOARDID_XMEN_II 5 +#define GO7007_BOARDID_XMEN_III 6 +#define GO7007_BOARDID_MATRIX_REV 7 +#define GO7007_BOARDID_PX_M402U 8 +#define GO7007_BOARDID_PX_TV402U 9 +#define GO7007_BOARDID_LIFEVIEW_LR192 10 /* TV Walker Ultra */ +#define GO7007_BOARDID_ENDURA 11 +#define GO7007_BOARDID_ADLINK_MPG24 12 +#define GO7007_BOARDID_SENSORAY_2250 13 /* Sensoray 2250/2251 */ +#define GO7007_BOARDID_ADS_USBAV_709 14 + +/* Various characteristics of each board */ +#define GO7007_BOARD_HAS_AUDIO (1<<0) +#define GO7007_BOARD_USE_ONBOARD_I2C (1<<1) +#define GO7007_BOARD_HAS_TUNER (1<<2) + +/* Characteristics of sensor devices */ +#define GO7007_SENSOR_VALID_POLAR (1<<0) +#define GO7007_SENSOR_HREF_POLAR (1<<1) +#define GO7007_SENSOR_VREF_POLAR (1<<2) +#define GO7007_SENSOR_FIELD_ID_POLAR (1<<3) +#define GO7007_SENSOR_BIT_WIDTH (1<<4) +#define GO7007_SENSOR_VALID_ENABLE (1<<5) +#define GO7007_SENSOR_656 (1<<6) +#define GO7007_SENSOR_CONFIG_MASK 0x7f +#define GO7007_SENSOR_TV (1<<7) +#define GO7007_SENSOR_VBI (1<<8) +#define GO7007_SENSOR_SCALING (1<<9) +#define GO7007_SENSOR_SAA7115 (1<<10) + +/* Characteristics of audio sensor devices */ +#define GO7007_AUDIO_I2S_MODE_1 (1) +#define GO7007_AUDIO_I2S_MODE_2 (2) +#define GO7007_AUDIO_I2S_MODE_3 (3) +#define GO7007_AUDIO_BCLK_POLAR (1<<2) +#define GO7007_AUDIO_WORD_14 (14<<4) +#define GO7007_AUDIO_WORD_16 (16<<4) +#define GO7007_AUDIO_ONE_CHANNEL (1<<11) +#define GO7007_AUDIO_I2S_MASTER (1<<16) +#define GO7007_AUDIO_OKI_MODE (1<<17) + +#define GO7007_CID_CUSTOM_BASE (V4L2_CID_DETECT_CLASS_BASE + 0x1000) +#define V4L2_CID_PIXEL_THRESHOLD0 (GO7007_CID_CUSTOM_BASE+1) +#define V4L2_CID_MOTION_THRESHOLD0 (GO7007_CID_CUSTOM_BASE+2) +#define V4L2_CID_MB_THRESHOLD0 (GO7007_CID_CUSTOM_BASE+3) +#define V4L2_CID_PIXEL_THRESHOLD1 (GO7007_CID_CUSTOM_BASE+4) +#define V4L2_CID_MOTION_THRESHOLD1 (GO7007_CID_CUSTOM_BASE+5) +#define V4L2_CID_MB_THRESHOLD1 (GO7007_CID_CUSTOM_BASE+6) +#define V4L2_CID_PIXEL_THRESHOLD2 (GO7007_CID_CUSTOM_BASE+7) +#define V4L2_CID_MOTION_THRESHOLD2 (GO7007_CID_CUSTOM_BASE+8) +#define V4L2_CID_MB_THRESHOLD2 (GO7007_CID_CUSTOM_BASE+9) +#define V4L2_CID_PIXEL_THRESHOLD3 (GO7007_CID_CUSTOM_BASE+10) +#define V4L2_CID_MOTION_THRESHOLD3 (GO7007_CID_CUSTOM_BASE+11) +#define V4L2_CID_MB_THRESHOLD3 (GO7007_CID_CUSTOM_BASE+12) + +struct go7007_board_info { + unsigned int flags; + int hpi_buffer_cap; + unsigned int sensor_flags; + int sensor_width; + int sensor_height; + int sensor_framerate; + int sensor_h_offset; + int sensor_v_offset; + unsigned int audio_flags; + int audio_rate; + int audio_bclk_div; + int audio_main_div; + int num_i2c_devs; + struct go_i2c { + const char *type; + unsigned int is_video:1; + unsigned int is_audio:1; + int addr; + u32 flags; + } i2c_devs[5]; + int num_inputs; + struct { + int video_input; + int audio_index; + char *name; + } inputs[4]; + int video_config; + int num_aud_inputs; + struct { + int audio_input; + char *name; + } aud_inputs[3]; +}; + +struct go7007_hpi_ops { + int (*interface_reset)(struct go7007 *go); + int (*write_interrupt)(struct go7007 *go, int addr, int data); + int (*read_interrupt)(struct go7007 *go); + int (*stream_start)(struct go7007 *go); + int (*stream_stop)(struct go7007 *go); + int (*send_firmware)(struct go7007 *go, u8 *data, int len); + int (*send_command)(struct go7007 *go, unsigned int cmd, void *arg); + void (*release)(struct go7007 *go); +}; + +/* The video buffer size must be a multiple of PAGE_SIZE */ +#define GO7007_BUF_PAGES (128 * 1024 / PAGE_SIZE) +#define GO7007_BUF_SIZE (GO7007_BUF_PAGES << PAGE_SHIFT) + +struct go7007_buffer { + struct vb2_buffer vb; + struct list_head list; + unsigned int frame_offset; + u32 modet_active; +}; + +#define GO7007_RATIO_1_1 0 +#define GO7007_RATIO_4_3 1 +#define GO7007_RATIO_16_9 2 + +enum go7007_parser_state { + STATE_DATA, + STATE_00, + STATE_00_00, + STATE_00_00_01, + STATE_FF, + STATE_VBI_LEN_A, + STATE_VBI_LEN_B, + STATE_MODET_MAP, + STATE_UNPARSED, +}; + +struct go7007 { + struct device *dev; + u8 bus_info[32]; + const struct go7007_board_info *board_info; + unsigned int board_id; + int tuner_type; + int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */ + char name[64]; + struct video_device vdev; + void *boot_fw; + unsigned boot_fw_len; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *mpeg_video_encoding; + struct v4l2_ctrl *mpeg_video_gop_size; + struct v4l2_ctrl *mpeg_video_gop_closure; + struct v4l2_ctrl *mpeg_video_bitrate; + struct v4l2_ctrl *mpeg_video_aspect_ratio; + struct v4l2_ctrl *mpeg_video_b_frames; + struct v4l2_ctrl *mpeg_video_rep_seqheader; + struct v4l2_ctrl *modet_mode; + enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status; + spinlock_t spinlock; + struct mutex hw_lock; + struct mutex serialize_lock; + int audio_enabled; + struct v4l2_subdev *sd_video; + struct v4l2_subdev *sd_audio; + u8 usb_buf[16]; + + /* Video input */ + int input; + int aud_input; + enum { GO7007_STD_NTSC, GO7007_STD_PAL, GO7007_STD_OTHER } standard; + v4l2_std_id std; + int sensor_framerate; + int width; + int height; + int encoder_h_offset; + int encoder_v_offset; + unsigned int encoder_h_halve:1; + unsigned int encoder_v_halve:1; + unsigned int encoder_subsample:1; + + /* Encoder config */ + u32 format; + int bitrate; + int fps_scale; + int pali; + int aspect_ratio; + int gop_size; + unsigned int ipb:1; + unsigned int closed_gop:1; + unsigned int repeat_seqhead:1; + unsigned int seq_header_enable:1; + unsigned int gop_header_enable:1; + unsigned int dvd_mode:1; + unsigned int interlace_coding:1; + + /* Motion detection */ + unsigned int modet_enable:1; + struct { + unsigned int enable:1; + int pixel_threshold; + int motion_threshold; + int mb_threshold; + } modet[4]; + unsigned char modet_map[1624]; + unsigned char active_map[216]; + u32 modet_event_status; + + /* Video streaming */ + struct mutex queue_lock; + struct vb2_queue vidq; + enum go7007_parser_state state; + int parse_length; + u16 modet_word; + int seen_frame; + u32 next_seq; + struct list_head vidq_active; + wait_queue_head_t frame_waitq; + struct go7007_buffer *active_buf; + + /* Audio streaming */ + void (*audio_deliver)(struct go7007 *go, u8 *buf, int length); + void *snd_context; + + /* I2C */ + int i2c_adapter_online; + struct i2c_adapter i2c_adapter; + + /* HPI driver */ + struct go7007_hpi_ops *hpi_ops; + void *hpi_context; + int interrupt_available; + wait_queue_head_t interrupt_waitq; + unsigned short interrupt_value; + unsigned short interrupt_data; +}; + +static inline struct go7007 *to_go7007(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct go7007, v4l2_dev); +} + +/* All of these must be called with the hpi_lock mutex held! */ +#define go7007_interface_reset(go) \ + ((go)->hpi_ops->interface_reset(go)) +#define go7007_write_interrupt(go, x, y) \ + ((go)->hpi_ops->write_interrupt)((go), (x), (y)) +#define go7007_stream_start(go) \ + ((go)->hpi_ops->stream_start(go)) +#define go7007_stream_stop(go) \ + ((go)->hpi_ops->stream_stop(go)) +#define go7007_send_firmware(go, x, y) \ + ((go)->hpi_ops->send_firmware)((go), (x), (y)) +#define go7007_write_addr(go, x, y) \ + ((go)->hpi_ops->write_interrupt)((go), (x)|0x8000, (y)) + +/* go7007-driver.c */ +int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data); +int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data); +int go7007_boot_encoder(struct go7007 *go, int init_i2c); +int go7007_reset_encoder(struct go7007 *go); +int go7007_register_encoder(struct go7007 *go, unsigned num_i2c_devs); +int go7007_start_encoder(struct go7007 *go); +void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length); +struct go7007 *go7007_alloc(const struct go7007_board_info *board, + struct device *dev); +void go7007_update_board(struct go7007 *go); + +/* go7007-fw.c */ +int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen); + +/* go7007-i2c.c */ +int go7007_i2c_init(struct go7007 *go); +int go7007_i2c_remove(struct go7007 *go); + +/* go7007-v4l2.c */ +int go7007_v4l2_init(struct go7007 *go); +int go7007_v4l2_ctrl_init(struct go7007 *go); +void go7007_v4l2_remove(struct go7007 *go); + +/* snd-go7007.c */ +int go7007_snd_init(struct go7007 *go); +int go7007_snd_remove(struct go7007 *go); diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c new file mode 100644 index 0000000..ece27ec --- /dev/null +++ b/drivers/media/usb/go7007/go7007-usb.c @@ -0,0 +1,1345 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007-priv.h" + +static unsigned int assume_endura; +module_param(assume_endura, int, 0644); +MODULE_PARM_DESC(assume_endura, + "when probing fails, hardware is a Pelco Endura"); + +/* #define GO7007_I2C_DEBUG */ /* for debugging the EZ-USB I2C adapter */ + +#define HPI_STATUS_ADDR 0xFFF4 +#define INT_PARAM_ADDR 0xFFF6 +#define INT_INDEX_ADDR 0xFFF8 + +/* + * Pipes on EZ-USB interface: + * 0 snd - Control + * 0 rcv - Control + * 2 snd - Download firmware (control) + * 4 rcv - Read Interrupt (interrupt) + * 6 rcv - Read Video (bulk) + * 8 rcv - Read Audio (bulk) + */ + +#define GO7007_USB_EZUSB (1<<0) +#define GO7007_USB_EZUSB_I2C (1<<1) + +struct go7007_usb_board { + unsigned int flags; + struct go7007_board_info main_info; +}; + +struct go7007_usb { + const struct go7007_usb_board *board; + struct mutex i2c_lock; + struct usb_device *usbdev; + struct urb *video_urbs[8]; + struct urb *audio_urbs[8]; + struct urb *intr_urb; +}; + +/*********************** Product specification data ***********************/ + +static const struct go7007_usb_board board_matrix_ii = { + .flags = GO7007_USB_EZUSB, + .main_info = { + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_VALID_ENABLE | + GO7007_SENSOR_TV | + GO7007_SENSOR_SAA7115 | + GO7007_SENSOR_VBI | + GO7007_SENSOR_SCALING, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "saa7115", + .addr = 0x20, + .is_video = 1, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + { + .video_input = 9, + .name = "S-Video", + }, + }, + .video_config = SAA7115_IDQ_IS_DEFAULT, + }, +}; + +static const struct go7007_usb_board board_matrix_reload = { + .flags = GO7007_USB_EZUSB, + .main_info = { + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "saa7113", + .addr = 0x25, + .is_video = 1, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + { + .video_input = 9, + .name = "S-Video", + }, + }, + .video_config = SAA7115_IDQ_IS_DEFAULT, + }, +}; + +static const struct go7007_usb_board board_star_trek = { + .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, + .main_info = { + .flags = GO7007_BOARD_HAS_AUDIO, /* | + GO7007_BOARD_HAS_TUNER, */ + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_VALID_ENABLE | + GO7007_SENSOR_TV | + GO7007_SENSOR_SAA7115 | + GO7007_SENSOR_VBI | + GO7007_SENSOR_SCALING, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_WORD_16, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "saa7115", + .addr = 0x20, + .is_video = 1, + }, + }, + .num_inputs = 2, + .inputs = { + /* { + * .video_input = 3, + * .audio_index = AUDIO_TUNER, + * .name = "Tuner", + * }, + */ + { + .video_input = 1, + /* .audio_index = AUDIO_EXTERN, */ + .name = "Composite", + }, + { + .video_input = 8, + /* .audio_index = AUDIO_EXTERN, */ + .name = "S-Video", + }, + }, + .video_config = SAA7115_IDQ_IS_DEFAULT, + }, +}; + +static const struct go7007_usb_board board_px_tv402u = { + .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, + .main_info = { + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_HAS_TUNER, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_VALID_ENABLE | + GO7007_SENSOR_TV | + GO7007_SENSOR_SAA7115 | + GO7007_SENSOR_VBI | + GO7007_SENSOR_SCALING, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_WORD_16, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .num_i2c_devs = 5, + .i2c_devs = { + { + .type = "saa7115", + .addr = 0x20, + .is_video = 1, + }, + { + .type = "uda1342", + .addr = 0x1a, + .is_audio = 1, + }, + { + .type = "tuner", + .addr = 0x60, + }, + { + .type = "tuner", + .addr = 0x43, + }, + { + .type = "sony-btf-mpx", + .addr = 0x44, + }, + }, + .num_inputs = 3, + .inputs = { + { + .video_input = 3, + .audio_index = 0, + .name = "Tuner", + }, + { + .video_input = 1, + .audio_index = 1, + .name = "Composite", + }, + { + .video_input = 8, + .audio_index = 1, + .name = "S-Video", + }, + }, + .video_config = SAA7115_IDQ_IS_DEFAULT, + .num_aud_inputs = 2, + .aud_inputs = { + { + .audio_input = UDA1342_IN2, + .name = "Tuner", + }, + { + .audio_input = UDA1342_IN1, + .name = "Line In", + }, + }, + }, +}; + +static const struct go7007_usb_board board_xmen = { + .flags = 0, + .main_info = { + .flags = GO7007_BOARD_USE_ONBOARD_I2C, + .hpi_buffer_cap = 0, + .sensor_flags = GO7007_SENSOR_VREF_POLAR, + .sensor_width = 320, + .sensor_height = 240, + .sensor_framerate = 30030, + .audio_flags = GO7007_AUDIO_ONE_CHANNEL | + GO7007_AUDIO_I2S_MODE_3 | + GO7007_AUDIO_WORD_14 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_BCLK_POLAR | + GO7007_AUDIO_OKI_MODE, + .audio_rate = 8000, + .audio_bclk_div = 48, + .audio_main_div = 1, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "ov7640", + .addr = 0x21, + }, + }, + .num_inputs = 1, + .inputs = { + { + .name = "Camera", + }, + }, + }, +}; + +static const struct go7007_usb_board board_matrix_revolution = { + .flags = GO7007_USB_EZUSB, + .main_info = { + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "tw9903", + .is_video = 1, + .addr = 0x44, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 2, + .name = "Composite", + }, + { + .video_input = 8, + .name = "S-Video", + }, + }, + }, +}; + +static const struct go7007_usb_board board_lifeview_lr192 = { + .flags = GO7007_USB_EZUSB, + .main_info = { + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_VALID_ENABLE | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI | + GO7007_SENSOR_SCALING, + .num_i2c_devs = 0, + .num_inputs = 1, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + }, + }, +}; + +static const struct go7007_usb_board board_endura = { + .flags = 0, + .main_info = { + .flags = 0, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .audio_rate = 8000, + .audio_bclk_div = 48, + .audio_main_div = 8, + .hpi_buffer_cap = 0, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV, + .sensor_h_offset = 8, + .num_i2c_devs = 0, + .num_inputs = 1, + .inputs = { + { + .name = "Camera", + }, + }, + }, +}; + +static const struct go7007_usb_board board_adlink_mpg24 = { + .flags = 0, + .main_info = { + .flags = GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 0, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "tw2804", + .addr = 0x00, /* yes, really */ + .flags = I2C_CLIENT_TEN, + .is_video = 1, + }, + }, + .num_inputs = 1, + .inputs = { + { + .name = "Composite", + }, + }, + }, +}; + +static const struct go7007_usb_board board_sensoray_2250 = { + .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, + .main_info = { + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .flags = GO7007_BOARD_HAS_AUDIO, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "s2250", + .addr = 0x43, + .is_video = 1, + .is_audio = 1, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + { + .video_input = 1, + .name = "S-Video", + }, + }, + .num_aud_inputs = 3, + .aud_inputs = { + { + .audio_input = 0, + .name = "Line In", + }, + { + .audio_input = 1, + .name = "Mic", + }, + { + .audio_input = 2, + .name = "Mic Boost", + }, + }, + }, +}; + +static const struct go7007_usb_board board_ads_usbav_709 = { + .flags = GO7007_USB_EZUSB, + .main_info = { + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "tw9906", + .is_video = 1, + .addr = 0x44, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + { + .video_input = 10, + .name = "S-Video", + }, + }, + }, +}; + +static const struct usb_device_id go7007_usb_id_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x200, /* Revision number of XMen */ + .bcdDevice_hi = 0x200, + .bInterfaceClass = 255, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 255, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x202, /* Revision number of Matrix II */ + .bcdDevice_hi = 0x202, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_II, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x204, /* Revision number of Matrix */ + .bcdDevice_hi = 0x204, /* Reloaded */ + .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_RELOAD, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x205, /* Revision number of XMen-II */ + .bcdDevice_hi = 0x205, + .bInterfaceClass = 255, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 255, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_II, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x208, /* Revision number of Star Trek */ + .bcdDevice_hi = 0x208, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_STAR_TREK, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x209, /* Revision number of XMen-III */ + .bcdDevice_hi = 0x209, + .bInterfaceClass = 255, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 255, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_III, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x210, /* Revision number of Matrix */ + .bcdDevice_hi = 0x210, /* Revolution */ + .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_REV, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x093b, /* Vendor ID of Plextor */ + .idProduct = 0xa102, /* Product ID of M402U */ + .bcdDevice_lo = 0x1, /* revision number of Blueberry */ + .bcdDevice_hi = 0x1, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_M402U, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x093b, /* Vendor ID of Plextor */ + .idProduct = 0xa104, /* Product ID of TV402U */ + .bcdDevice_lo = 0x1, + .bcdDevice_hi = 0x1, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_TV402U, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x10fd, /* Vendor ID of Anubis Electronics */ + .idProduct = 0xde00, /* Product ID of Lifeview LR192 */ + .bcdDevice_lo = 0x1, + .bcdDevice_hi = 0x1, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_LIFEVIEW_LR192, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x1943, /* Vendor ID Sensoray */ + .idProduct = 0x2250, /* Product ID of 2250/2251 */ + .bcdDevice_lo = 0x1, + .bcdDevice_hi = 0x1, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_SENSORAY_2250, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x06e1, /* Vendor ID of ADS Technologies */ + .idProduct = 0x0709, /* Product ID of DVD Xpress DX2 */ + .bcdDevice_lo = 0x204, + .bcdDevice_hi = 0x204, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_ADS_USBAV_709, + }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, go7007_usb_id_table); + +/********************* Driver for EZ-USB HPI interface *********************/ + +static int go7007_usb_vendor_request(struct go7007 *go, int request, + int value, int index, void *transfer_buffer, int length, int in) +{ + struct go7007_usb *usb = go->hpi_context; + int timeout = 5000; + + if (in) { + return usb_control_msg(usb->usbdev, + usb_rcvctrlpipe(usb->usbdev, 0), request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + value, index, transfer_buffer, length, timeout); + } else { + return usb_control_msg(usb->usbdev, + usb_sndctrlpipe(usb->usbdev, 0), request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, transfer_buffer, length, timeout); + } +} + +static int go7007_usb_interface_reset(struct go7007 *go) +{ + struct go7007_usb *usb = go->hpi_context; + u16 intr_val, intr_data; + + if (go->status == STATUS_SHUTDOWN) + return -1; + /* Reset encoder */ + if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0) + return -1; + msleep(100); + + if (usb->board->flags & GO7007_USB_EZUSB) { + /* Reset buffer in EZ-USB */ + pr_debug("resetting EZ-USB buffers\n"); + if (go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0 || + go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0) + return -1; + + /* Reset encoder again */ + if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0) + return -1; + msleep(100); + } + + /* Wait for an interrupt to indicate successful hardware reset */ + if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || + (intr_val & ~0x1) != 0x55aa) { + dev_err(go->dev, "unable to reset the USB interface\n"); + return -1; + } + return 0; +} + +static int go7007_usb_ezusb_write_interrupt(struct go7007 *go, + int addr, int data) +{ + struct go7007_usb *usb = go->hpi_context; + int i, r; + u16 status_reg = 0; + int timeout = 500; + + pr_debug("WriteInterrupt: %04x %04x\n", addr, data); + + for (i = 0; i < 100; ++i) { + r = usb_control_msg(usb->usbdev, + usb_rcvctrlpipe(usb->usbdev, 0), 0x14, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0, HPI_STATUS_ADDR, go->usb_buf, + sizeof(status_reg), timeout); + if (r < 0) + break; + status_reg = le16_to_cpu(*((u16 *)go->usb_buf)); + if (!(status_reg & 0x0010)) + break; + msleep(10); + } + if (r < 0) + goto write_int_error; + if (i == 100) { + dev_err(go->dev, "device is hung, status reg = 0x%04x\n", status_reg); + return -1; + } + r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), 0x12, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, data, + INT_PARAM_ADDR, NULL, 0, timeout); + if (r < 0) + goto write_int_error; + r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), + 0x12, USB_TYPE_VENDOR | USB_RECIP_DEVICE, addr, + INT_INDEX_ADDR, NULL, 0, timeout); + if (r < 0) + goto write_int_error; + return 0; + +write_int_error: + dev_err(go->dev, "error in WriteInterrupt: %d\n", r); + return r; +} + +static int go7007_usb_onboard_write_interrupt(struct go7007 *go, + int addr, int data) +{ + struct go7007_usb *usb = go->hpi_context; + int r; + int timeout = 500; + + pr_debug("WriteInterrupt: %04x %04x\n", addr, data); + + go->usb_buf[0] = data & 0xff; + go->usb_buf[1] = data >> 8; + go->usb_buf[2] = addr & 0xff; + go->usb_buf[3] = addr >> 8; + go->usb_buf[4] = go->usb_buf[5] = go->usb_buf[6] = go->usb_buf[7] = 0; + r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 2), 0x00, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0x55aa, + 0xf0f0, go->usb_buf, 8, timeout); + if (r < 0) { + dev_err(go->dev, "error in WriteInterrupt: %d\n", r); + return r; + } + return 0; +} + +static void go7007_usb_readinterrupt_complete(struct urb *urb) +{ + struct go7007 *go = (struct go7007 *)urb->context; + u16 *regs = (u16 *)urb->transfer_buffer; + int status = urb->status; + + if (status) { + if (status != -ESHUTDOWN && + go->status != STATUS_SHUTDOWN) { + dev_err(go->dev, "error in read interrupt: %d\n", urb->status); + } else { + wake_up(&go->interrupt_waitq); + return; + } + } else if (urb->actual_length != urb->transfer_buffer_length) { + dev_err(go->dev, "short read in interrupt pipe!\n"); + } else { + go->interrupt_available = 1; + go->interrupt_data = __le16_to_cpu(regs[0]); + go->interrupt_value = __le16_to_cpu(regs[1]); + pr_debug("ReadInterrupt: %04x %04x\n", + go->interrupt_value, go->interrupt_data); + } + + wake_up(&go->interrupt_waitq); +} + +static int go7007_usb_read_interrupt(struct go7007 *go) +{ + struct go7007_usb *usb = go->hpi_context; + int r; + + r = usb_submit_urb(usb->intr_urb, GFP_KERNEL); + if (r < 0) { + dev_err(go->dev, "unable to submit interrupt urb: %d\n", r); + return r; + } + return 0; +} + +static void go7007_usb_read_video_pipe_complete(struct urb *urb) +{ + struct go7007 *go = (struct go7007 *)urb->context; + int r, status = urb->status; + + if (!vb2_is_streaming(&go->vidq)) { + wake_up_interruptible(&go->frame_waitq); + return; + } + if (status) { + dev_err(go->dev, "error in video pipe: %d\n", status); + return; + } + if (urb->actual_length != urb->transfer_buffer_length) { + dev_err(go->dev, "short read in video pipe!\n"); + return; + } + go7007_parse_video_stream(go, urb->transfer_buffer, urb->actual_length); + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r < 0) + dev_err(go->dev, "error in video pipe: %d\n", r); +} + +static void go7007_usb_read_audio_pipe_complete(struct urb *urb) +{ + struct go7007 *go = (struct go7007 *)urb->context; + int r, status = urb->status; + + if (!vb2_is_streaming(&go->vidq)) + return; + if (status) { + dev_err(go->dev, "error in audio pipe: %d\n", + status); + return; + } + if (urb->actual_length != urb->transfer_buffer_length) { + dev_err(go->dev, "short read in audio pipe!\n"); + return; + } + if (go->audio_deliver != NULL) + go->audio_deliver(go, urb->transfer_buffer, urb->actual_length); + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r < 0) + dev_err(go->dev, "error in audio pipe: %d\n", r); +} + +static int go7007_usb_stream_start(struct go7007 *go) +{ + struct go7007_usb *usb = go->hpi_context; + int i, r; + + for (i = 0; i < 8; ++i) { + r = usb_submit_urb(usb->video_urbs[i], GFP_KERNEL); + if (r < 0) { + dev_err(go->dev, "error submitting video urb %d: %d\n", i, r); + goto video_submit_failed; + } + } + if (!go->audio_enabled) + return 0; + + for (i = 0; i < 8; ++i) { + r = usb_submit_urb(usb->audio_urbs[i], GFP_KERNEL); + if (r < 0) { + dev_err(go->dev, "error submitting audio urb %d: %d\n", i, r); + goto audio_submit_failed; + } + } + return 0; + +audio_submit_failed: + for (i = 0; i < 7; ++i) + usb_kill_urb(usb->audio_urbs[i]); +video_submit_failed: + for (i = 0; i < 8; ++i) + usb_kill_urb(usb->video_urbs[i]); + return -1; +} + +static int go7007_usb_stream_stop(struct go7007 *go) +{ + struct go7007_usb *usb = go->hpi_context; + int i; + + if (go->status == STATUS_SHUTDOWN) + return 0; + for (i = 0; i < 8; ++i) + usb_kill_urb(usb->video_urbs[i]); + if (go->audio_enabled) + for (i = 0; i < 8; ++i) + usb_kill_urb(usb->audio_urbs[i]); + return 0; +} + +static int go7007_usb_send_firmware(struct go7007 *go, u8 *data, int len) +{ + struct go7007_usb *usb = go->hpi_context; + int transferred, pipe; + int timeout = 500; + + pr_debug("DownloadBuffer sending %d bytes\n", len); + + if (usb->board->flags & GO7007_USB_EZUSB) + pipe = usb_sndbulkpipe(usb->usbdev, 2); + else + pipe = usb_sndbulkpipe(usb->usbdev, 3); + + return usb_bulk_msg(usb->usbdev, pipe, data, len, + &transferred, timeout); +} + +static void go7007_usb_release(struct go7007 *go) +{ + struct go7007_usb *usb = go->hpi_context; + struct urb *vurb, *aurb; + int i; + + if (usb->intr_urb) { + usb_kill_urb(usb->intr_urb); + kfree(usb->intr_urb->transfer_buffer); + usb_free_urb(usb->intr_urb); + } + + /* Free USB-related structs */ + for (i = 0; i < 8; ++i) { + vurb = usb->video_urbs[i]; + if (vurb) { + usb_kill_urb(vurb); + kfree(vurb->transfer_buffer); + usb_free_urb(vurb); + } + aurb = usb->audio_urbs[i]; + if (aurb) { + usb_kill_urb(aurb); + kfree(aurb->transfer_buffer); + usb_free_urb(aurb); + } + } + + kfree(go->hpi_context); +} + +static struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = { + .interface_reset = go7007_usb_interface_reset, + .write_interrupt = go7007_usb_ezusb_write_interrupt, + .read_interrupt = go7007_usb_read_interrupt, + .stream_start = go7007_usb_stream_start, + .stream_stop = go7007_usb_stream_stop, + .send_firmware = go7007_usb_send_firmware, + .release = go7007_usb_release, +}; + +static struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = { + .interface_reset = go7007_usb_interface_reset, + .write_interrupt = go7007_usb_onboard_write_interrupt, + .read_interrupt = go7007_usb_read_interrupt, + .stream_start = go7007_usb_stream_start, + .stream_stop = go7007_usb_stream_stop, + .send_firmware = go7007_usb_send_firmware, + .release = go7007_usb_release, +}; + +/********************* Driver for EZ-USB I2C adapter *********************/ + +static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct go7007 *go = i2c_get_adapdata(adapter); + struct go7007_usb *usb = go->hpi_context; + u8 *buf = go->usb_buf; + int buf_len, i; + int ret = -EIO; + + if (go->status == STATUS_SHUTDOWN) + return -ENODEV; + + mutex_lock(&usb->i2c_lock); + + for (i = 0; i < num; ++i) { + /* The hardware command is "write some bytes then read some + * bytes", so we try to coalesce a write followed by a read + * into a single USB transaction */ + if (i + 1 < num && msgs[i].addr == msgs[i + 1].addr && + !(msgs[i].flags & I2C_M_RD) && + (msgs[i + 1].flags & I2C_M_RD)) { +#ifdef GO7007_I2C_DEBUG + pr_debug("i2c write/read %d/%d bytes on %02x\n", + msgs[i].len, msgs[i + 1].len, msgs[i].addr); +#endif + buf[0] = 0x01; + buf[1] = msgs[i].len + 1; + buf[2] = msgs[i].addr << 1; + memcpy(&buf[3], msgs[i].buf, msgs[i].len); + buf_len = msgs[i].len + 3; + buf[buf_len++] = msgs[++i].len; + } else if (msgs[i].flags & I2C_M_RD) { +#ifdef GO7007_I2C_DEBUG + pr_debug("i2c read %d bytes on %02x\n", + msgs[i].len, msgs[i].addr); +#endif + buf[0] = 0x01; + buf[1] = 1; + buf[2] = msgs[i].addr << 1; + buf[3] = msgs[i].len; + buf_len = 4; + } else { +#ifdef GO7007_I2C_DEBUG + pr_debug("i2c write %d bytes on %02x\n", + msgs[i].len, msgs[i].addr); +#endif + buf[0] = 0x00; + buf[1] = msgs[i].len + 1; + buf[2] = msgs[i].addr << 1; + memcpy(&buf[3], msgs[i].buf, msgs[i].len); + buf_len = msgs[i].len + 3; + buf[buf_len++] = 0; + } + if (go7007_usb_vendor_request(go, 0x24, 0, 0, + buf, buf_len, 0) < 0) + goto i2c_done; + if (msgs[i].flags & I2C_M_RD) { + memset(buf, 0, msgs[i].len + 1); + if (go7007_usb_vendor_request(go, 0x25, 0, 0, buf, + msgs[i].len + 1, 1) < 0) + goto i2c_done; + memcpy(msgs[i].buf, buf + 1, msgs[i].len); + } + } + ret = num; + +i2c_done: + mutex_unlock(&usb->i2c_lock); + return ret; +} + +static u32 go7007_usb_functionality(struct i2c_adapter *adapter) +{ + /* No errors are reported by the hardware, so we don't bother + * supporting quick writes to avoid confusing probing */ + return (I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK; +} + +static struct i2c_algorithm go7007_usb_algo = { + .master_xfer = go7007_usb_i2c_master_xfer, + .functionality = go7007_usb_functionality, +}; + +static struct i2c_adapter go7007_usb_adap_templ = { + .owner = THIS_MODULE, + .name = "WIS GO7007SB EZ-USB", + .algo = &go7007_usb_algo, +}; + +/********************* USB add/remove functions *********************/ + +static int go7007_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct go7007 *go; + struct go7007_usb *usb; + const struct go7007_usb_board *board; + struct usb_device *usbdev = interface_to_usbdev(intf); + unsigned num_i2c_devs; + char *name; + int video_pipe, i, v_urb_len; + + pr_debug("probing new GO7007 USB board\n"); + + switch (id->driver_info) { + case GO7007_BOARDID_MATRIX_II: + name = "WIS Matrix II or compatible"; + board = &board_matrix_ii; + break; + case GO7007_BOARDID_MATRIX_RELOAD: + name = "WIS Matrix Reloaded or compatible"; + board = &board_matrix_reload; + break; + case GO7007_BOARDID_MATRIX_REV: + name = "WIS Matrix Revolution or compatible"; + board = &board_matrix_revolution; + break; + case GO7007_BOARDID_STAR_TREK: + name = "WIS Star Trek or compatible"; + board = &board_star_trek; + break; + case GO7007_BOARDID_XMEN: + name = "WIS XMen or compatible"; + board = &board_xmen; + break; + case GO7007_BOARDID_XMEN_II: + name = "WIS XMen II or compatible"; + board = &board_xmen; + break; + case GO7007_BOARDID_XMEN_III: + name = "WIS XMen III or compatible"; + board = &board_xmen; + break; + case GO7007_BOARDID_PX_M402U: + name = "Plextor PX-M402U"; + board = &board_matrix_ii; + break; + case GO7007_BOARDID_PX_TV402U: + name = "Plextor PX-TV402U (unknown tuner)"; + board = &board_px_tv402u; + break; + case GO7007_BOARDID_LIFEVIEW_LR192: + dev_err(&intf->dev, "The Lifeview TV Walker Ultra is not supported. Sorry!\n"); + return -ENODEV; + name = "Lifeview TV Walker Ultra"; + board = &board_lifeview_lr192; + break; + case GO7007_BOARDID_SENSORAY_2250: + dev_info(&intf->dev, "Sensoray 2250 found\n"); + name = "Sensoray 2250/2251"; + board = &board_sensoray_2250; + break; + case GO7007_BOARDID_ADS_USBAV_709: + name = "ADS Tech DVD Xpress DX2"; + board = &board_ads_usbav_709; + break; + default: + dev_err(&intf->dev, "unknown board ID %d!\n", + (unsigned int)id->driver_info); + return -ENODEV; + } + + go = go7007_alloc(&board->main_info, &intf->dev); + if (go == NULL) + return -ENOMEM; + + usb = kzalloc(sizeof(struct go7007_usb), GFP_KERNEL); + if (usb == NULL) { + kfree(go); + return -ENOMEM; + } + + usb->board = board; + usb->usbdev = usbdev; + usb_make_path(usbdev, go->bus_info, sizeof(go->bus_info)); + go->board_id = id->driver_info; + strncpy(go->name, name, sizeof(go->name)); + if (board->flags & GO7007_USB_EZUSB) + go->hpi_ops = &go7007_usb_ezusb_hpi_ops; + else + go->hpi_ops = &go7007_usb_onboard_hpi_ops; + go->hpi_context = usb; + + /* Allocate the URB and buffer for receiving incoming interrupts */ + usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (usb->intr_urb == NULL) + goto allocfail; + usb->intr_urb->transfer_buffer = kmalloc(2*sizeof(u16), GFP_KERNEL); + if (usb->intr_urb->transfer_buffer == NULL) + goto allocfail; + + if (go->board_id == GO7007_BOARDID_SENSORAY_2250) + usb_fill_bulk_urb(usb->intr_urb, usb->usbdev, + usb_rcvbulkpipe(usb->usbdev, 4), + usb->intr_urb->transfer_buffer, 2*sizeof(u16), + go7007_usb_readinterrupt_complete, go); + else + usb_fill_int_urb(usb->intr_urb, usb->usbdev, + usb_rcvintpipe(usb->usbdev, 4), + usb->intr_urb->transfer_buffer, 2*sizeof(u16), + go7007_usb_readinterrupt_complete, go, 8); + usb_set_intfdata(intf, &go->v4l2_dev); + + /* Boot the GO7007 */ + if (go7007_boot_encoder(go, go->board_info->flags & + GO7007_BOARD_USE_ONBOARD_I2C) < 0) + goto allocfail; + + /* Register the EZ-USB I2C adapter, if we're using it */ + if (board->flags & GO7007_USB_EZUSB_I2C) { + memcpy(&go->i2c_adapter, &go7007_usb_adap_templ, + sizeof(go7007_usb_adap_templ)); + mutex_init(&usb->i2c_lock); + go->i2c_adapter.dev.parent = go->dev; + i2c_set_adapdata(&go->i2c_adapter, go); + if (i2c_add_adapter(&go->i2c_adapter) < 0) { + dev_err(go->dev, "error: i2c_add_adapter failed\n"); + goto allocfail; + } + go->i2c_adapter_online = 1; + } + + /* Pelco and Adlink reused the XMen and XMen-III vendor and product + * IDs for their own incompatible designs. We can detect XMen boards + * by probing the sensor, but there is no way to probe the sensors on + * the Pelco and Adlink designs so we default to the Adlink. If it + * is actually a Pelco, the user must set the assume_endura module + * parameter. */ + if ((go->board_id == GO7007_BOARDID_XMEN || + go->board_id == GO7007_BOARDID_XMEN_III) && + go->i2c_adapter_online) { + union i2c_smbus_data data; + + /* Check to see if register 0x0A is 0x76 */ + i2c_smbus_xfer(&go->i2c_adapter, 0x21, I2C_CLIENT_SCCB, + I2C_SMBUS_READ, 0x0A, I2C_SMBUS_BYTE_DATA, &data); + if (data.byte != 0x76) { + if (assume_endura) { + go->board_id = GO7007_BOARDID_ENDURA; + usb->board = board = &board_endura; + go->board_info = &board->main_info; + strncpy(go->name, "Pelco Endura", + sizeof(go->name)); + } else { + u16 channel; + + /* read channel number from GPIO[1:0] */ + go7007_read_addr(go, 0x3c81, &channel); + channel &= 0x3; + go->board_id = GO7007_BOARDID_ADLINK_MPG24; + usb->board = board = &board_adlink_mpg24; + go->board_info = &board->main_info; + go->channel_number = channel; + snprintf(go->name, sizeof(go->name), + "Adlink PCI-MPG24, channel #%d", + channel); + } + go7007_update_board(go); + } + } + + num_i2c_devs = go->board_info->num_i2c_devs; + + /* Probe the tuner model on the TV402U */ + if (go->board_id == GO7007_BOARDID_PX_TV402U) { + /* Board strapping indicates tuner model */ + if (go7007_usb_vendor_request(go, 0x41, 0, 0, go->usb_buf, 3, + 1) < 0) { + dev_err(go->dev, "GPIO read failed!\n"); + goto allocfail; + } + switch (go->usb_buf[0] >> 6) { + case 1: + go->tuner_type = TUNER_SONY_BTF_PG472Z; + go->std = V4L2_STD_PAL; + strncpy(go->name, "Plextor PX-TV402U-EU", + sizeof(go->name)); + break; + case 2: + go->tuner_type = TUNER_SONY_BTF_PK467Z; + go->std = V4L2_STD_NTSC_M_JP; + num_i2c_devs -= 2; + strncpy(go->name, "Plextor PX-TV402U-JP", + sizeof(go->name)); + break; + case 3: + go->tuner_type = TUNER_SONY_BTF_PB463Z; + num_i2c_devs -= 2; + strncpy(go->name, "Plextor PX-TV402U-NA", + sizeof(go->name)); + break; + default: + pr_debug("unable to detect tuner type!\n"); + break; + } + /* Configure tuner mode selection inputs connected + * to the EZ-USB GPIO output pins */ + if (go7007_usb_vendor_request(go, 0x40, 0x7f02, 0, + NULL, 0, 0) < 0) { + dev_err(go->dev, "GPIO write failed!\n"); + goto allocfail; + } + } + + /* Print a nasty message if the user attempts to use a USB2.0 device in + * a USB1.1 port. There will be silent corruption of the stream. */ + if ((board->flags & GO7007_USB_EZUSB) && + usbdev->speed != USB_SPEED_HIGH) + dev_err(go->dev, "*** WARNING *** This device must be connected to a USB 2.0 port! Attempting to capture video through a USB 1.1 port will result in stream corruption, even at low bitrates!\n"); + + /* Allocate the URBs and buffers for receiving the video stream */ + if (board->flags & GO7007_USB_EZUSB) { + v_urb_len = 1024; + video_pipe = usb_rcvbulkpipe(usb->usbdev, 6); + } else { + v_urb_len = 512; + video_pipe = usb_rcvbulkpipe(usb->usbdev, 1); + } + for (i = 0; i < 8; ++i) { + usb->video_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (usb->video_urbs[i] == NULL) + goto allocfail; + usb->video_urbs[i]->transfer_buffer = + kmalloc(v_urb_len, GFP_KERNEL); + if (usb->video_urbs[i]->transfer_buffer == NULL) + goto allocfail; + usb_fill_bulk_urb(usb->video_urbs[i], usb->usbdev, video_pipe, + usb->video_urbs[i]->transfer_buffer, v_urb_len, + go7007_usb_read_video_pipe_complete, go); + } + + /* Allocate the URBs and buffers for receiving the audio stream */ + if ((board->flags & GO7007_USB_EZUSB) && + (board->flags & GO7007_BOARD_HAS_AUDIO)) { + for (i = 0; i < 8; ++i) { + usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (usb->audio_urbs[i] == NULL) + goto allocfail; + usb->audio_urbs[i]->transfer_buffer = kmalloc(4096, + GFP_KERNEL); + if (usb->audio_urbs[i]->transfer_buffer == NULL) + goto allocfail; + usb_fill_bulk_urb(usb->audio_urbs[i], usb->usbdev, + usb_rcvbulkpipe(usb->usbdev, 8), + usb->audio_urbs[i]->transfer_buffer, 4096, + go7007_usb_read_audio_pipe_complete, go); + } + } + + /* Do any final GO7007 initialization, then register the + * V4L2 and ALSA interfaces */ + if (go7007_register_encoder(go, num_i2c_devs) < 0) + goto allocfail; + + go->status = STATUS_ONLINE; + return 0; + +allocfail: + go7007_usb_release(go); + kfree(go); + return -ENOMEM; +} + +static void go7007_usb_disconnect(struct usb_interface *intf) +{ + struct go7007 *go = to_go7007(usb_get_intfdata(intf)); + + mutex_lock(&go->queue_lock); + mutex_lock(&go->serialize_lock); + + if (go->audio_enabled) + go7007_snd_remove(go); + + go->status = STATUS_SHUTDOWN; + v4l2_device_disconnect(&go->v4l2_dev); + video_unregister_device(&go->vdev); + mutex_unlock(&go->serialize_lock); + mutex_unlock(&go->queue_lock); + + v4l2_device_put(&go->v4l2_dev); +} + +static struct usb_driver go7007_usb_driver = { + .name = "go7007", + .probe = go7007_usb_probe, + .disconnect = go7007_usb_disconnect, + .id_table = go7007_usb_id_table, +}; + +module_usb_driver(go7007_usb_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c new file mode 100644 index 0000000..ec799b4 --- /dev/null +++ b/drivers/media/usb/go7007/go7007-v4l2.c @@ -0,0 +1,1173 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007-priv.h" + +#define call_all(dev, o, f, args...) \ + v4l2_device_call_until_err(dev, 0, o, f, ##args) + +static bool valid_pixelformat(u32 pixelformat) +{ + switch (pixelformat) { + case V4L2_PIX_FMT_MJPEG: + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: + case V4L2_PIX_FMT_MPEG4: + return true; + default: + return false; + } +} + +static u32 get_frame_type_flag(struct go7007_buffer *vb, int format) +{ + u8 *ptr = vb2_plane_vaddr(&vb->vb, 0); + + switch (format) { + case V4L2_PIX_FMT_MJPEG: + return V4L2_BUF_FLAG_KEYFRAME; + case V4L2_PIX_FMT_MPEG4: + switch ((ptr[vb->frame_offset + 4] >> 6) & 0x3) { + case 0: + return V4L2_BUF_FLAG_KEYFRAME; + case 1: + return V4L2_BUF_FLAG_PFRAME; + case 2: + return V4L2_BUF_FLAG_BFRAME; + default: + return 0; + } + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: + switch ((ptr[vb->frame_offset + 5] >> 3) & 0x7) { + case 1: + return V4L2_BUF_FLAG_KEYFRAME; + case 2: + return V4L2_BUF_FLAG_PFRAME; + case 3: + return V4L2_BUF_FLAG_BFRAME; + default: + return 0; + } + } + + return 0; +} + +static void get_resolution(struct go7007 *go, int *width, int *height) +{ + switch (go->standard) { + case GO7007_STD_NTSC: + *width = 720; + *height = 480; + break; + case GO7007_STD_PAL: + *width = 720; + *height = 576; + break; + case GO7007_STD_OTHER: + default: + *width = go->board_info->sensor_width; + *height = go->board_info->sensor_height; + break; + } +} + +static void set_formatting(struct go7007 *go) +{ + if (go->format == V4L2_PIX_FMT_MJPEG) { + go->pali = 0; + go->aspect_ratio = GO7007_RATIO_1_1; + go->gop_size = 0; + go->ipb = 0; + go->closed_gop = 0; + go->repeat_seqhead = 0; + go->seq_header_enable = 0; + go->gop_header_enable = 0; + go->dvd_mode = 0; + return; + } + + switch (go->format) { + case V4L2_PIX_FMT_MPEG1: + go->pali = 0; + break; + default: + case V4L2_PIX_FMT_MPEG2: + go->pali = 0x48; + break; + case V4L2_PIX_FMT_MPEG4: + /* For future reference: this is the list of MPEG4 + * profiles that are available, although they are + * untested: + * + * Profile pali + * -------------- ---- + * PROFILE_S_L0 0x08 + * PROFILE_S_L1 0x01 + * PROFILE_S_L2 0x02 + * PROFILE_S_L3 0x03 + * PROFILE_ARTS_L1 0x91 + * PROFILE_ARTS_L2 0x92 + * PROFILE_ARTS_L3 0x93 + * PROFILE_ARTS_L4 0x94 + * PROFILE_AS_L0 0xf0 + * PROFILE_AS_L1 0xf1 + * PROFILE_AS_L2 0xf2 + * PROFILE_AS_L3 0xf3 + * PROFILE_AS_L4 0xf4 + * PROFILE_AS_L5 0xf5 + */ + go->pali = 0xf5; + break; + } + go->gop_size = v4l2_ctrl_g_ctrl(go->mpeg_video_gop_size); + go->closed_gop = v4l2_ctrl_g_ctrl(go->mpeg_video_gop_closure); + go->ipb = v4l2_ctrl_g_ctrl(go->mpeg_video_b_frames) != 0; + go->bitrate = v4l2_ctrl_g_ctrl(go->mpeg_video_bitrate); + go->repeat_seqhead = v4l2_ctrl_g_ctrl(go->mpeg_video_rep_seqheader); + go->gop_header_enable = 1; + go->dvd_mode = 0; + if (go->format == V4L2_PIX_FMT_MPEG2) + go->dvd_mode = + go->bitrate == 9800000 && + go->gop_size == 15 && + go->ipb == 0 && + go->repeat_seqhead == 1 && + go->closed_gop; + + switch (v4l2_ctrl_g_ctrl(go->mpeg_video_aspect_ratio)) { + default: + case V4L2_MPEG_VIDEO_ASPECT_1x1: + go->aspect_ratio = GO7007_RATIO_1_1; + break; + case V4L2_MPEG_VIDEO_ASPECT_4x3: + go->aspect_ratio = GO7007_RATIO_4_3; + break; + case V4L2_MPEG_VIDEO_ASPECT_16x9: + go->aspect_ratio = GO7007_RATIO_16_9; + break; + } +} + +static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) +{ + int sensor_height = 0, sensor_width = 0; + int width, height; + + if (fmt != NULL && !valid_pixelformat(fmt->fmt.pix.pixelformat)) + return -EINVAL; + + get_resolution(go, &sensor_width, &sensor_height); + + if (fmt == NULL) { + width = sensor_width; + height = sensor_height; + } else if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { + if (fmt->fmt.pix.width > sensor_width) + width = sensor_width; + else if (fmt->fmt.pix.width < 144) + width = 144; + else + width = fmt->fmt.pix.width & ~0x0f; + + if (fmt->fmt.pix.height > sensor_height) + height = sensor_height; + else if (fmt->fmt.pix.height < 96) + height = 96; + else + height = fmt->fmt.pix.height & ~0x0f; + } else { + width = fmt->fmt.pix.width; + + if (width <= sensor_width / 4) { + width = sensor_width / 4; + height = sensor_height / 4; + } else if (width <= sensor_width / 2) { + width = sensor_width / 2; + height = sensor_height / 2; + } else { + width = sensor_width; + height = sensor_height; + } + width &= ~0xf; + height &= ~0xf; + } + + if (fmt != NULL) { + u32 pixelformat = fmt->fmt.pix.pixelformat; + + memset(fmt, 0, sizeof(*fmt)); + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt->fmt.pix.width = width; + fmt->fmt.pix.height = height; + fmt->fmt.pix.pixelformat = pixelformat; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + } + + if (try) + return 0; + + if (fmt) + go->format = fmt->fmt.pix.pixelformat; + go->width = width; + go->height = height; + go->encoder_h_offset = go->board_info->sensor_h_offset; + go->encoder_v_offset = go->board_info->sensor_v_offset; + + if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { + struct v4l2_mbus_framefmt mbus_fmt; + + mbus_fmt.code = V4L2_MBUS_FMT_FIXED; + mbus_fmt.width = fmt ? fmt->fmt.pix.width : width; + mbus_fmt.height = height; + go->encoder_h_halve = 0; + go->encoder_v_halve = 0; + go->encoder_subsample = 0; + call_all(&go->v4l2_dev, video, s_mbus_fmt, &mbus_fmt); + } else { + if (width <= sensor_width / 4) { + go->encoder_h_halve = 1; + go->encoder_v_halve = 1; + go->encoder_subsample = 1; + } else if (width <= sensor_width / 2) { + go->encoder_h_halve = 1; + go->encoder_v_halve = 1; + go->encoder_subsample = 0; + } else { + go->encoder_h_halve = 0; + go->encoder_v_halve = 0; + go->encoder_subsample = 0; + } + } + return 0; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct go7007 *go = video_drvdata(file); + + strlcpy(cap->driver, "go7007", sizeof(cap->driver)); + strlcpy(cap->card, go->name, sizeof(cap->card)); + strlcpy(cap->bus_info, go->bus_info, sizeof(cap->bus_info)); + + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + if (go->board_info->num_aud_inputs) + cap->device_caps |= V4L2_CAP_AUDIO; + if (go->board_info->flags & GO7007_BOARD_HAS_TUNER) + cap->device_caps |= V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + char *desc = NULL; + + switch (fmt->index) { + case 0: + fmt->pixelformat = V4L2_PIX_FMT_MJPEG; + desc = "Motion JPEG"; + break; + case 1: + fmt->pixelformat = V4L2_PIX_FMT_MPEG1; + desc = "MPEG-1 ES"; + break; + case 2: + fmt->pixelformat = V4L2_PIX_FMT_MPEG2; + desc = "MPEG-2 ES"; + break; + case 3: + fmt->pixelformat = V4L2_PIX_FMT_MPEG4; + desc = "MPEG-4 ES"; + break; + default: + return -EINVAL; + } + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt->flags = V4L2_FMT_FLAG_COMPRESSED; + + strncpy(fmt->description, desc, sizeof(fmt->description)); + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct go7007 *go = video_drvdata(file); + + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt->fmt.pix.width = go->width; + fmt->fmt.pix.height = go->height; + fmt->fmt.pix.pixelformat = go->format; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct go7007 *go = video_drvdata(file); + + return set_capture_size(go, fmt, 1); +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct go7007 *go = video_drvdata(file); + + if (vb2_is_busy(&go->vidq)) + return -EBUSY; + + return set_capture_size(go, fmt, 0); +} + +static int go7007_queue_setup(struct vb2_queue *q, + const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + sizes[0] = GO7007_BUF_SIZE; + *num_planes = 1; + + if (*num_buffers < 2) + *num_buffers = 2; + + return 0; +} + +static void go7007_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct go7007 *go = vb2_get_drv_priv(vq); + struct go7007_buffer *go7007_vb = + container_of(vb, struct go7007_buffer, vb); + unsigned long flags; + + spin_lock_irqsave(&go->spinlock, flags); + list_add_tail(&go7007_vb->list, &go->vidq_active); + spin_unlock_irqrestore(&go->spinlock, flags); +} + +static int go7007_buf_prepare(struct vb2_buffer *vb) +{ + struct go7007_buffer *go7007_vb = + container_of(vb, struct go7007_buffer, vb); + + go7007_vb->modet_active = 0; + go7007_vb->frame_offset = 0; + vb->v4l2_planes[0].bytesused = 0; + return 0; +} + +static void go7007_buf_finish(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct go7007 *go = vb2_get_drv_priv(vq); + struct go7007_buffer *go7007_vb = + container_of(vb, struct go7007_buffer, vb); + u32 frame_type_flag = get_frame_type_flag(go7007_vb, go->format); + struct v4l2_buffer *buf = &vb->v4l2_buf; + + buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_BFRAME | + V4L2_BUF_FLAG_PFRAME); + buf->flags |= frame_type_flag; + buf->field = V4L2_FIELD_NONE; +} + +static int go7007_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct go7007 *go = vb2_get_drv_priv(q); + int ret; + + set_formatting(go); + mutex_lock(&go->hw_lock); + go->next_seq = 0; + go->active_buf = NULL; + go->modet_event_status = 0; + q->streaming = 1; + if (go7007_start_encoder(go) < 0) + ret = -EIO; + else + ret = 0; + mutex_unlock(&go->hw_lock); + if (ret) { + q->streaming = 0; + return ret; + } + call_all(&go->v4l2_dev, video, s_stream, 1); + v4l2_ctrl_grab(go->mpeg_video_gop_size, true); + v4l2_ctrl_grab(go->mpeg_video_gop_closure, true); + v4l2_ctrl_grab(go->mpeg_video_bitrate, true); + v4l2_ctrl_grab(go->mpeg_video_aspect_ratio, true); + /* Turn on Capture LED */ + if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) + go7007_write_addr(go, 0x3c82, 0x0005); + return ret; +} + +static void go7007_stop_streaming(struct vb2_queue *q) +{ + struct go7007 *go = vb2_get_drv_priv(q); + unsigned long flags; + + q->streaming = 0; + go7007_stream_stop(go); + mutex_lock(&go->hw_lock); + go7007_reset_encoder(go); + mutex_unlock(&go->hw_lock); + call_all(&go->v4l2_dev, video, s_stream, 0); + + spin_lock_irqsave(&go->spinlock, flags); + INIT_LIST_HEAD(&go->vidq_active); + spin_unlock_irqrestore(&go->spinlock, flags); + v4l2_ctrl_grab(go->mpeg_video_gop_size, false); + v4l2_ctrl_grab(go->mpeg_video_gop_closure, false); + v4l2_ctrl_grab(go->mpeg_video_bitrate, false); + v4l2_ctrl_grab(go->mpeg_video_aspect_ratio, false); + /* Turn on Capture LED */ + if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) + go7007_write_addr(go, 0x3c82, 0x000d); +} + +static struct vb2_ops go7007_video_qops = { + .queue_setup = go7007_queue_setup, + .buf_queue = go7007_buf_queue, + .buf_prepare = go7007_buf_prepare, + .buf_finish = go7007_buf_finish, + .start_streaming = go7007_start_streaming, + .stop_streaming = go7007_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int vidioc_g_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct go7007 *go = video_drvdata(filp); + struct v4l2_fract timeperframe = { + .numerator = 1001 * go->fps_scale, + .denominator = go->sensor_framerate, + }; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + parm->parm.capture.readbuffers = 2; + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe = timeperframe; + + return 0; +} + +static int vidioc_s_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct go7007 *go = video_drvdata(filp); + unsigned int n, d; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + n = go->sensor_framerate * + parm->parm.capture.timeperframe.numerator; + d = 1001 * parm->parm.capture.timeperframe.denominator; + if (n != 0 && d != 0 && n > d) + go->fps_scale = (n + d/2) / d; + else + go->fps_scale = 1; + + return vidioc_g_parm(filp, priv, parm); +} + +/* VIDIOC_ENUMSTD on go7007 were used for enumerating the supported fps and + its resolution, when the device is not connected to TV. + This is were an API abuse, probably used by the lack of specific IOCTL's to + enumerate it, by the time the driver was written. + + However, since kernel 2.6.19, two new ioctls (VIDIOC_ENUM_FRAMEINTERVALS + and VIDIOC_ENUM_FRAMESIZES) were added for this purpose. + + The two functions below implement the newer ioctls +*/ +static int vidioc_enum_framesizes(struct file *filp, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct go7007 *go = video_drvdata(filp); + int width, height; + + if (fsize->index > 2) + return -EINVAL; + + if (!valid_pixelformat(fsize->pixel_format)) + return -EINVAL; + + get_resolution(go, &width, &height); + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = (width >> fsize->index) & ~0xf; + fsize->discrete.height = (height >> fsize->index) & ~0xf; + return 0; +} + +static int vidioc_enum_frameintervals(struct file *filp, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct go7007 *go = video_drvdata(filp); + int width, height; + int i; + + if (fival->index > 4) + return -EINVAL; + + if (!valid_pixelformat(fival->pixel_format)) + return -EINVAL; + + if (!(go->board_info->sensor_flags & GO7007_SENSOR_SCALING)) { + get_resolution(go, &width, &height); + for (i = 0; i <= 2; i++) + if (fival->width == ((width >> i) & ~0xf) && + fival->height == ((height >> i) & ~0xf)) + break; + if (i > 2) + return -EINVAL; + } + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1001 * (fival->index + 1); + fival->discrete.denominator = go->sensor_framerate; + return 0; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct go7007 *go = video_drvdata(file); + + *std = go->std; + return 0; +} + +static int go7007_s_std(struct go7007 *go) +{ + if (go->std & V4L2_STD_625_50) { + go->standard = GO7007_STD_PAL; + go->sensor_framerate = 25025; + } else { + go->standard = GO7007_STD_NTSC; + go->sensor_framerate = 30000; + } + + call_all(&go->v4l2_dev, video, s_std, go->std); + set_capture_size(go, NULL, 0); + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) +{ + struct go7007 *go = video_drvdata(file); + + if (vb2_is_busy(&go->vidq)) + return -EBUSY; + + go->std = std; + + return go7007_s_std(go); +} + +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct go7007 *go = video_drvdata(file); + + return call_all(&go->v4l2_dev, video, querystd, std); +} + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct go7007 *go = video_drvdata(file); + + if (inp->index >= go->board_info->num_inputs) + return -EINVAL; + + strncpy(inp->name, go->board_info->inputs[inp->index].name, + sizeof(inp->name)); + + /* If this board has a tuner, it will be the first input */ + if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && + inp->index == 0) + inp->type = V4L2_INPUT_TYPE_TUNER; + else + inp->type = V4L2_INPUT_TYPE_CAMERA; + + if (go->board_info->num_aud_inputs) + inp->audioset = (1 << go->board_info->num_aud_inputs) - 1; + else + inp->audioset = 0; + inp->tuner = 0; + if (go->board_info->sensor_flags & GO7007_SENSOR_TV) + inp->std = video_devdata(file)->tvnorms; + else + inp->std = 0; + + return 0; +} + + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *input) +{ + struct go7007 *go = video_drvdata(file); + + *input = go->input; + + return 0; +} + +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) +{ + struct go7007 *go = video_drvdata(file); + + if (a->index >= go->board_info->num_aud_inputs) + return -EINVAL; + strlcpy(a->name, go->board_info->aud_inputs[a->index].name, + sizeof(a->name)); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + struct go7007 *go = video_drvdata(file); + + a->index = go->aud_input; + strlcpy(a->name, go->board_info->aud_inputs[go->aud_input].name, + sizeof(a->name)); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *fh, + const struct v4l2_audio *a) +{ + struct go7007 *go = video_drvdata(file); + + if (a->index >= go->board_info->num_aud_inputs) + return -EINVAL; + go->aud_input = a->index; + v4l2_subdev_call(go->sd_audio, audio, s_routing, + go->board_info->aud_inputs[go->aud_input].audio_input, 0, 0); + return 0; +} + +static void go7007_s_input(struct go7007 *go) +{ + unsigned int input = go->input; + + v4l2_subdev_call(go->sd_video, video, s_routing, + go->board_info->inputs[input].video_input, 0, + go->board_info->video_config); + if (go->board_info->num_aud_inputs) { + int aud_input = go->board_info->inputs[input].audio_index; + + v4l2_subdev_call(go->sd_audio, audio, s_routing, + go->board_info->aud_inputs[aud_input].audio_input, 0, 0); + go->aud_input = aud_input; + } +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int input) +{ + struct go7007 *go = video_drvdata(file); + + if (input >= go->board_info->num_inputs) + return -EINVAL; + if (vb2_is_busy(&go->vidq)) + return -EBUSY; + + go->input = input; + go7007_s_input(go); + + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct go7007 *go = video_drvdata(file); + + if (t->index != 0) + return -EINVAL; + + strlcpy(t->name, "Tuner", sizeof(t->name)); + return call_all(&go->v4l2_dev, tuner, g_tuner, t); +} + +static int vidioc_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *t) +{ + struct go7007 *go = video_drvdata(file); + + if (t->index != 0) + return -EINVAL; + + return call_all(&go->v4l2_dev, tuner, s_tuner, t); +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct go7007 *go = video_drvdata(file); + + if (f->tuner) + return -EINVAL; + + return call_all(&go->v4l2_dev, tuner, g_frequency, f); +} + +static int vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct go7007 *go = video_drvdata(file); + + if (f->tuner) + return -EINVAL; + + return call_all(&go->v4l2_dev, tuner, s_frequency, f); +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct go7007 *go = video_drvdata(file); + + v4l2_ctrl_log_status(file, priv); + return call_all(&go->v4l2_dev, core, log_status); +} + +static int vidioc_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + case V4L2_EVENT_MOTION_DET: + /* Allow for up to 30 events (1 second for NTSC) to be + * stored. */ + return v4l2_event_subscribe(fh, sub, 30, NULL); + } + return -EINVAL; +} + + +static int go7007_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct go7007 *go = + container_of(ctrl->handler, struct go7007, hdl); + unsigned y; + u8 *mt; + + switch (ctrl->id) { + case V4L2_CID_PIXEL_THRESHOLD0: + go->modet[0].pixel_threshold = ctrl->val; + break; + case V4L2_CID_MOTION_THRESHOLD0: + go->modet[0].motion_threshold = ctrl->val; + break; + case V4L2_CID_MB_THRESHOLD0: + go->modet[0].mb_threshold = ctrl->val; + break; + case V4L2_CID_PIXEL_THRESHOLD1: + go->modet[1].pixel_threshold = ctrl->val; + break; + case V4L2_CID_MOTION_THRESHOLD1: + go->modet[1].motion_threshold = ctrl->val; + break; + case V4L2_CID_MB_THRESHOLD1: + go->modet[1].mb_threshold = ctrl->val; + break; + case V4L2_CID_PIXEL_THRESHOLD2: + go->modet[2].pixel_threshold = ctrl->val; + break; + case V4L2_CID_MOTION_THRESHOLD2: + go->modet[2].motion_threshold = ctrl->val; + break; + case V4L2_CID_MB_THRESHOLD2: + go->modet[2].mb_threshold = ctrl->val; + break; + case V4L2_CID_PIXEL_THRESHOLD3: + go->modet[3].pixel_threshold = ctrl->val; + break; + case V4L2_CID_MOTION_THRESHOLD3: + go->modet[3].motion_threshold = ctrl->val; + break; + case V4L2_CID_MB_THRESHOLD3: + go->modet[3].mb_threshold = ctrl->val; + break; + case V4L2_CID_DETECT_MD_REGION_GRID: + mt = go->modet_map; + for (y = 0; y < go->height / 16; y++, mt += go->width / 16) + memcpy(mt, ctrl->p_new.p_u8 + y * (720 / 16), go->width / 16); + break; + default: + return -EINVAL; + } + return 0; +} + +static struct v4l2_file_operations go7007_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_enumaudio = vidioc_enumaudio, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + .vidioc_log_status = vidioc_log_status, + .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device go7007_template = { + .name = "go7007", + .fops = &go7007_fops, + .release = video_device_release_empty, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = V4L2_STD_ALL, +}; + +static const struct v4l2_ctrl_ops go7007_ctrl_ops = { + .s_ctrl = go7007_s_ctrl, +}; + +static const struct v4l2_ctrl_config go7007_pixel_threshold0_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_PIXEL_THRESHOLD0, + .name = "Pixel Threshold Region 0", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 20, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_motion_threshold0_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MOTION_THRESHOLD0, + .name = "Motion Threshold Region 0", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 80, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_mb_threshold0_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MB_THRESHOLD0, + .name = "MB Threshold Region 0", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 200, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_pixel_threshold1_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_PIXEL_THRESHOLD1, + .name = "Pixel Threshold Region 1", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 20, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_motion_threshold1_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MOTION_THRESHOLD1, + .name = "Motion Threshold Region 1", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 80, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_mb_threshold1_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MB_THRESHOLD1, + .name = "MB Threshold Region 1", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 200, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_pixel_threshold2_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_PIXEL_THRESHOLD2, + .name = "Pixel Threshold Region 2", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 20, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_motion_threshold2_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MOTION_THRESHOLD2, + .name = "Motion Threshold Region 2", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 80, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_mb_threshold2_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MB_THRESHOLD2, + .name = "MB Threshold Region 2", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 200, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_pixel_threshold3_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_PIXEL_THRESHOLD3, + .name = "Pixel Threshold Region 3", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 20, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_motion_threshold3_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MOTION_THRESHOLD3, + .name = "Motion Threshold Region 3", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 80, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_mb_threshold3_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_MB_THRESHOLD3, + .name = "MB Threshold Region 3", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 200, + .max = 32767, + .step = 1, +}; + +static const struct v4l2_ctrl_config go7007_mb_regions_ctrl = { + .ops = &go7007_ctrl_ops, + .id = V4L2_CID_DETECT_MD_REGION_GRID, + .dims = { 576 / 16, 720 / 16 }, + .max = 3, + .step = 1, +}; + +int go7007_v4l2_ctrl_init(struct go7007 *go) +{ + struct v4l2_ctrl_handler *hdl = &go->hdl; + struct v4l2_ctrl *ctrl; + + v4l2_ctrl_handler_init(hdl, 22); + go->mpeg_video_gop_size = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, 34, 1, 15); + go->mpeg_video_gop_closure = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1); + go->mpeg_video_bitrate = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_BITRATE, + 64000, 10000000, 1, 9800000); + go->mpeg_video_b_frames = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 2, 2, 0); + go->mpeg_video_rep_seqheader = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, 0, 1, 1, 1); + + go->mpeg_video_aspect_ratio = v4l2_ctrl_new_std_menu(hdl, NULL, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_MPEG_VIDEO_ASPECT_16x9, 0, + V4L2_MPEG_VIDEO_ASPECT_1x1); + ctrl = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_JPEG_ACTIVE_MARKER, 0, + V4L2_JPEG_ACTIVE_MARKER_DQT | + V4L2_JPEG_ACTIVE_MARKER_DHT, 0, + V4L2_JPEG_ACTIVE_MARKER_DQT | + V4L2_JPEG_ACTIVE_MARKER_DHT); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold0_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold0_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold0_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold1_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold1_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold1_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold2_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold2_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold2_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold3_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold3_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold3_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &go7007_mb_regions_ctrl, NULL); + go->modet_mode = v4l2_ctrl_new_std_menu(hdl, NULL, + V4L2_CID_DETECT_MD_MODE, + V4L2_DETECT_MD_MODE_REGION_GRID, + 1 << V4L2_DETECT_MD_MODE_THRESHOLD_GRID, + V4L2_DETECT_MD_MODE_DISABLED); + if (hdl->error) { + int rv = hdl->error; + + v4l2_err(&go->v4l2_dev, "Could not register controls\n"); + return rv; + } + go->v4l2_dev.ctrl_handler = hdl; + return 0; +} + +int go7007_v4l2_init(struct go7007 *go) +{ + struct video_device *vdev = &go->vdev; + int rv; + + mutex_init(&go->serialize_lock); + mutex_init(&go->queue_lock); + + INIT_LIST_HEAD(&go->vidq_active); + go->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + go->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + go->vidq.ops = &go7007_video_qops; + go->vidq.mem_ops = &vb2_vmalloc_memops; + go->vidq.drv_priv = go; + go->vidq.buf_struct_size = sizeof(struct go7007_buffer); + go->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + go->vidq.lock = &go->queue_lock; + rv = vb2_queue_init(&go->vidq); + if (rv) + return rv; + *vdev = go7007_template; + vdev->lock = &go->serialize_lock; + vdev->queue = &go->vidq; + video_set_drvdata(vdev, go); + vdev->v4l2_dev = &go->v4l2_dev; + if (!v4l2_device_has_op(&go->v4l2_dev, video, querystd)) + v4l2_disable_ioctl(vdev, VIDIOC_QUERYSTD); + if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) { + v4l2_disable_ioctl(vdev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(vdev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(vdev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(vdev, VIDIOC_G_TUNER); + } else { + struct v4l2_frequency f = { + .type = V4L2_TUNER_ANALOG_TV, + .frequency = 980, + }; + + call_all(&go->v4l2_dev, tuner, s_frequency, &f); + } + if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV)) { + v4l2_disable_ioctl(vdev, VIDIOC_G_STD); + v4l2_disable_ioctl(vdev, VIDIOC_S_STD); + vdev->tvnorms = 0; + } + if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) + v4l2_disable_ioctl(vdev, VIDIOC_ENUM_FRAMESIZES); + if (go->board_info->num_aud_inputs == 0) { + v4l2_disable_ioctl(vdev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(vdev, VIDIOC_S_AUDIO); + v4l2_disable_ioctl(vdev, VIDIOC_ENUMAUDIO); + } + /* Setup correct crystal frequency on this board */ + if (go->board_info->sensor_flags & GO7007_SENSOR_SAA7115) + v4l2_subdev_call(go->sd_video, video, s_crystal_freq, + SAA7115_FREQ_24_576_MHZ, + SAA7115_FREQ_FL_APLL | SAA7115_FREQ_FL_UCGC | + SAA7115_FREQ_FL_DOUBLE_ASCLK); + go7007_s_input(go); + if (go->board_info->sensor_flags & GO7007_SENSOR_TV) + go7007_s_std(go); + rv = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (rv < 0) + return rv; + dev_info(go->dev, "registered device %s [v4l2]\n", + video_device_node_name(vdev)); + + return 0; +} + +void go7007_v4l2_remove(struct go7007 *go) +{ + v4l2_ctrl_handler_free(&go->hdl); +} diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c new file mode 100644 index 0000000..bb84668 --- /dev/null +++ b/drivers/media/usb/go7007/s2250-board.c @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2008 Sensoray Company Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "go7007-priv.h" + +MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver"); +MODULE_LICENSE("GPL v2"); + +/* + * Note: this board has two i2c devices: a vpx3226f and a tlv320aic23b. + * Due to the unusual way these are accessed on this device we do not + * reuse the i2c drivers, but instead they are implemented in this + * driver. It would be nice to improve on this, though. + */ + +#define TLV320_ADDRESS 0x34 +#define VPX322_ADDR_ANALOGCONTROL1 0x02 +#define VPX322_ADDR_BRIGHTNESS0 0x0127 +#define VPX322_ADDR_BRIGHTNESS1 0x0131 +#define VPX322_ADDR_CONTRAST0 0x0128 +#define VPX322_ADDR_CONTRAST1 0x0132 +#define VPX322_ADDR_HUE 0x00dc +#define VPX322_ADDR_SAT 0x0030 + +struct go7007_usb_board { + unsigned int flags; + struct go7007_board_info main_info; +}; + +struct go7007_usb { + struct go7007_usb_board *board; + struct mutex i2c_lock; + struct usb_device *usbdev; + struct urb *video_urbs[8]; + struct urb *audio_urbs[8]; + struct urb *intr_urb; +}; + +static unsigned char aud_regs[] = { + 0x1e, 0x00, + 0x00, 0x17, + 0x02, 0x17, + 0x04, 0xf9, + 0x06, 0xf9, + 0x08, 0x02, + 0x0a, 0x00, + 0x0c, 0x00, + 0x0a, 0x00, + 0x0c, 0x00, + 0x0e, 0x02, + 0x10, 0x00, + 0x12, 0x01, + 0x00, 0x00, +}; + + +static unsigned char vid_regs[] = { + 0xF2, 0x0f, + 0xAA, 0x00, + 0xF8, 0xff, + 0x00, 0x00, +}; + +static u16 vid_regs_fp[] = { + 0x028, 0x067, + 0x120, 0x016, + 0x121, 0xcF2, + 0x122, 0x0F2, + 0x123, 0x00c, + 0x124, 0x2d0, + 0x125, 0x2e0, + 0x126, 0x004, + 0x128, 0x1E0, + 0x12A, 0x016, + 0x12B, 0x0F2, + 0x12C, 0x0F2, + 0x12D, 0x00c, + 0x12E, 0x2d0, + 0x12F, 0x2e0, + 0x130, 0x004, + 0x132, 0x1E0, + 0x140, 0x060, + 0x153, 0x00C, + 0x154, 0x200, + 0x150, 0x801, + 0x000, 0x000 +}; + +/* PAL specific values */ +static u16 vid_regs_fp_pal[] = { + 0x120, 0x017, + 0x121, 0xd22, + 0x122, 0x122, + 0x12A, 0x017, + 0x12B, 0x122, + 0x12C, 0x122, + 0x140, 0x060, + 0x000, 0x000, +}; + +struct s2250 { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + v4l2_std_id std; + int input; + int brightness; + int contrast; + int saturation; + int hue; + int reg12b_val; + int audio_input; + struct i2c_client *audio; +}; + +static inline struct s2250 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s2250, sd); +} + +/* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ +static int go7007_usb_vendor_request(struct go7007 *go, u16 request, + u16 value, u16 index, void *transfer_buffer, int length, int in) +{ + struct go7007_usb *usb = go->hpi_context; + int timeout = 5000; + + if (in) { + return usb_control_msg(usb->usbdev, + usb_rcvctrlpipe(usb->usbdev, 0), request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + value, index, transfer_buffer, length, timeout); + } else { + return usb_control_msg(usb->usbdev, + usb_sndctrlpipe(usb->usbdev, 0), request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, transfer_buffer, length, timeout); + } +} +/* end from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ + +static int write_reg(struct i2c_client *client, u8 reg, u8 value) +{ + struct go7007 *go = i2c_get_adapdata(client->adapter); + struct go7007_usb *usb; + int rc; + int dev_addr = client->addr << 1; /* firmware wants 8-bit address */ + u8 *buf; + + if (go == NULL) + return -ENODEV; + + if (go->status == STATUS_SHUTDOWN) + return -EBUSY; + + buf = kzalloc(16, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + usb = go->hpi_context; + if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { + dev_info(&client->dev, "i2c lock failed\n"); + kfree(buf); + return -EINTR; + } + rc = go7007_usb_vendor_request(go, 0x55, dev_addr, + (reg<<8 | value), + buf, + 16, 1); + + mutex_unlock(&usb->i2c_lock); + kfree(buf); + return rc; +} + +static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) +{ + struct go7007 *go = i2c_get_adapdata(client->adapter); + struct go7007_usb *usb; + int rc; + u8 *buf; + struct s2250 *dec = i2c_get_clientdata(client); + + if (go == NULL) + return -ENODEV; + + if (go->status == STATUS_SHUTDOWN) + return -EBUSY; + + buf = kzalloc(16, GFP_KERNEL); + + if (buf == NULL) + return -ENOMEM; + + + + memset(buf, 0xcd, 6); + + usb = go->hpi_context; + if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { + dev_info(&client->dev, "i2c lock failed\n"); + kfree(buf); + return -EINTR; + } + rc = go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1); + mutex_unlock(&usb->i2c_lock); + if (rc < 0) { + kfree(buf); + return rc; + } + + if (buf[0] == 0) { + unsigned int subaddr, val_read; + + subaddr = (buf[4] << 8) + buf[5]; + val_read = (buf[2] << 8) + buf[3]; + kfree(buf); + if (val_read != val) { + dev_info(&client->dev, "invalid fp write %x %x\n", + val_read, val); + return -EFAULT; + } + if (subaddr != addr) { + dev_info(&client->dev, "invalid fp write addr %x %x\n", + subaddr, addr); + return -EFAULT; + } + } else { + kfree(buf); + return -EFAULT; + } + + /* save last 12b value */ + if (addr == 0x12b) + dec->reg12b_val = val; + + return 0; +} + +static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) +{ + struct go7007 *go = i2c_get_adapdata(client->adapter); + struct go7007_usb *usb; + int rc; + u8 *buf; + + if (go == NULL) + return -ENODEV; + + if (go->status == STATUS_SHUTDOWN) + return -EBUSY; + + buf = kzalloc(16, GFP_KERNEL); + + if (buf == NULL) + return -ENOMEM; + + + + memset(buf, 0xcd, 6); + usb = go->hpi_context; + if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { + dev_info(&client->dev, "i2c lock failed\n"); + kfree(buf); + return -EINTR; + } + rc = go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1); + mutex_unlock(&usb->i2c_lock); + if (rc < 0) { + kfree(buf); + return rc; + } + + *val = (buf[0] << 8) | buf[1]; + kfree(buf); + + return 0; +} + + +static int write_regs(struct i2c_client *client, u8 *regs) +{ + int i; + + for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { + if (write_reg(client, regs[i], regs[i+1]) < 0) { + dev_info(&client->dev, "failed\n"); + return -1; + } + } + return 0; +} + +static int write_regs_fp(struct i2c_client *client, u16 *regs) +{ + int i; + + for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { + if (write_reg_fp(client, regs[i], regs[i+1]) < 0) { + dev_info(&client->dev, "failed fp\n"); + return -1; + } + } + return 0; +} + + +/* ------------------------------------------------------------------------- */ + +static int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int vidsys; + + vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00; + if (input == 0) { + /* composite */ + write_reg_fp(client, 0x20, 0x020 | vidsys); + write_reg_fp(client, 0x21, 0x662); + write_reg_fp(client, 0x140, 0x060); + } else if (input == 1) { + /* S-Video */ + write_reg_fp(client, 0x20, 0x040 | vidsys); + write_reg_fp(client, 0x21, 0x666); + write_reg_fp(client, 0x140, 0x060); + } else { + return -EINVAL; + } + state->input = input; + return 0; +} + +static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 vidsource; + + vidsource = (state->input == 1) ? 0x040 : 0x020; + if (norm & V4L2_STD_625_50) { + write_regs_fp(client, vid_regs_fp); + write_regs_fp(client, vid_regs_fp_pal); + write_reg_fp(client, 0x20, vidsource); + } else { + write_regs_fp(client, vid_regs_fp); + write_reg_fp(client, 0x20, vidsource | 1); + } + state->std = norm; + return 0; +} + +static int s2250_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct s2250 *state = container_of(ctrl->handler, struct s2250, hdl); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + u16 oldvalue; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue); + write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, + ctrl->val | (oldvalue & ~0xff)); + read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue); + write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, + ctrl->val | (oldvalue & ~0xff)); + write_reg_fp(client, 0x140, 0x60); + break; + case V4L2_CID_CONTRAST: + read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue); + write_reg_fp(client, VPX322_ADDR_CONTRAST0, + ctrl->val | (oldvalue & ~0x3f)); + read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue); + write_reg_fp(client, VPX322_ADDR_CONTRAST1, + ctrl->val | (oldvalue & ~0x3f)); + write_reg_fp(client, 0x140, 0x60); + break; + case V4L2_CID_SATURATION: + write_reg_fp(client, VPX322_ADDR_SAT, ctrl->val); + break; + case V4L2_CID_HUE: + write_reg_fp(client, VPX322_ADDR_HUE, ctrl->val); + break; + default: + return -EINVAL; + } + return 0; +} + +static int s2250_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (fmt->height < 640) { + write_reg_fp(client, 0x12b, state->reg12b_val | 0x400); + write_reg_fp(client, 0x140, 0x060); + } else { + write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400); + write_reg_fp(client, 0x140, 0x060); + } + return 0; +} + +static int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct s2250 *state = to_state(sd); + + switch (input) { + case 0: + write_reg(state->audio, 0x08, 0x02); /* Line In */ + break; + case 1: + write_reg(state->audio, 0x08, 0x04); /* Mic */ + break; + case 2: + write_reg(state->audio, 0x08, 0x05); /* Mic Boost */ + break; + default: + return -EINVAL; + } + state->audio_input = input; + return 0; +} + + +static int s2250_log_status(struct v4l2_subdev *sd) +{ + struct s2250 *state = to_state(sd); + + v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" : + state->std == V4L2_STD_PAL ? "PAL" : + state->std == V4L2_STD_SECAM ? "SECAM" : + "unknown"); + v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" : + state->input == 1 ? "S-video" : + "error"); + v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" : + state->audio_input == 1 ? "Mic" : + state->audio_input == 2 ? "Mic Boost" : + "error"); + return v4l2_ctrl_subdev_log_status(sd); +} + +/* --------------------------------------------------------------------------*/ + +static const struct v4l2_ctrl_ops s2250_ctrl_ops = { + .s_ctrl = s2250_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops s2250_core_ops = { + .log_status = s2250_log_status, +}; + +static const struct v4l2_subdev_audio_ops s2250_audio_ops = { + .s_routing = s2250_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops s2250_video_ops = { + .s_std = s2250_s_std, + .s_routing = s2250_s_video_routing, + .s_mbus_fmt = s2250_s_mbus_fmt, +}; + +static const struct v4l2_subdev_ops s2250_ops = { + .core = &s2250_core_ops, + .audio = &s2250_audio_ops, + .video = &s2250_video_ops, +}; + +/* --------------------------------------------------------------------------*/ + +static int s2250_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_client *audio; + struct i2c_adapter *adapter = client->adapter; + struct s2250 *state; + struct v4l2_subdev *sd; + u8 *data; + struct go7007 *go = i2c_get_adapdata(adapter); + struct go7007_usb *usb = go->hpi_context; + + audio = i2c_new_dummy(adapter, TLV320_ADDRESS >> 1); + if (audio == NULL) + return -ENOMEM; + + state = kzalloc(sizeof(struct s2250), GFP_KERNEL); + if (state == NULL) { + i2c_unregister_device(audio); + return -ENOMEM; + } + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &s2250_ops); + + v4l2_info(sd, "initializing %s at address 0x%x on %s\n", + "Sensoray 2250/2251", client->addr, client->adapter->name); + + v4l2_ctrl_handler_init(&state->hdl, 4); + v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x32); + v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, + V4L2_CID_SATURATION, 0, 4094, 1, 2070); + v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, + V4L2_CID_HUE, -512, 511, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + + state->std = V4L2_STD_NTSC; + state->brightness = 50; + state->contrast = 50; + state->saturation = 50; + state->hue = 0; + state->audio = audio; + + /* initialize the audio */ + if (write_regs(audio, aud_regs) < 0) { + dev_err(&client->dev, "error initializing audio\n"); + goto fail; + } + + if (write_regs(client, vid_regs) < 0) { + dev_err(&client->dev, "error initializing decoder\n"); + goto fail; + } + if (write_regs_fp(client, vid_regs_fp) < 0) { + dev_err(&client->dev, "error initializing decoder\n"); + goto fail; + } + /* set default channel */ + /* composite */ + write_reg_fp(client, 0x20, 0x020 | 1); + write_reg_fp(client, 0x21, 0x662); + write_reg_fp(client, 0x140, 0x060); + + /* set default audio input */ + state->audio_input = 0; + write_reg(client, 0x08, 0x02); /* Line In */ + + if (mutex_lock_interruptible(&usb->i2c_lock) == 0) { + data = kzalloc(16, GFP_KERNEL); + if (data != NULL) { + int rc = go7007_usb_vendor_request(go, 0x41, 0, 0, + data, 16, 1); + + if (rc > 0) { + u8 mask; + + data[0] = 0; + mask = 1<<5; + data[0] &= ~mask; + data[1] |= mask; + go7007_usb_vendor_request(go, 0x40, 0, + (data[1]<<8) + + data[1], + data, 16, 0); + } + kfree(data); + } + mutex_unlock(&usb->i2c_lock); + } + + v4l2_info(sd, "initialized successfully\n"); + return 0; + +fail: + i2c_unregister_device(audio); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return -EIO; +} + +static int s2250_remove(struct i2c_client *client) +{ + struct s2250 *state = to_state(i2c_get_clientdata(client)); + + v4l2_device_unregister_subdev(&state->sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return 0; +} + +static const struct i2c_device_id s2250_id[] = { + { "s2250", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, s2250_id); + +static struct i2c_driver s2250_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "s2250", + }, + .probe = s2250_probe, + .remove = s2250_remove, + .id_table = s2250_id, +}; + +module_i2c_driver(s2250_driver); diff --git a/drivers/media/usb/go7007/snd-go7007.c b/drivers/media/usb/go7007/snd-go7007.c new file mode 100644 index 0000000..d22d7d5 --- /dev/null +++ b/drivers/media/usb/go7007/snd-go7007.c @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007-priv.h" + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +module_param_array(id, charp, NULL, 0444); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for the go7007 audio driver"); +MODULE_PARM_DESC(id, "ID string for the go7007 audio driver"); +MODULE_PARM_DESC(enable, "Enable for the go7007 audio driver"); + +struct go7007_snd { + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + spinlock_t lock; + int w_idx; + int hw_ptr; + int avail; + int capturing; +}; + +static struct snd_pcm_hardware go7007_snd_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 4096, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 32, +}; + +static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length) +{ + struct go7007_snd *gosnd = go->snd_context; + struct snd_pcm_runtime *runtime = gosnd->substream->runtime; + int frames = bytes_to_frames(runtime, length); + + spin_lock(&gosnd->lock); + gosnd->hw_ptr += frames; + if (gosnd->hw_ptr >= runtime->buffer_size) + gosnd->hw_ptr -= runtime->buffer_size; + gosnd->avail += frames; + spin_unlock(&gosnd->lock); + if (gosnd->w_idx + length > runtime->dma_bytes) { + int cpy = runtime->dma_bytes - gosnd->w_idx; + + memcpy(runtime->dma_area + gosnd->w_idx, buf, cpy); + length -= cpy; + buf += cpy; + gosnd->w_idx = 0; + } + memcpy(runtime->dma_area + gosnd->w_idx, buf, length); + gosnd->w_idx += length; + spin_lock(&gosnd->lock); + if (gosnd->avail < runtime->period_size) { + spin_unlock(&gosnd->lock); + return; + } + gosnd->avail -= runtime->period_size; + spin_unlock(&gosnd->lock); + if (gosnd->capturing) + snd_pcm_period_elapsed(gosnd->substream); +} + +static int go7007_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + unsigned int bytes; + + bytes = params_buffer_bytes(hw_params); + if (substream->runtime->dma_bytes > 0) + vfree(substream->runtime->dma_area); + substream->runtime->dma_bytes = 0; + substream->runtime->dma_area = vmalloc(bytes); + if (substream->runtime->dma_area == NULL) + return -ENOMEM; + substream->runtime->dma_bytes = bytes; + go->audio_deliver = parse_audio_stream_data; + return 0; +} + +static int go7007_snd_hw_free(struct snd_pcm_substream *substream) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + + go->audio_deliver = NULL; + if (substream->runtime->dma_bytes > 0) + vfree(substream->runtime->dma_area); + substream->runtime->dma_bytes = 0; + return 0; +} + +static int go7007_snd_capture_open(struct snd_pcm_substream *substream) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + struct go7007_snd *gosnd = go->snd_context; + unsigned long flags; + int r; + + spin_lock_irqsave(&gosnd->lock, flags); + if (gosnd->substream == NULL) { + gosnd->substream = substream; + substream->runtime->hw = go7007_snd_capture_hw; + r = 0; + } else + r = -EBUSY; + spin_unlock_irqrestore(&gosnd->lock, flags); + return r; +} + +static int go7007_snd_capture_close(struct snd_pcm_substream *substream) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + struct go7007_snd *gosnd = go->snd_context; + + gosnd->substream = NULL; + return 0; +} + +static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int go7007_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + struct go7007_snd *gosnd = go->snd_context; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* Just set a flag to indicate we should signal ALSA when + * sound comes in */ + gosnd->capturing = 1; + return 0; + case SNDRV_PCM_TRIGGER_STOP: + gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0; + gosnd->capturing = 0; + return 0; + default: + return -EINVAL; + } +} + +static snd_pcm_uframes_t go7007_snd_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + struct go7007_snd *gosnd = go->snd_context; + + return gosnd->hw_ptr; +} + +static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + return vmalloc_to_page(substream->runtime->dma_area + offset); +} + +static struct snd_pcm_ops go7007_snd_capture_ops = { + .open = go7007_snd_capture_open, + .close = go7007_snd_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = go7007_snd_hw_params, + .hw_free = go7007_snd_hw_free, + .prepare = go7007_snd_pcm_prepare, + .trigger = go7007_snd_pcm_trigger, + .pointer = go7007_snd_pcm_pointer, + .page = go7007_snd_pcm_page, +}; + +static int go7007_snd_free(struct snd_device *device) +{ + struct go7007 *go = device->device_data; + + kfree(go->snd_context); + go->snd_context = NULL; + return 0; +} + +static struct snd_device_ops go7007_snd_device_ops = { + .dev_free = go7007_snd_free, +}; + +int go7007_snd_init(struct go7007 *go) +{ + static int dev; + struct go7007_snd *gosnd; + int ret = 0; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + gosnd = kmalloc(sizeof(struct go7007_snd), GFP_KERNEL); + if (gosnd == NULL) + return -ENOMEM; + spin_lock_init(&gosnd->lock); + gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0; + gosnd->capturing = 0; + ret = snd_card_new(go->dev, index[dev], id[dev], THIS_MODULE, 0, + &gosnd->card); + if (ret < 0) { + kfree(gosnd); + return ret; + } + ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go, + &go7007_snd_device_ops); + if (ret < 0) { + kfree(gosnd); + return ret; + } + ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm); + if (ret < 0) { + snd_card_free(gosnd->card); + kfree(gosnd); + return ret; + } + strlcpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver)); + strlcpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver)); + strlcpy(gosnd->card->longname, gosnd->card->shortname, + sizeof(gosnd->card->longname)); + + gosnd->pcm->private_data = go; + snd_pcm_set_ops(gosnd->pcm, SNDRV_PCM_STREAM_CAPTURE, + &go7007_snd_capture_ops); + + ret = snd_card_register(gosnd->card); + if (ret < 0) { + snd_card_free(gosnd->card); + kfree(gosnd); + return ret; + } + + gosnd->substream = NULL; + go->snd_context = gosnd; + v4l2_device_get(&go->v4l2_dev); + ++dev; + + return 0; +} +EXPORT_SYMBOL(go7007_snd_init); + +int go7007_snd_remove(struct go7007 *go) +{ + struct go7007_snd *gosnd = go->snd_context; + + snd_card_disconnect(gosnd->card); + snd_card_free_when_closed(gosnd->card); + v4l2_device_put(&go->v4l2_dev); + return 0; +} +EXPORT_SYMBOL(go7007_snd_remove); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 3312a3b..3323eb5 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -29,8 +29,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/dt3155v4l/Kconfig" -source "drivers/staging/media/go7007/Kconfig" - source "drivers/staging/media/omap24xx/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 205eba2..7db83f3 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -3,7 +3,6 @@ obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/ -obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_OMAP2) += omap24xx/ diff --git a/drivers/staging/media/go7007/Kconfig b/drivers/staging/media/go7007/Kconfig deleted file mode 100644 index 95a3af6..0000000 --- a/drivers/staging/media/go7007/Kconfig +++ /dev/null @@ -1,51 +0,0 @@ -config VIDEO_GO7007 - tristate "WIS GO7007 MPEG encoder support" - depends on VIDEO_DEV && I2C - depends on SND && USB - select VIDEOBUF2_VMALLOC - select VIDEO_TUNER - select CYPRESS_FIRMWARE - select SND_PCM - select VIDEO_SONY_BTF_MPX if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_TW2804 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_TW9903 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_TW9906 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_OV7640 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_UDA1342 if MEDIA_SUBDRV_AUTOSELECT - ---help--- - This is a video4linux driver for the WIS GO7007 MPEG - encoder chip. - - To compile this driver as a module, choose M here: the - module will be called go7007. - -config VIDEO_GO7007_USB - tristate "WIS GO7007 USB support" - depends on VIDEO_GO7007 && USB - ---help--- - This is a video4linux driver for the WIS GO7007 MPEG - encoder chip over USB. - - To compile this driver as a module, choose M here: the - module will be called go7007-usb. - -config VIDEO_GO7007_LOADER - tristate "WIS GO7007 Loader support" - depends on VIDEO_GO7007 - default y - ---help--- - This is a go7007 firmware loader driver for the WIS GO7007 - MPEG encoder chip over USB. - - To compile this driver as a module, choose M here: the - module will be called go7007-loader. - -config VIDEO_GO7007_USB_S2250_BOARD - tristate "Sensoray 2250/2251 support" - depends on VIDEO_GO7007_USB && USB - ---help--- - This is a video4linux driver for the Sensoray 2250/2251 device. - - To compile this driver as a module, choose M here: the - module will be called s2250. diff --git a/drivers/staging/media/go7007/Makefile b/drivers/staging/media/go7007/Makefile deleted file mode 100644 index 9c6ad4a..0000000 --- a/drivers/staging/media/go7007/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -obj-$(CONFIG_VIDEO_GO7007) += go7007.o -obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o -obj-$(CONFIG_VIDEO_GO7007_LOADER) += go7007-loader.o -obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o - -go7007-y := go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \ - snd-go7007.o - -s2250-y := s2250-board.o - -# Uncomment when the saa7134 patches get into upstream -#obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o -#ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/pci/saa7134 - -ccflags-$(CONFIG_VIDEO_GO7007_LOADER:m=y) += -Idrivers/media/common diff --git a/drivers/staging/media/go7007/README b/drivers/staging/media/go7007/README deleted file mode 100644 index 34516ea..0000000 --- a/drivers/staging/media/go7007/README +++ /dev/null @@ -1,136 +0,0 @@ -Todo: - - let s2250-board use i2c subdevs as well instead of hardcoding - support for the i2c devices. - - when the driver is moved out of staging, support for saa7134-go7007 - should be added to the saa7134 driver. The patch for that is - included below. - -Patch for saa7134: - -diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c -index dc68cf1..9a53794 100644 ---- a/drivers/media/pci/saa7134/saa7134-cards.c -+++ b/drivers/media/pci/saa7134/saa7134-cards.c -@@ -5790,6 +5790,29 @@ struct saa7134_board saa7134_boards[] = { - .gpio = 0x6010000, - } }, - }, -+ [SAA7134_BOARD_WIS_VOYAGER] = { -+ .name = "WIS Voyager or compatible", -+ .audio_clock = 0x00200000, -+ .tuner_type = TUNER_PHILIPS_TDA8290, -+ .radio_type = UNSET, -+ .tuner_addr = ADDR_UNSET, -+ .radio_addr = ADDR_UNSET, -+ .mpeg = SAA7134_MPEG_GO7007, -+ .inputs = { { -+ .name = name_comp1, -+ .vmux = 0, -+ .amux = LINE2, -+ }, { -+ .name = name_tv, -+ .vmux = 3, -+ .amux = TV, -+ .tv = 1, -+ }, { -+ .name = name_svideo, -+ .vmux = 6, -+ .amux = LINE1, -+ } }, -+ }, - - }; - -@@ -7037,6 +7060,12 @@ struct pci_device_id saa7134_pci_tbl[] = { - .subdevice = 0x0911, - .driver_data = SAA7134_BOARD_SENSORAY811_911, - }, { -+ .vendor = PCI_VENDOR_ID_PHILIPS, -+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, -+ .subvendor = 0x1905, /* WIS */ -+ .subdevice = 0x7007, -+ .driver_data = SAA7134_BOARD_WIS_VOYAGER, -+ }, { - /* --- boards without eeprom + subsystem ID --- */ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, -diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c -index 8fd24e7..0a849ea 100644 ---- a/drivers/media/pci/saa7134/saa7134-core.c -+++ b/drivers/media/pci/saa7134/saa7134-core.c -@@ -156,6 +156,8 @@ static void request_module_async(struct work_struct *work){ - request_module("saa7134-empress"); - if (card_is_dvb(dev)) - request_module("saa7134-dvb"); -+ if (card_is_go7007(dev)) -+ request_module("saa7134-go7007"); - if (alsa) { - if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130) - request_module("saa7134-alsa"); -@@ -557,8 +559,12 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id) - saa7134_irq_vbi_done(dev,status); - - if ((report & SAA7134_IRQ_REPORT_DONE_RA2) && -- card_has_mpeg(dev)) -- saa7134_irq_ts_done(dev,status); -+ card_has_mpeg(dev)) { -+ if (dev->mops->irq_ts_done != NULL) -+ dev->mops->irq_ts_done(dev, status); -+ else -+ saa7134_irq_ts_done(dev, status); -+ } - - if (report & SAA7134_IRQ_REPORT_GPIO16) { - switch (dev->has_remote) { -diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h -index 62169dd..5fad39a 100644 ---- a/drivers/media/pci/saa7134/saa7134.h -+++ b/drivers/media/pci/saa7134/saa7134.h -@@ -334,6 +334,7 @@ struct saa7134_card_ir { - #define SAA7134_BOARD_KWORLD_PC150U 189 - #define SAA7134_BOARD_ASUSTeK_PS3_100 190 - #define SAA7134_BOARD_HAWELL_HW_9004V1 191 -+#define SAA7134_BOARD_WIS_VOYAGER 192 - - #define SAA7134_MAXBOARDS 32 - #define SAA7134_INPUT_MAX 8 -@@ -364,6 +365,7 @@ enum saa7134_mpeg_type { - SAA7134_MPEG_UNUSED, - SAA7134_MPEG_EMPRESS, - SAA7134_MPEG_DVB, -+ SAA7134_MPEG_GO7007, - }; - - enum saa7134_mpeg_ts_type { -@@ -403,6 +405,7 @@ struct saa7134_board { - #define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name) - #define card_is_empress(dev) (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg) - #define card_is_dvb(dev) (SAA7134_MPEG_DVB == saa7134_boards[dev->board].mpeg) -+#define card_is_go7007(dev) (SAA7134_MPEG_GO7007 == saa7134_boards[dev->board].mpeg) - #define card_has_mpeg(dev) (SAA7134_MPEG_UNUSED != saa7134_boards[dev->board].mpeg) - #define card(dev) (saa7134_boards[dev->board]) - #define card_in(dev,n) (saa7134_boards[dev->board].inputs[n]) -@@ -535,6 +538,8 @@ struct saa7134_mpeg_ops { - int (*init)(struct saa7134_dev *dev); - int (*fini)(struct saa7134_dev *dev); - void (*signal_change)(struct saa7134_dev *dev); -+ void (*irq_ts_done)(struct saa7134_dev *dev, -+ unsigned long status); - }; - - /* global device status */ -diff --git a/drivers/staging/media/go7007/Makefile b/drivers/staging/media/go7007/Makefile -index 9c6ad4a..1b23689 100644 ---- a/drivers/staging/media/go7007/Makefile -+++ b/drivers/staging/media/go7007/Makefile -@@ -8,8 +8,7 @@ go7007-y := go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \ - - s2250-y := s2250-board.o - --# Uncomment when the saa7134 patches get into upstream --#obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o --#ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/pci/saa7134 -+obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o -+ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/pci/saa7134 - - ccflags-$(CONFIG_VIDEO_GO7007_LOADER:m=y) += -Idrivers/media/common diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c deleted file mode 100644 index 95cffb7..0000000 --- a/drivers/staging/media/go7007/go7007-driver.c +++ /dev/null @@ -1,766 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007-priv.h" - -/* - * Wait for an interrupt to be delivered from the GO7007SB and return - * the associated value and data. - * - * Must be called with the hw_lock held. - */ -int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data) -{ - go->interrupt_available = 0; - go->hpi_ops->read_interrupt(go); - if (wait_event_timeout(go->interrupt_waitq, - go->interrupt_available, 5*HZ) < 0) { - v4l2_err(&go->v4l2_dev, "timeout waiting for read interrupt\n"); - return -1; - } - if (!go->interrupt_available) - return -1; - go->interrupt_available = 0; - *value = go->interrupt_value & 0xfffe; - *data = go->interrupt_data; - return 0; -} -EXPORT_SYMBOL(go7007_read_interrupt); - -/* - * Read a register/address on the GO7007SB. - * - * Must be called with the hw_lock held. - */ -int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data) -{ - int count = 100; - u16 value; - - if (go7007_write_interrupt(go, 0x0010, addr) < 0) - return -EIO; - while (count-- > 0) { - if (go7007_read_interrupt(go, &value, data) == 0 && - value == 0xa000) - return 0; - } - return -EIO; -} -EXPORT_SYMBOL(go7007_read_addr); - -/* - * Send the boot firmware to the encoder, which just wakes it up and lets - * us talk to the GPIO pins and on-board I2C adapter. - * - * Must be called with the hw_lock held. - */ -static int go7007_load_encoder(struct go7007 *go) -{ - const struct firmware *fw_entry; - char fw_name[] = "go7007/go7007fw.bin"; - void *bounce; - int fw_len, rv = 0; - u16 intr_val, intr_data; - - if (go->boot_fw == NULL) { - if (request_firmware(&fw_entry, fw_name, go->dev)) { - v4l2_err(go, "unable to load firmware from file \"%s\"\n", fw_name); - return -1; - } - if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) { - v4l2_err(go, "file \"%s\" does not appear to be go7007 firmware\n", fw_name); - release_firmware(fw_entry); - return -1; - } - fw_len = fw_entry->size - 16; - bounce = kmemdup(fw_entry->data + 16, fw_len, GFP_KERNEL); - if (bounce == NULL) { - v4l2_err(go, "unable to allocate %d bytes for firmware transfer\n", fw_len); - release_firmware(fw_entry); - return -1; - } - release_firmware(fw_entry); - go->boot_fw_len = fw_len; - go->boot_fw = bounce; - } - if (go7007_interface_reset(go) < 0 || - go7007_send_firmware(go, go->boot_fw, go->boot_fw_len) < 0 || - go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || - (intr_val & ~0x1) != 0x5a5a) { - v4l2_err(go, "error transferring firmware\n"); - rv = -1; - } - return rv; -} - -MODULE_FIRMWARE("go7007/go7007fw.bin"); - -/* - * Boot the encoder and register the I2C adapter if requested. Do the - * minimum initialization necessary, since the board-specific code may - * still need to probe the board ID. - * - * Must NOT be called with the hw_lock held. - */ -int go7007_boot_encoder(struct go7007 *go, int init_i2c) -{ - int ret; - - mutex_lock(&go->hw_lock); - ret = go7007_load_encoder(go); - mutex_unlock(&go->hw_lock); - if (ret < 0) - return -1; - if (!init_i2c) - return 0; - if (go7007_i2c_init(go) < 0) - return -1; - go->i2c_adapter_online = 1; - return 0; -} -EXPORT_SYMBOL(go7007_boot_encoder); - -/* - * Configure any hardware-related registers in the GO7007, such as GPIO - * pins and bus parameters, which are board-specific. This assumes - * the boot firmware has already been downloaded. - * - * Must be called with the hw_lock held. - */ -static int go7007_init_encoder(struct go7007 *go) -{ - if (go->board_info->audio_flags & GO7007_AUDIO_I2S_MASTER) { - go7007_write_addr(go, 0x1000, 0x0811); - go7007_write_addr(go, 0x1000, 0x0c11); - } - switch (go->board_id) { - case GO7007_BOARDID_MATRIX_REV: - /* Set GPIO pin 0 to be an output (audio clock control) */ - go7007_write_addr(go, 0x3c82, 0x0001); - go7007_write_addr(go, 0x3c80, 0x00fe); - break; - case GO7007_BOARDID_ADLINK_MPG24: - /* set GPIO5 to be an output, currently low */ - go7007_write_addr(go, 0x3c82, 0x0000); - go7007_write_addr(go, 0x3c80, 0x00df); - break; - case GO7007_BOARDID_ADS_USBAV_709: - /* GPIO pin 0: audio clock control */ - /* pin 2: TW9906 reset */ - /* pin 3: capture LED */ - go7007_write_addr(go, 0x3c82, 0x000d); - go7007_write_addr(go, 0x3c80, 0x00f2); - break; - } - return 0; -} - -/* - * Send the boot firmware to the GO7007 and configure the registers. This - * is the only way to stop the encoder once it has started streaming video. - * - * Must be called with the hw_lock held. - */ -int go7007_reset_encoder(struct go7007 *go) -{ - if (go7007_load_encoder(go) < 0) - return -1; - return go7007_init_encoder(go); -} - -/* - * Attempt to instantiate an I2C client by ID, probably loading a module. - */ -static int init_i2c_module(struct i2c_adapter *adapter, const struct go_i2c *const i2c) -{ - struct go7007 *go = i2c_get_adapdata(adapter); - struct v4l2_device *v4l2_dev = &go->v4l2_dev; - struct v4l2_subdev *sd; - struct i2c_board_info info; - - memset(&info, 0, sizeof(info)); - strlcpy(info.type, i2c->type, sizeof(info.type)); - info.addr = i2c->addr; - info.flags = i2c->flags; - - sd = v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, NULL); - if (sd) { - if (i2c->is_video) - go->sd_video = sd; - if (i2c->is_audio) - go->sd_audio = sd; - return 0; - } - - pr_info("go7007: probing for module i2c:%s failed\n", i2c->type); - return -EINVAL; -} - -/* - * Detach and unregister the encoder. The go7007 struct won't be freed - * until v4l2 finishes releasing its resources and all associated fds are - * closed by applications. - */ -static void go7007_remove(struct v4l2_device *v4l2_dev) -{ - struct go7007 *go = container_of(v4l2_dev, struct go7007, v4l2_dev); - - v4l2_device_unregister(v4l2_dev); - if (go->hpi_ops->release) - go->hpi_ops->release(go); - if (go->i2c_adapter_online) { - i2c_del_adapter(&go->i2c_adapter); - go->i2c_adapter_online = 0; - } - - kfree(go->boot_fw); - go7007_v4l2_remove(go); - kfree(go); -} - -/* - * Finalize the GO7007 hardware setup, register the on-board I2C adapter - * (if used on this board), load the I2C client driver for the sensor - * (SAA7115 or whatever) and other devices, and register the ALSA and V4L2 - * interfaces. - * - * Must NOT be called with the hw_lock held. - */ -int go7007_register_encoder(struct go7007 *go, unsigned num_i2c_devs) -{ - int i, ret; - - dev_info(go->dev, "go7007: registering new %s\n", go->name); - - go->v4l2_dev.release = go7007_remove; - ret = v4l2_device_register(go->dev, &go->v4l2_dev); - if (ret < 0) - return ret; - - mutex_lock(&go->hw_lock); - ret = go7007_init_encoder(go); - mutex_unlock(&go->hw_lock); - if (ret < 0) - return ret; - - ret = go7007_v4l2_ctrl_init(go); - if (ret < 0) - return ret; - - if (!go->i2c_adapter_online && - go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) { - ret = go7007_i2c_init(go); - if (ret < 0) - return ret; - go->i2c_adapter_online = 1; - } - if (go->i2c_adapter_online) { - if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) { - /* Reset the TW9906 */ - go7007_write_addr(go, 0x3c82, 0x0009); - msleep(50); - go7007_write_addr(go, 0x3c82, 0x000d); - } - for (i = 0; i < num_i2c_devs; ++i) - init_i2c_module(&go->i2c_adapter, &go->board_info->i2c_devs[i]); - - if (go->tuner_type >= 0) { - struct tuner_setup setup = { - .addr = ADDR_UNSET, - .type = go->tuner_type, - .mode_mask = T_ANALOG_TV, - }; - - v4l2_device_call_all(&go->v4l2_dev, 0, tuner, - s_type_addr, &setup); - } - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) - v4l2_subdev_call(go->sd_video, video, s_routing, - 0, 0, go->channel_number + 1); - } - - ret = go7007_v4l2_init(go); - if (ret < 0) - return ret; - - if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) { - go->audio_enabled = 1; - go7007_snd_init(go); - } - return 0; -} -EXPORT_SYMBOL(go7007_register_encoder); - -/* - * Send the encode firmware to the encoder, which will cause it - * to immediately start delivering the video and audio streams. - * - * Must be called with the hw_lock held. - */ -int go7007_start_encoder(struct go7007 *go) -{ - u8 *fw; - int fw_len, rv = 0, i, x, y; - u16 intr_val, intr_data; - - go->modet_enable = 0; - for (i = 0; i < 4; i++) - go->modet[i].enable = 0; - - switch (v4l2_ctrl_g_ctrl(go->modet_mode)) { - case V4L2_DETECT_MD_MODE_GLOBAL: - memset(go->modet_map, 0, sizeof(go->modet_map)); - go->modet[0].enable = 1; - go->modet_enable = 1; - break; - case V4L2_DETECT_MD_MODE_REGION_GRID: - for (y = 0; y < go->height / 16; y++) { - for (x = 0; x < go->width / 16; x++) { - int idx = y * go->width / 16 + x; - - go->modet[go->modet_map[idx]].enable = 1; - } - } - go->modet_enable = 1; - break; - } - - if (go->dvd_mode) - go->modet_enable = 0; - - if (go7007_construct_fw_image(go, &fw, &fw_len) < 0) - return -1; - - if (go7007_send_firmware(go, fw, fw_len) < 0 || - go7007_read_interrupt(go, &intr_val, &intr_data) < 0) { - v4l2_err(&go->v4l2_dev, "error transferring firmware\n"); - rv = -1; - goto start_error; - } - - go->state = STATE_DATA; - go->parse_length = 0; - go->seen_frame = 0; - if (go7007_stream_start(go) < 0) { - v4l2_err(&go->v4l2_dev, "error starting stream transfer\n"); - rv = -1; - goto start_error; - } - -start_error: - kfree(fw); - return rv; -} - -/* - * Store a byte in the current video buffer, if there is one. - */ -static inline void store_byte(struct go7007_buffer *vb, u8 byte) -{ - if (vb && vb->vb.v4l2_planes[0].bytesused < GO7007_BUF_SIZE) { - u8 *ptr = vb2_plane_vaddr(&vb->vb, 0); - - ptr[vb->vb.v4l2_planes[0].bytesused++] = byte; - } -} - -static void go7007_set_motion_regions(struct go7007 *go, struct go7007_buffer *vb, - u32 motion_regions) -{ - if (motion_regions != go->modet_event_status) { - struct v4l2_event ev = { - .type = V4L2_EVENT_MOTION_DET, - .u.motion_det = { - .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, - .frame_sequence = vb->vb.v4l2_buf.sequence, - .region_mask = motion_regions, - }, - }; - - v4l2_event_queue(&go->vdev, &ev); - go->modet_event_status = motion_regions; - } -} - -/* - * Determine regions with motion and send a motion detection event - * in case of changes. - */ -static void go7007_motion_regions(struct go7007 *go, struct go7007_buffer *vb) -{ - u32 *bytesused = &vb->vb.v4l2_planes[0].bytesused; - unsigned motion[4] = { 0, 0, 0, 0 }; - u32 motion_regions = 0; - unsigned stride = (go->width + 7) >> 3; - unsigned x, y; - int i; - - for (i = 0; i < 216; ++i) - store_byte(vb, go->active_map[i]); - for (y = 0; y < go->height / 16; y++) { - for (x = 0; x < go->width / 16; x++) { - if (!(go->active_map[y * stride + (x >> 3)] & (1 << (x & 7)))) - continue; - motion[go->modet_map[y * (go->width / 16) + x]]++; - } - } - motion_regions = ((motion[0] > 0) << 0) | - ((motion[1] > 0) << 1) | - ((motion[2] > 0) << 2) | - ((motion[3] > 0) << 3); - *bytesused -= 216; - go7007_set_motion_regions(go, vb, motion_regions); -} - -/* - * Deliver the last video buffer and get a new one to start writing to. - */ -static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buffer *vb) -{ - u32 *bytesused = &vb->vb.v4l2_planes[0].bytesused; - struct go7007_buffer *vb_tmp = NULL; - - if (vb == NULL) { - spin_lock(&go->spinlock); - if (!list_empty(&go->vidq_active)) - vb = go->active_buf = - list_first_entry(&go->vidq_active, struct go7007_buffer, list); - spin_unlock(&go->spinlock); - go->next_seq++; - return vb; - } - - vb->vb.v4l2_buf.sequence = go->next_seq++; - if (vb->modet_active && *bytesused + 216 < GO7007_BUF_SIZE) - go7007_motion_regions(go, vb); - else - go7007_set_motion_regions(go, vb, 0); - - v4l2_get_timestamp(&vb->vb.v4l2_buf.timestamp); - vb_tmp = vb; - spin_lock(&go->spinlock); - list_del(&vb->list); - if (list_empty(&go->vidq_active)) - vb = NULL; - else - vb = list_first_entry(&go->vidq_active, struct go7007_buffer, list); - go->active_buf = vb; - spin_unlock(&go->spinlock); - vb2_buffer_done(&vb_tmp->vb, VB2_BUF_STATE_DONE); - return vb; -} - -static void write_bitmap_word(struct go7007 *go) -{ - int x, y, i, stride = ((go->width >> 4) + 7) >> 3; - - for (i = 0; i < 16; ++i) { - y = (((go->parse_length - 1) << 3) + i) / (go->width >> 4); - x = (((go->parse_length - 1) << 3) + i) % (go->width >> 4); - if (stride * y + (x >> 3) < sizeof(go->active_map)) - go->active_map[stride * y + (x >> 3)] |= - (go->modet_word & 1) << (x & 0x7); - go->modet_word >>= 1; - } -} - -/* - * Parse a chunk of the video stream into frames. The frames are not - * delimited by the hardware, so we have to parse the frame boundaries - * based on the type of video stream we're receiving. - */ -void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) -{ - struct go7007_buffer *vb = go->active_buf; - int i, seq_start_code = -1, gop_start_code = -1, frame_start_code = -1; - - switch (go->format) { - case V4L2_PIX_FMT_MPEG4: - seq_start_code = 0xB0; - gop_start_code = 0xB3; - frame_start_code = 0xB6; - break; - case V4L2_PIX_FMT_MPEG1: - case V4L2_PIX_FMT_MPEG2: - seq_start_code = 0xB3; - gop_start_code = 0xB8; - frame_start_code = 0x00; - break; - } - - for (i = 0; i < length; ++i) { - if (vb && vb->vb.v4l2_planes[0].bytesused >= GO7007_BUF_SIZE - 3) { - v4l2_info(&go->v4l2_dev, "dropping oversized frame\n"); - vb->vb.v4l2_planes[0].bytesused = 0; - vb->frame_offset = 0; - vb->modet_active = 0; - vb = go->active_buf = NULL; - } - - switch (go->state) { - case STATE_DATA: - switch (buf[i]) { - case 0x00: - go->state = STATE_00; - break; - case 0xFF: - go->state = STATE_FF; - break; - default: - store_byte(vb, buf[i]); - break; - } - break; - case STATE_00: - switch (buf[i]) { - case 0x00: - go->state = STATE_00_00; - break; - case 0xFF: - store_byte(vb, 0x00); - go->state = STATE_FF; - break; - default: - store_byte(vb, 0x00); - store_byte(vb, buf[i]); - go->state = STATE_DATA; - break; - } - break; - case STATE_00_00: - switch (buf[i]) { - case 0x00: - store_byte(vb, 0x00); - /* go->state remains STATE_00_00 */ - break; - case 0x01: - go->state = STATE_00_00_01; - break; - case 0xFF: - store_byte(vb, 0x00); - store_byte(vb, 0x00); - go->state = STATE_FF; - break; - default: - store_byte(vb, 0x00); - store_byte(vb, 0x00); - store_byte(vb, buf[i]); - go->state = STATE_DATA; - break; - } - break; - case STATE_00_00_01: - if (buf[i] == 0xF8 && go->modet_enable == 0) { - /* MODET start code, but MODET not enabled */ - store_byte(vb, 0x00); - store_byte(vb, 0x00); - store_byte(vb, 0x01); - store_byte(vb, 0xF8); - go->state = STATE_DATA; - break; - } - /* If this is the start of a new MPEG frame, - * get a new buffer */ - if ((go->format == V4L2_PIX_FMT_MPEG1 || - go->format == V4L2_PIX_FMT_MPEG2 || - go->format == V4L2_PIX_FMT_MPEG4) && - (buf[i] == seq_start_code || - buf[i] == gop_start_code || - buf[i] == frame_start_code)) { - if (vb == NULL || go->seen_frame) - vb = frame_boundary(go, vb); - go->seen_frame = buf[i] == frame_start_code; - if (vb && go->seen_frame) - vb->frame_offset = vb->vb.v4l2_planes[0].bytesused; - } - /* Handle any special chunk types, or just write the - * start code to the (potentially new) buffer */ - switch (buf[i]) { - case 0xF5: /* timestamp */ - go->parse_length = 12; - go->state = STATE_UNPARSED; - break; - case 0xF6: /* vbi */ - go->state = STATE_VBI_LEN_A; - break; - case 0xF8: /* MD map */ - go->parse_length = 0; - memset(go->active_map, 0, - sizeof(go->active_map)); - go->state = STATE_MODET_MAP; - break; - case 0xFF: /* Potential JPEG start code */ - store_byte(vb, 0x00); - store_byte(vb, 0x00); - store_byte(vb, 0x01); - go->state = STATE_FF; - break; - default: - store_byte(vb, 0x00); - store_byte(vb, 0x00); - store_byte(vb, 0x01); - store_byte(vb, buf[i]); - go->state = STATE_DATA; - break; - } - break; - case STATE_FF: - switch (buf[i]) { - case 0x00: - store_byte(vb, 0xFF); - go->state = STATE_00; - break; - case 0xFF: - store_byte(vb, 0xFF); - /* go->state remains STATE_FF */ - break; - case 0xD8: - if (go->format == V4L2_PIX_FMT_MJPEG) - vb = frame_boundary(go, vb); - /* fall through */ - default: - store_byte(vb, 0xFF); - store_byte(vb, buf[i]); - go->state = STATE_DATA; - break; - } - break; - case STATE_VBI_LEN_A: - go->parse_length = buf[i] << 8; - go->state = STATE_VBI_LEN_B; - break; - case STATE_VBI_LEN_B: - go->parse_length |= buf[i]; - if (go->parse_length > 0) - go->state = STATE_UNPARSED; - else - go->state = STATE_DATA; - break; - case STATE_MODET_MAP: - if (go->parse_length < 204) { - if (go->parse_length & 1) { - go->modet_word |= buf[i]; - write_bitmap_word(go); - } else - go->modet_word = buf[i] << 8; - } else if (go->parse_length == 207 && vb) { - vb->modet_active = buf[i]; - } - if (++go->parse_length == 208) - go->state = STATE_DATA; - break; - case STATE_UNPARSED: - if (--go->parse_length == 0) - go->state = STATE_DATA; - break; - } - } -} -EXPORT_SYMBOL(go7007_parse_video_stream); - -/* - * Allocate a new go7007 struct. Used by the hardware-specific probe. - */ -struct go7007 *go7007_alloc(const struct go7007_board_info *board, - struct device *dev) -{ - struct go7007 *go; - int i; - - go = kzalloc(sizeof(struct go7007), GFP_KERNEL); - if (go == NULL) - return NULL; - go->dev = dev; - go->board_info = board; - go->board_id = 0; - go->tuner_type = -1; - go->channel_number = 0; - go->name[0] = 0; - mutex_init(&go->hw_lock); - init_waitqueue_head(&go->frame_waitq); - spin_lock_init(&go->spinlock); - go->status = STATUS_INIT; - memset(&go->i2c_adapter, 0, sizeof(go->i2c_adapter)); - go->i2c_adapter_online = 0; - go->interrupt_available = 0; - init_waitqueue_head(&go->interrupt_waitq); - go->input = 0; - go7007_update_board(go); - go->encoder_h_halve = 0; - go->encoder_v_halve = 0; - go->encoder_subsample = 0; - go->format = V4L2_PIX_FMT_MJPEG; - go->bitrate = 1500000; - go->fps_scale = 1; - go->pali = 0; - go->aspect_ratio = GO7007_RATIO_1_1; - go->gop_size = 0; - go->ipb = 0; - go->closed_gop = 0; - go->repeat_seqhead = 0; - go->seq_header_enable = 0; - go->gop_header_enable = 0; - go->dvd_mode = 0; - go->interlace_coding = 0; - for (i = 0; i < 4; ++i) - go->modet[i].enable = 0; - for (i = 0; i < 1624; ++i) - go->modet_map[i] = 0; - go->audio_deliver = NULL; - go->audio_enabled = 0; - - return go; -} -EXPORT_SYMBOL(go7007_alloc); - -void go7007_update_board(struct go7007 *go) -{ - const struct go7007_board_info *board = go->board_info; - - if (board->sensor_flags & GO7007_SENSOR_TV) { - go->standard = GO7007_STD_NTSC; - go->std = V4L2_STD_NTSC_M; - go->width = 720; - go->height = 480; - go->sensor_framerate = 30000; - } else { - go->standard = GO7007_STD_OTHER; - go->width = board->sensor_width; - go->height = board->sensor_height; - go->sensor_framerate = board->sensor_framerate; - } - go->encoder_v_offset = board->sensor_v_offset; - go->encoder_h_offset = board->sensor_h_offset; -} -EXPORT_SYMBOL(go7007_update_board); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/go7007-fw.c b/drivers/staging/media/go7007/go7007-fw.c deleted file mode 100644 index 5f4c9b9..0000000 --- a/drivers/staging/media/go7007/go7007-fw.c +++ /dev/null @@ -1,1628 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * 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. - */ - -/* - * This file contains code to generate a firmware image for the GO7007SB - * encoder. Much of the firmware is read verbatim from a file, but some of - * it concerning bitrate control and other things that can be configured at - * run-time are generated dynamically. Note that the format headers - * generated here do not affect the functioning of the encoder; they are - * merely parroted back to the host at the start of each frame. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007-priv.h" - -#define GO7007_FW_NAME "go7007/go7007tv.bin" - -/* Constants used in the source firmware image to describe code segments */ - -#define FLAG_MODE_MJPEG (1) -#define FLAG_MODE_MPEG1 (1<<1) -#define FLAG_MODE_MPEG2 (1<<2) -#define FLAG_MODE_MPEG4 (1<<3) -#define FLAG_MODE_H263 (1<<4) -#define FLAG_MODE_ALL (FLAG_MODE_MJPEG | FLAG_MODE_MPEG1 | \ - FLAG_MODE_MPEG2 | FLAG_MODE_MPEG4 | \ - FLAG_MODE_H263) -#define FLAG_SPECIAL (1<<8) - -#define SPECIAL_FRM_HEAD 0 -#define SPECIAL_BRC_CTRL 1 -#define SPECIAL_CONFIG 2 -#define SPECIAL_SEQHEAD 3 -#define SPECIAL_AV_SYNC 4 -#define SPECIAL_FINAL 5 -#define SPECIAL_AUDIO 6 -#define SPECIAL_MODET 7 - -/* Little data class for creating MPEG headers bit-by-bit */ - -struct code_gen { - unsigned char *p; /* destination */ - u32 a; /* collects bits at the top of the variable */ - int b; /* bit position of most recently-written bit */ - int len; /* written out so far */ -}; - -#define CODE_GEN(name, dest) struct code_gen name = { dest, 0, 32, 0 } - -#define CODE_ADD(name, val, length) do { \ - name.b -= (length); \ - name.a |= (val) << name.b; \ - while (name.b <= 24) { \ - *name.p = name.a >> 24; \ - ++name.p; \ - name.a <<= 8; \ - name.b += 8; \ - name.len += 8; \ - } \ -} while (0) - -#define CODE_LENGTH(name) (name.len + (32 - name.b)) - -/* Tables for creating the bitrate control data */ - -static const s16 converge_speed_ip[101] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, - 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, - 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, - 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, - 19, 20, 22, 23, 25, 27, 30, 32, 35, 38, - 41, 45, 49, 53, 58, 63, 69, 76, 83, 91, - 100 -}; - -static const s16 converge_speed_ipb[101] = { - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, - 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, - 9, 9, 10, 10, 11, 12, 12, 13, 14, 14, - 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, - 28, 30, 32, 34, 37, 40, 42, 46, 49, 53, - 57, 61, 66, 71, 77, 83, 90, 97, 106, 115, - 125, 135, 147, 161, 175, 191, 209, 228, 249, 273, - 300 -}; - -static const s16 LAMBDA_table[4][101] = { - { 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, - 19, 19, 19, 20, 20, 20, 21, 21, 22, 22, - 22, 23, 23, 24, 24, 25, 25, 25, 26, 26, - 27, 27, 28, 28, 29, 29, 30, 31, 31, 32, - 32, 33, 33, 34, 35, 35, 36, 37, 37, 38, - 39, 39, 40, 41, 42, 42, 43, 44, 45, 46, - 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, - 67, 68, 69, 70, 72, 73, 74, 76, 77, 78, - 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, - 96 - }, - { - 20, 20, 20, 21, 21, 21, 22, 22, 23, 23, - 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, - 28, 29, 29, 30, 30, 31, 31, 32, 33, 33, - 34, 34, 35, 36, 36, 37, 38, 38, 39, 40, - 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, - 70, 71, 72, 73, 75, 76, 78, 79, 80, 82, - 83, 85, 86, 88, 90, 91, 93, 95, 96, 98, - 100, 102, 103, 105, 107, 109, 111, 113, 115, 117, - 120 - }, - { - 24, 24, 24, 25, 25, 26, 26, 27, 27, 28, - 28, 29, 29, 30, 30, 31, 31, 32, 33, 33, - 34, 34, 35, 36, 36, 37, 38, 38, 39, 40, - 41, 41, 42, 43, 44, 44, 45, 46, 47, 48, - 49, 50, 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 62, 63, 64, 65, 66, 67, 69, - 70, 71, 72, 74, 75, 76, 78, 79, 81, 82, - 84, 85, 87, 88, 90, 92, 93, 95, 97, 98, - 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, - 120, 122, 124, 127, 129, 131, 134, 136, 138, 141, - 144 - }, - { - 32, 32, 33, 33, 34, 34, 35, 36, 36, 37, - 38, 38, 39, 40, 41, 41, 42, 43, 44, 44, - 45, 46, 47, 48, 49, 50, 50, 51, 52, 53, - 54, 55, 56, 57, 58, 59, 60, 62, 63, 64, - 65, 66, 67, 69, 70, 71, 72, 74, 75, 76, - 78, 79, 81, 82, 84, 85, 87, 88, 90, 92, - 93, 95, 97, 98, 100, 102, 104, 106, 108, 110, - 112, 114, 116, 118, 120, 122, 124, 127, 129, 131, - 134, 136, 139, 141, 144, 146, 149, 152, 154, 157, - 160, 163, 166, 169, 172, 175, 178, 181, 185, 188, - 192 - } -}; - -/* MPEG blank frame generation tables */ - -enum mpeg_frame_type { - PFRAME, - BFRAME_PRE, - BFRAME_POST, - BFRAME_BIDIR, - BFRAME_EMPTY -}; - -static const u32 addrinctab[33][2] = { - { 0x01, 1 }, { 0x03, 3 }, { 0x02, 3 }, { 0x03, 4 }, - { 0x02, 4 }, { 0x03, 5 }, { 0x02, 5 }, { 0x07, 7 }, - { 0x06, 7 }, { 0x0b, 8 }, { 0x0a, 8 }, { 0x09, 8 }, - { 0x08, 8 }, { 0x07, 8 }, { 0x06, 8 }, { 0x17, 10 }, - { 0x16, 10 }, { 0x15, 10 }, { 0x14, 10 }, { 0x13, 10 }, - { 0x12, 10 }, { 0x23, 11 }, { 0x22, 11 }, { 0x21, 11 }, - { 0x20, 11 }, { 0x1f, 11 }, { 0x1e, 11 }, { 0x1d, 11 }, - { 0x1c, 11 }, { 0x1b, 11 }, { 0x1a, 11 }, { 0x19, 11 }, - { 0x18, 11 } -}; - -/* Standard JPEG tables */ - -static const u8 default_intra_quant_table[] = { - 8, 16, 19, 22, 26, 27, 29, 34, - 16, 16, 22, 24, 27, 29, 34, 37, - 19, 22, 26, 27, 29, 34, 34, 38, - 22, 22, 26, 27, 29, 34, 37, 40, - 22, 26, 27, 29, 32, 35, 40, 48, - 26, 27, 29, 32, 35, 40, 48, 58, - 26, 27, 29, 34, 38, 46, 56, 69, - 27, 29, 35, 38, 46, 56, 69, 83 -}; - -static const u8 bits_dc_luminance[] = { - 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 -}; - -static const u8 val_dc_luminance[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 -}; - -static const u8 bits_dc_chrominance[] = { - 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 -}; - -static const u8 val_dc_chrominance[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 -}; - -static const u8 bits_ac_luminance[] = { - 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d -}; - -static const u8 val_ac_luminance[] = { - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, - 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, - 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, - 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa -}; - -static const u8 bits_ac_chrominance[] = { - 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 -}; - -static const u8 val_ac_chrominance[] = { - 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, - 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, - 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, - 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, - 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, - 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, - 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa -}; - -/* Zig-zag mapping for quant table - * - * OK, let's do this mapping on the actual table above so it doesn't have - * to be done on the fly. - */ -static const int zz[64] = { - 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 -}; - -static int copy_packages(__le16 *dest, u16 *src, int pkg_cnt, int space) -{ - int i, cnt = pkg_cnt * 32; - - if (space < cnt) - return -1; - - for (i = 0; i < cnt; ++i) - dest[i] = cpu_to_le16p(src + i); - - return cnt; -} - -static int mjpeg_frame_header(struct go7007 *go, unsigned char *buf, int q) -{ - int i, p = 0; - - buf[p++] = 0xff; - buf[p++] = 0xd8; - buf[p++] = 0xff; - buf[p++] = 0xdb; - buf[p++] = 0; - buf[p++] = 2 + 65; - buf[p++] = 0; - buf[p++] = default_intra_quant_table[0]; - for (i = 1; i < 64; ++i) - /* buf[p++] = (default_intra_quant_table[i] * q) >> 3; */ - buf[p++] = (default_intra_quant_table[zz[i]] * q) >> 3; - buf[p++] = 0xff; - buf[p++] = 0xc0; - buf[p++] = 0; - buf[p++] = 17; - buf[p++] = 8; - buf[p++] = go->height >> 8; - buf[p++] = go->height & 0xff; - buf[p++] = go->width >> 8; - buf[p++] = go->width & 0xff; - buf[p++] = 3; - buf[p++] = 1; - buf[p++] = 0x22; - buf[p++] = 0; - buf[p++] = 2; - buf[p++] = 0x11; - buf[p++] = 0; - buf[p++] = 3; - buf[p++] = 0x11; - buf[p++] = 0; - buf[p++] = 0xff; - buf[p++] = 0xc4; - buf[p++] = 418 >> 8; - buf[p++] = 418 & 0xff; - buf[p++] = 0x00; - memcpy(buf + p, bits_dc_luminance + 1, 16); - p += 16; - memcpy(buf + p, val_dc_luminance, sizeof(val_dc_luminance)); - p += sizeof(val_dc_luminance); - buf[p++] = 0x01; - memcpy(buf + p, bits_dc_chrominance + 1, 16); - p += 16; - memcpy(buf + p, val_dc_chrominance, sizeof(val_dc_chrominance)); - p += sizeof(val_dc_chrominance); - buf[p++] = 0x10; - memcpy(buf + p, bits_ac_luminance + 1, 16); - p += 16; - memcpy(buf + p, val_ac_luminance, sizeof(val_ac_luminance)); - p += sizeof(val_ac_luminance); - buf[p++] = 0x11; - memcpy(buf + p, bits_ac_chrominance + 1, 16); - p += 16; - memcpy(buf + p, val_ac_chrominance, sizeof(val_ac_chrominance)); - p += sizeof(val_ac_chrominance); - buf[p++] = 0xff; - buf[p++] = 0xda; - buf[p++] = 0; - buf[p++] = 12; - buf[p++] = 3; - buf[p++] = 1; - buf[p++] = 0x00; - buf[p++] = 2; - buf[p++] = 0x11; - buf[p++] = 3; - buf[p++] = 0x11; - buf[p++] = 0; - buf[p++] = 63; - buf[p++] = 0; - return p; -} - -static int gen_mjpeghdr_to_package(struct go7007 *go, __le16 *code, int space) -{ - u8 *buf; - u16 mem = 0x3e00; - unsigned int addr = 0x19; - int size = 0, i, off = 0, chunk; - - buf = kzalloc(4096, GFP_KERNEL); - if (buf == NULL) - return -1; - - for (i = 1; i < 32; ++i) { - mjpeg_frame_header(go, buf + size, i); - size += 80; - } - chunk = mjpeg_frame_header(go, buf + size, 1); - memmove(buf + size, buf + size + 80, chunk - 80); - size += chunk - 80; - - for (i = 0; i < size; i += chunk * 2) { - if (space - off < 32) { - off = -1; - goto done; - } - - code[off + 1] = __cpu_to_le16(0x8000 | mem); - - chunk = 28; - if (mem + chunk > 0x4000) - chunk = 0x4000 - mem; - if (i + 2 * chunk > size) - chunk = (size - i) / 2; - - if (chunk < 28) { - code[off] = __cpu_to_le16(0x4000 | chunk); - code[off + 31] = __cpu_to_le16(addr++); - mem = 0x3e00; - } else { - code[off] = __cpu_to_le16(0x1000 | 28); - code[off + 31] = 0; - mem += 28; - } - - memcpy(&code[off + 2], buf + i, chunk * 2); - off += 32; - } -done: - kfree(buf); - return off; -} - -static int mpeg1_frame_header(struct go7007 *go, unsigned char *buf, - int modulo, int pict_struct, enum mpeg_frame_type frame) -{ - int i, j, mb_code, mb_len; - int rows = go->interlace_coding ? go->height / 32 : go->height / 16; - CODE_GEN(c, buf + 6); - - switch (frame) { - case PFRAME: - mb_code = 0x1; - mb_len = 3; - break; - case BFRAME_PRE: - mb_code = 0x2; - mb_len = 4; - break; - case BFRAME_POST: - mb_code = 0x2; - mb_len = 3; - break; - case BFRAME_BIDIR: - mb_code = 0x2; - mb_len = 2; - break; - default: /* keep the compiler happy */ - mb_code = mb_len = 0; - break; - } - - CODE_ADD(c, frame == PFRAME ? 0x2 : 0x3, 13); - CODE_ADD(c, 0xffff, 16); - CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 0x7 : 0x4, 4); - if (frame != PFRAME) - CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 0x7 : 0x4, 4); - else - CODE_ADD(c, 0, 4); /* Is this supposed to be here?? */ - CODE_ADD(c, 0, 3); /* What is this?? */ - /* Byte-align with zeros */ - j = 8 - (CODE_LENGTH(c) % 8); - if (j != 8) - CODE_ADD(c, 0, j); - - if (go->format == V4L2_PIX_FMT_MPEG2) { - CODE_ADD(c, 0x1, 24); - CODE_ADD(c, 0xb5, 8); - CODE_ADD(c, 0x844, 12); - CODE_ADD(c, frame == PFRAME ? 0xff : 0x44, 8); - if (go->interlace_coding) { - CODE_ADD(c, pict_struct, 4); - if (go->dvd_mode) - CODE_ADD(c, 0x000, 11); - else - CODE_ADD(c, 0x200, 11); - } else { - CODE_ADD(c, 0x3, 4); - CODE_ADD(c, 0x20c, 11); - } - /* Byte-align with zeros */ - j = 8 - (CODE_LENGTH(c) % 8); - if (j != 8) - CODE_ADD(c, 0, j); - } - - for (i = 0; i < rows; ++i) { - CODE_ADD(c, 1, 24); - CODE_ADD(c, i + 1, 8); - CODE_ADD(c, 0x2, 6); - CODE_ADD(c, 0x1, 1); - CODE_ADD(c, mb_code, mb_len); - if (go->interlace_coding) { - CODE_ADD(c, 0x1, 2); - CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); - } - if (frame == BFRAME_BIDIR) { - CODE_ADD(c, 0x3, 2); - if (go->interlace_coding) - CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); - } - CODE_ADD(c, 0x3, 2); - for (j = (go->width >> 4) - 2; j >= 33; j -= 33) - CODE_ADD(c, 0x8, 11); - CODE_ADD(c, addrinctab[j][0], addrinctab[j][1]); - CODE_ADD(c, mb_code, mb_len); - if (go->interlace_coding) { - CODE_ADD(c, 0x1, 2); - CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); - } - if (frame == BFRAME_BIDIR) { - CODE_ADD(c, 0x3, 2); - if (go->interlace_coding) - CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); - } - CODE_ADD(c, 0x3, 2); - - /* Byte-align with zeros */ - j = 8 - (CODE_LENGTH(c) % 8); - if (j != 8) - CODE_ADD(c, 0, j); - } - - i = CODE_LENGTH(c) + 4 * 8; - buf[2] = 0x00; - buf[3] = 0x00; - buf[4] = 0x01; - buf[5] = 0x00; - return i; -} - -static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext) -{ - int i, aspect_ratio, picture_rate; - CODE_GEN(c, buf + 6); - - if (go->format == V4L2_PIX_FMT_MPEG1) { - switch (go->aspect_ratio) { - case GO7007_RATIO_4_3: - aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2; - break; - case GO7007_RATIO_16_9: - aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4; - break; - default: - aspect_ratio = 1; - break; - } - } else { - switch (go->aspect_ratio) { - case GO7007_RATIO_4_3: - aspect_ratio = 2; - break; - case GO7007_RATIO_16_9: - aspect_ratio = 3; - break; - default: - aspect_ratio = 1; - break; - } - } - switch (go->sensor_framerate) { - case 24000: - picture_rate = 1; - break; - case 24024: - picture_rate = 2; - break; - case 25025: - picture_rate = go->interlace_coding ? 6 : 3; - break; - case 30000: - picture_rate = go->interlace_coding ? 7 : 4; - break; - case 30030: - picture_rate = go->interlace_coding ? 8 : 5; - break; - default: - picture_rate = 5; /* 30 fps seems like a reasonable default */ - break; - } - - CODE_ADD(c, go->width, 12); - CODE_ADD(c, go->height, 12); - CODE_ADD(c, aspect_ratio, 4); - CODE_ADD(c, picture_rate, 4); - CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 20000 : 0x3ffff, 18); - CODE_ADD(c, 1, 1); - CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 112 : 20, 10); - CODE_ADD(c, 0, 3); - - /* Byte-align with zeros */ - i = 8 - (CODE_LENGTH(c) % 8); - if (i != 8) - CODE_ADD(c, 0, i); - - if (go->format == V4L2_PIX_FMT_MPEG2) { - CODE_ADD(c, 0x1, 24); - CODE_ADD(c, 0xb5, 8); - CODE_ADD(c, 0x148, 12); - if (go->interlace_coding) - CODE_ADD(c, 0x20001, 20); - else - CODE_ADD(c, 0xa0001, 20); - CODE_ADD(c, 0, 16); - - /* Byte-align with zeros */ - i = 8 - (CODE_LENGTH(c) % 8); - if (i != 8) - CODE_ADD(c, 0, i); - - if (ext) { - CODE_ADD(c, 0x1, 24); - CODE_ADD(c, 0xb52, 12); - CODE_ADD(c, go->standard == GO7007_STD_NTSC ? 2 : 1, 3); - CODE_ADD(c, 0x105, 9); - CODE_ADD(c, 0x505, 16); - CODE_ADD(c, go->width, 14); - CODE_ADD(c, 1, 1); - CODE_ADD(c, go->height, 14); - - /* Byte-align with zeros */ - i = 8 - (CODE_LENGTH(c) % 8); - if (i != 8) - CODE_ADD(c, 0, i); - } - } - - i = CODE_LENGTH(c) + 4 * 8; - buf[0] = i & 0xff; - buf[1] = i >> 8; - buf[2] = 0x00; - buf[3] = 0x00; - buf[4] = 0x01; - buf[5] = 0xb3; - return i; -} - -static int gen_mpeg1hdr_to_package(struct go7007 *go, - __le16 *code, int space, int *framelen) -{ - u8 *buf; - u16 mem = 0x3e00; - unsigned int addr = 0x19; - int i, off = 0, chunk; - - buf = kzalloc(5120, GFP_KERNEL); - if (buf == NULL) - return -1; - - framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME); - if (go->interlace_coding) - framelen[0] += mpeg1_frame_header(go, buf + framelen[0] / 8, - 0, 2, PFRAME); - buf[0] = framelen[0] & 0xff; - buf[1] = framelen[0] >> 8; - i = 368; - framelen[1] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_PRE); - if (go->interlace_coding) - framelen[1] += mpeg1_frame_header(go, buf + i + framelen[1] / 8, - 0, 2, BFRAME_PRE); - buf[i] = framelen[1] & 0xff; - buf[i + 1] = framelen[1] >> 8; - i += 1632; - framelen[2] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_POST); - if (go->interlace_coding) - framelen[2] += mpeg1_frame_header(go, buf + i + framelen[2] / 8, - 0, 2, BFRAME_POST); - buf[i] = framelen[2] & 0xff; - buf[i + 1] = framelen[2] >> 8; - i += 1432; - framelen[3] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_BIDIR); - if (go->interlace_coding) - framelen[3] += mpeg1_frame_header(go, buf + i + framelen[3] / 8, - 0, 2, BFRAME_BIDIR); - buf[i] = framelen[3] & 0xff; - buf[i + 1] = framelen[3] >> 8; - i += 1632 + 16; - mpeg1_sequence_header(go, buf + i, 0); - i += 40; - for (i = 0; i < 5120; i += chunk * 2) { - if (space - off < 32) { - off = -1; - goto done; - } - - code[off + 1] = __cpu_to_le16(0x8000 | mem); - - chunk = 28; - if (mem + chunk > 0x4000) - chunk = 0x4000 - mem; - if (i + 2 * chunk > 5120) - chunk = (5120 - i) / 2; - - if (chunk < 28) { - code[off] = __cpu_to_le16(0x4000 | chunk); - code[off + 31] = __cpu_to_le16(addr); - if (mem + chunk == 0x4000) { - mem = 0x3e00; - ++addr; - } - } else { - code[off] = __cpu_to_le16(0x1000 | 28); - code[off + 31] = 0; - mem += 28; - } - - memcpy(&code[off + 2], buf + i, chunk * 2); - off += 32; - } -done: - kfree(buf); - return off; -} - -static int vti_bitlen(struct go7007 *go) -{ - unsigned int i, max_time_incr = go->sensor_framerate / go->fps_scale; - - for (i = 31; (max_time_incr & ((1 << i) - 1)) == max_time_incr; --i) - ; - return i + 1; -} - -static int mpeg4_frame_header(struct go7007 *go, unsigned char *buf, - int modulo, enum mpeg_frame_type frame) -{ - int i; - CODE_GEN(c, buf + 6); - int mb_count = (go->width >> 4) * (go->height >> 4); - - CODE_ADD(c, frame == PFRAME ? 0x1 : 0x2, 2); - if (modulo) - CODE_ADD(c, 0x1, 1); - CODE_ADD(c, 0x1, 2); - CODE_ADD(c, 0, vti_bitlen(go)); - CODE_ADD(c, 0x3, 2); - if (frame == PFRAME) - CODE_ADD(c, 0, 1); - CODE_ADD(c, 0xc, 11); - if (frame != PFRAME) - CODE_ADD(c, 0x4, 3); - if (frame != BFRAME_EMPTY) { - for (i = 0; i < mb_count; ++i) { - switch (frame) { - case PFRAME: - CODE_ADD(c, 0x1, 1); - break; - case BFRAME_PRE: - CODE_ADD(c, 0x47, 8); - break; - case BFRAME_POST: - CODE_ADD(c, 0x27, 7); - break; - case BFRAME_BIDIR: - CODE_ADD(c, 0x5f, 8); - break; - case BFRAME_EMPTY: /* keep compiler quiet */ - break; - } - } - } - - /* Byte-align with a zero followed by ones */ - i = 8 - (CODE_LENGTH(c) % 8); - CODE_ADD(c, 0, 1); - CODE_ADD(c, (1 << (i - 1)) - 1, i - 1); - - i = CODE_LENGTH(c) + 4 * 8; - buf[0] = i & 0xff; - buf[1] = i >> 8; - buf[2] = 0x00; - buf[3] = 0x00; - buf[4] = 0x01; - buf[5] = 0xb6; - return i; -} - -static int mpeg4_sequence_header(struct go7007 *go, unsigned char *buf, int ext) -{ - const unsigned char head[] = { 0x00, 0x00, 0x01, 0xb0, go->pali, - 0x00, 0x00, 0x01, 0xb5, 0x09, - 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x01, 0x20, }; - int i, aspect_ratio; - int fps = go->sensor_framerate / go->fps_scale; - CODE_GEN(c, buf + 2 + sizeof(head)); - - switch (go->aspect_ratio) { - case GO7007_RATIO_4_3: - aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2; - break; - case GO7007_RATIO_16_9: - aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4; - break; - default: - aspect_ratio = 1; - break; - } - - memcpy(buf + 2, head, sizeof(head)); - CODE_ADD(c, 0x191, 17); - CODE_ADD(c, aspect_ratio, 4); - CODE_ADD(c, 0x1, 4); - CODE_ADD(c, fps, 16); - CODE_ADD(c, 0x3, 2); - CODE_ADD(c, 1001, vti_bitlen(go)); - CODE_ADD(c, 1, 1); - CODE_ADD(c, go->width, 13); - CODE_ADD(c, 1, 1); - CODE_ADD(c, go->height, 13); - CODE_ADD(c, 0x2830, 14); - - /* Byte-align */ - i = 8 - (CODE_LENGTH(c) % 8); - CODE_ADD(c, 0, 1); - CODE_ADD(c, (1 << (i - 1)) - 1, i - 1); - - i = CODE_LENGTH(c) + sizeof(head) * 8; - buf[0] = i & 0xff; - buf[1] = i >> 8; - return i; -} - -static int gen_mpeg4hdr_to_package(struct go7007 *go, - __le16 *code, int space, int *framelen) -{ - u8 *buf; - u16 mem = 0x3e00; - unsigned int addr = 0x19; - int i, off = 0, chunk; - - buf = kzalloc(5120, GFP_KERNEL); - if (buf == NULL) - return -1; - - framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME); - i = 368; - framelen[1] = mpeg4_frame_header(go, buf + i, 0, BFRAME_PRE); - i += 1632; - framelen[2] = mpeg4_frame_header(go, buf + i, 0, BFRAME_POST); - i += 1432; - framelen[3] = mpeg4_frame_header(go, buf + i, 0, BFRAME_BIDIR); - i += 1632; - mpeg4_frame_header(go, buf + i, 0, BFRAME_EMPTY); - i += 16; - mpeg4_sequence_header(go, buf + i, 0); - i += 40; - for (i = 0; i < 5120; i += chunk * 2) { - if (space - off < 32) { - off = -1; - goto done; - } - - code[off + 1] = __cpu_to_le16(0x8000 | mem); - - chunk = 28; - if (mem + chunk > 0x4000) - chunk = 0x4000 - mem; - if (i + 2 * chunk > 5120) - chunk = (5120 - i) / 2; - - if (chunk < 28) { - code[off] = __cpu_to_le16(0x4000 | chunk); - code[off + 31] = __cpu_to_le16(addr); - if (mem + chunk == 0x4000) { - mem = 0x3e00; - ++addr; - } - } else { - code[off] = __cpu_to_le16(0x1000 | 28); - code[off + 31] = 0; - mem += 28; - } - - memcpy(&code[off + 2], buf + i, chunk * 2); - off += 32; - } - mem = 0x3e00; - addr = go->ipb ? 0x14f9 : 0x0af9; - memset(buf, 0, 5120); - framelen[4] = mpeg4_frame_header(go, buf, 1, PFRAME); - i = 368; - framelen[5] = mpeg4_frame_header(go, buf + i, 1, BFRAME_PRE); - i += 1632; - framelen[6] = mpeg4_frame_header(go, buf + i, 1, BFRAME_POST); - i += 1432; - framelen[7] = mpeg4_frame_header(go, buf + i, 1, BFRAME_BIDIR); - i += 1632; - mpeg4_frame_header(go, buf + i, 1, BFRAME_EMPTY); - i += 16; - for (i = 0; i < 5120; i += chunk * 2) { - if (space - off < 32) { - off = -1; - goto done; - } - - code[off + 1] = __cpu_to_le16(0x8000 | mem); - - chunk = 28; - if (mem + chunk > 0x4000) - chunk = 0x4000 - mem; - if (i + 2 * chunk > 5120) - chunk = (5120 - i) / 2; - - if (chunk < 28) { - code[off] = __cpu_to_le16(0x4000 | chunk); - code[off + 31] = __cpu_to_le16(addr); - if (mem + chunk == 0x4000) { - mem = 0x3e00; - ++addr; - } - } else { - code[off] = __cpu_to_le16(0x1000 | 28); - code[off + 31] = 0; - mem += 28; - } - - memcpy(&code[off + 2], buf + i, chunk * 2); - off += 32; - } -done: - kfree(buf); - return off; -} - -static int brctrl_to_package(struct go7007 *go, - __le16 *code, int space, int *framelen) -{ - int converge_speed = 0; - int lambda = (go->format == V4L2_PIX_FMT_MJPEG || go->dvd_mode) ? - 100 : 0; - int peak_rate = 6 * go->bitrate / 5; - int vbv_buffer = go->format == V4L2_PIX_FMT_MJPEG ? - go->bitrate : - (go->dvd_mode ? 900000 : peak_rate); - int fps = go->sensor_framerate / go->fps_scale; - int q = 0; - /* Bizarre math below depends on rounding errors in division */ - u32 sgop_expt_addr = go->bitrate / 32 * (go->ipb ? 3 : 1) * 1001 / fps; - u32 sgop_peak_addr = peak_rate / 32 * 1001 / fps; - u32 total_expt_addr = go->bitrate / 32 * 1000 / fps * (fps / 1000); - u32 vbv_alert_addr = vbv_buffer * 3 / (4 * 32); - u32 cplx[] = { - q > 0 ? sgop_expt_addr * q : - 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, - q > 0 ? sgop_expt_addr * q : - 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, - q > 0 ? sgop_expt_addr * q : - 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, - q > 0 ? sgop_expt_addr * q : - 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, - }; - u32 calc_q = q > 0 ? q : cplx[0] / sgop_expt_addr; - u16 pack[] = { - 0x200e, 0x0000, - 0xBF20, go->ipb ? converge_speed_ipb[converge_speed] - : converge_speed_ip[converge_speed], - 0xBF21, go->ipb ? 2 : 0, - 0xBF22, go->ipb ? LAMBDA_table[0][lambda / 2 + 50] - : 32767, - 0xBF23, go->ipb ? LAMBDA_table[1][lambda] : 32767, - 0xBF24, 32767, - 0xBF25, lambda > 99 ? 32767 : LAMBDA_table[3][lambda], - 0xBF26, sgop_expt_addr & 0x0000FFFF, - 0xBF27, sgop_expt_addr >> 16, - 0xBF28, sgop_peak_addr & 0x0000FFFF, - 0xBF29, sgop_peak_addr >> 16, - 0xBF2A, vbv_alert_addr & 0x0000FFFF, - 0xBF2B, vbv_alert_addr >> 16, - 0xBF2C, 0, - 0xBF2D, 0, - 0, 0, - - 0x200e, 0x0000, - 0xBF2E, vbv_alert_addr & 0x0000FFFF, - 0xBF2F, vbv_alert_addr >> 16, - 0xBF30, cplx[0] & 0x0000FFFF, - 0xBF31, cplx[0] >> 16, - 0xBF32, cplx[1] & 0x0000FFFF, - 0xBF33, cplx[1] >> 16, - 0xBF34, cplx[2] & 0x0000FFFF, - 0xBF35, cplx[2] >> 16, - 0xBF36, cplx[3] & 0x0000FFFF, - 0xBF37, cplx[3] >> 16, - 0xBF38, 0, - 0xBF39, 0, - 0xBF3A, total_expt_addr & 0x0000FFFF, - 0xBF3B, total_expt_addr >> 16, - 0, 0, - - 0x200e, 0x0000, - 0xBF3C, total_expt_addr & 0x0000FFFF, - 0xBF3D, total_expt_addr >> 16, - 0xBF3E, 0, - 0xBF3F, 0, - 0xBF48, 0, - 0xBF49, 0, - 0xBF4A, calc_q < 4 ? 4 : (calc_q > 124 ? 124 : calc_q), - 0xBF4B, 4, - 0xBF4C, 0, - 0xBF4D, 0, - 0xBF4E, 0, - 0xBF4F, 0, - 0xBF50, 0, - 0xBF51, 0, - 0, 0, - - 0x200e, 0x0000, - 0xBF40, sgop_expt_addr & 0x0000FFFF, - 0xBF41, sgop_expt_addr >> 16, - 0xBF42, 0, - 0xBF43, 0, - 0xBF44, 0, - 0xBF45, 0, - 0xBF46, (go->width >> 4) * (go->height >> 4), - 0xBF47, 0, - 0xBF64, 0, - 0xBF65, 0, - 0xBF18, framelen[4], - 0xBF19, framelen[5], - 0xBF1A, framelen[6], - 0xBF1B, framelen[7], - 0, 0, - -#if 0 - /* Remove once we don't care about matching */ - 0x200e, 0x0000, - 0xBF56, 4, - 0xBF57, 0, - 0xBF58, 5, - 0xBF59, 0, - 0xBF5A, 6, - 0xBF5B, 0, - 0xBF5C, 8, - 0xBF5D, 0, - 0xBF5E, 1, - 0xBF5F, 0, - 0xBF60, 1, - 0xBF61, 0, - 0xBF62, 0, - 0xBF63, 0, - 0, 0, -#else - 0x2008, 0x0000, - 0xBF56, 4, - 0xBF57, 0, - 0xBF58, 5, - 0xBF59, 0, - 0xBF5A, 6, - 0xBF5B, 0, - 0xBF5C, 8, - 0xBF5D, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, -#endif - - 0x200e, 0x0000, - 0xBF10, 0, - 0xBF11, 0, - 0xBF12, 0, - 0xBF13, 0, - 0xBF14, 0, - 0xBF15, 0, - 0xBF16, 0, - 0xBF17, 0, - 0xBF7E, 0, - 0xBF7F, 1, - 0xBF52, framelen[0], - 0xBF53, framelen[1], - 0xBF54, framelen[2], - 0xBF55, framelen[3], - 0, 0, - }; - - return copy_packages(code, pack, 6, space); -} - -static int config_package(struct go7007 *go, __le16 *code, int space) -{ - int fps = go->sensor_framerate / go->fps_scale / 1000; - int rows = go->interlace_coding ? go->height / 32 : go->height / 16; - int brc_window_size = fps; - int q_min = 2, q_max = 31; - int THACCoeffSet0 = 0; - u16 pack[] = { - 0x200e, 0x0000, - 0xc002, 0x14b4, - 0xc003, 0x28b4, - 0xc004, 0x3c5a, - 0xdc05, 0x2a77, - 0xc6c3, go->format == V4L2_PIX_FMT_MPEG4 ? 0 : - (go->format == V4L2_PIX_FMT_H263 ? 0 : 1), - 0xc680, go->format == V4L2_PIX_FMT_MPEG4 ? 0xf1 : - (go->format == V4L2_PIX_FMT_H263 ? 0x61 : - 0xd3), - 0xc780, 0x0140, - 0xe009, 0x0001, - 0xc60f, 0x0008, - 0xd4ff, 0x0002, - 0xe403, 2340, - 0xe406, 75, - 0xd411, 0x0001, - 0xd410, 0xa1d6, - 0x0001, 0x2801, - - 0x200d, 0x0000, - 0xe402, 0x018b, - 0xe401, 0x8b01, - 0xd472, (go->board_info->sensor_flags & - GO7007_SENSOR_TV) && - (!go->interlace_coding) ? - 0x01b0 : 0x0170, - 0xd475, (go->board_info->sensor_flags & - GO7007_SENSOR_TV) && - (!go->interlace_coding) ? - 0x0008 : 0x0009, - 0xc404, go->interlace_coding ? 0x44 : - (go->format == V4L2_PIX_FMT_MPEG4 ? 0x11 : - (go->format == V4L2_PIX_FMT_MPEG1 ? 0x02 : - (go->format == V4L2_PIX_FMT_MPEG2 ? 0x04 : - (go->format == V4L2_PIX_FMT_H263 ? 0x08 : - 0x20)))), - 0xbf0a, (go->format == V4L2_PIX_FMT_MPEG4 ? 8 : - (go->format == V4L2_PIX_FMT_MPEG1 ? 1 : - (go->format == V4L2_PIX_FMT_MPEG2 ? 2 : - (go->format == V4L2_PIX_FMT_H263 ? 4 : 16)))) | - ((go->repeat_seqhead ? 1 : 0) << 6) | - ((go->dvd_mode ? 1 : 0) << 9) | - ((go->gop_header_enable ? 1 : 0) << 10), - 0xbf0b, 0, - 0xdd5a, go->ipb ? 0x14 : 0x0a, - 0xbf0c, 0, - 0xbf0d, 0, - 0xc683, THACCoeffSet0, - 0xc40a, (go->width << 4) | rows, - 0xe01a, go->board_info->hpi_buffer_cap, - 0, 0, - 0, 0, - - 0x2008, 0, - 0xe402, 0x88, - 0xe401, 0x8f01, - 0xbf6a, 0, - 0xbf6b, 0, - 0xbf6c, 0, - 0xbf6d, 0, - 0xbf6e, 0, - 0xbf6f, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - - 0x200e, 0, - 0xbf66, brc_window_size, - 0xbf67, 0, - 0xbf68, q_min, - 0xbf69, q_max, - 0xbfe0, 0, - 0xbfe1, 0, - 0xbfe2, 0, - 0xbfe3, go->ipb ? 3 : 1, - 0xc031, go->board_info->sensor_flags & - GO7007_SENSOR_VBI ? 1 : 0, - 0xc01c, 0x1f, - 0xdd8c, 0x15, - 0xdd94, 0x15, - 0xdd88, go->ipb ? 0x1401 : 0x0a01, - 0xdd90, go->ipb ? 0x1401 : 0x0a01, - 0, 0, - - 0x200e, 0, - 0xbfe4, 0, - 0xbfe5, 0, - 0xbfe6, 0, - 0xbfe7, fps << 8, - 0xbfe8, 0x3a00, - 0xbfe9, 0, - 0xbfea, 0, - 0xbfeb, 0, - 0xbfec, (go->interlace_coding ? 1 << 15 : 0) | - (go->modet_enable ? 0xa : 0) | - (go->board_info->sensor_flags & - GO7007_SENSOR_VBI ? 1 : 0), - 0xbfed, 0, - 0xbfee, 0, - 0xbfef, 0, - 0xbff0, go->board_info->sensor_flags & - GO7007_SENSOR_TV ? 0xf060 : 0xb060, - 0xbff1, 0, - 0, 0, - }; - - return copy_packages(code, pack, 5, space); -} - -static int seqhead_to_package(struct go7007 *go, __le16 *code, int space, - int (*sequence_header_func)(struct go7007 *go, - unsigned char *buf, int ext)) -{ - int vop_time_increment_bitlength = vti_bitlen(go); - int fps = go->sensor_framerate / go->fps_scale * - (go->interlace_coding ? 2 : 1); - unsigned char buf[40] = { }; - int len = sequence_header_func(go, buf, 1); - u16 pack[] = { - 0x2006, 0, - 0xbf08, fps, - 0xbf09, 0, - 0xbff2, vop_time_increment_bitlength, - 0xbff3, (1 << vop_time_increment_bitlength) - 1, - 0xbfe6, 0, - 0xbfe7, (fps / 1000) << 8, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - - 0x2007, 0, - 0xc800, buf[2] << 8 | buf[3], - 0xc801, buf[4] << 8 | buf[5], - 0xc802, buf[6] << 8 | buf[7], - 0xc803, buf[8] << 8 | buf[9], - 0xc406, 64, - 0xc407, len - 64, - 0xc61b, 1, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - - 0x200e, 0, - 0xc808, buf[10] << 8 | buf[11], - 0xc809, buf[12] << 8 | buf[13], - 0xc80a, buf[14] << 8 | buf[15], - 0xc80b, buf[16] << 8 | buf[17], - 0xc80c, buf[18] << 8 | buf[19], - 0xc80d, buf[20] << 8 | buf[21], - 0xc80e, buf[22] << 8 | buf[23], - 0xc80f, buf[24] << 8 | buf[25], - 0xc810, buf[26] << 8 | buf[27], - 0xc811, buf[28] << 8 | buf[29], - 0xc812, buf[30] << 8 | buf[31], - 0xc813, buf[32] << 8 | buf[33], - 0xc814, buf[34] << 8 | buf[35], - 0xc815, buf[36] << 8 | buf[37], - 0, 0, - 0, 0, - 0, 0, - }; - - return copy_packages(code, pack, 3, space); -} - -static int relative_prime(int big, int little) -{ - int remainder; - - while (little != 0) { - remainder = big % little; - big = little; - little = remainder; - } - return big; -} - -static int avsync_to_package(struct go7007 *go, __le16 *code, int space) -{ - int arate = go->board_info->audio_rate * 1001 * go->fps_scale; - int ratio = arate / go->sensor_framerate; - int adjratio = ratio * 215 / 100; - int rprime = relative_prime(go->sensor_framerate, - arate % go->sensor_framerate); - int f1 = (arate % go->sensor_framerate) / rprime; - int f2 = (go->sensor_framerate - arate % go->sensor_framerate) / rprime; - u16 pack[] = { - 0x200e, 0, - 0xbf98, (u16)((-adjratio) & 0xffff), - 0xbf99, (u16)((-adjratio) >> 16), - 0xbf92, 0, - 0xbf93, 0, - 0xbff4, f1 > f2 ? f1 : f2, - 0xbff5, f1 < f2 ? f1 : f2, - 0xbff6, f1 < f2 ? ratio : ratio + 1, - 0xbff7, f1 > f2 ? ratio : ratio + 1, - 0xbff8, 0, - 0xbff9, 0, - 0xbffa, adjratio & 0xffff, - 0xbffb, adjratio >> 16, - 0xbf94, 0, - 0xbf95, 0, - 0, 0, - }; - - return copy_packages(code, pack, 1, space); -} - -static int final_package(struct go7007 *go, __le16 *code, int space) -{ - int rows = go->interlace_coding ? go->height / 32 : go->height / 16; - u16 pack[] = { - 0x8000, - 0, - 0, - 0, - 0, - 0, - 0, - 2, - ((go->board_info->sensor_flags & GO7007_SENSOR_TV) && - (!go->interlace_coding) ? - (1 << 14) | (1 << 9) : 0) | - ((go->encoder_subsample ? 1 : 0) << 8) | - (go->board_info->sensor_flags & - GO7007_SENSOR_CONFIG_MASK), - ((go->encoder_v_halve ? 1 : 0) << 14) | - (go->encoder_v_halve ? rows << 9 : rows << 8) | - (go->encoder_h_halve ? 1 << 6 : 0) | - (go->encoder_h_halve ? go->width >> 3 : go->width >> 4), - (1 << 15) | (go->encoder_v_offset << 6) | - (1 << 7) | (go->encoder_h_offset >> 2), - (1 << 6), - 0, - 0, - ((go->fps_scale - 1) << 8) | - (go->board_info->sensor_flags & GO7007_SENSOR_TV ? - (1 << 7) : 0) | - 0x41, - go->ipb ? 0xd4c : 0x36b, - (rows << 8) | (go->width >> 4), - go->format == V4L2_PIX_FMT_MPEG4 ? 0x0404 : 0, - (1 << 15) | ((go->interlace_coding ? 1 : 0) << 13) | - ((go->closed_gop ? 1 : 0) << 12) | - ((go->format == V4L2_PIX_FMT_MPEG4 ? 1 : 0) << 11) | - /* (1 << 9) | */ - ((go->ipb ? 3 : 0) << 7) | - ((go->modet_enable ? 1 : 0) << 2) | - ((go->dvd_mode ? 1 : 0) << 1) | 1, - (go->format == V4L2_PIX_FMT_MPEG1 ? 0x89a0 : - (go->format == V4L2_PIX_FMT_MPEG2 ? 0x89a0 : - (go->format == V4L2_PIX_FMT_MJPEG ? 0x89a0 : - (go->format == V4L2_PIX_FMT_MPEG4 ? 0x8920 : - (go->format == V4L2_PIX_FMT_H263 ? 0x8920 : 0))))), - go->ipb ? 0x1f15 : 0x1f0b, - go->ipb ? 0x0015 : 0x000b, - go->ipb ? 0xa800 : 0x5800, - 0xffff, - 0x0020 + 0x034b * 0, - 0x0020 + 0x034b * 1, - 0x0020 + 0x034b * 2, - 0x0020 + 0x034b * 3, - 0x0020 + 0x034b * 4, - 0x0020 + 0x034b * 5, - go->ipb ? (go->gop_size / 3) : go->gop_size, - (go->height >> 4) * (go->width >> 4) * 110 / 100, - }; - - return copy_packages(code, pack, 1, space); -} - -static int audio_to_package(struct go7007 *go, __le16 *code, int space) -{ - int clock_config = ((go->board_info->audio_flags & - GO7007_AUDIO_I2S_MASTER ? 1 : 0) << 11) | - ((go->board_info->audio_flags & - GO7007_AUDIO_OKI_MODE ? 1 : 0) << 8) | - (((go->board_info->audio_bclk_div / 4) - 1) << 4) | - (go->board_info->audio_main_div - 1); - u16 pack[] = { - 0x200d, 0, - 0x9002, 0, - 0x9002, 0, - 0x9031, 0, - 0x9032, 0, - 0x9033, 0, - 0x9034, 0, - 0x9035, 0, - 0x9036, 0, - 0x9037, 0, - 0x9040, 0, - 0x9000, clock_config, - 0x9001, (go->board_info->audio_flags & 0xffff) | - (1 << 9), - 0x9000, ((go->board_info->audio_flags & - GO7007_AUDIO_I2S_MASTER ? - 1 : 0) << 10) | - clock_config, - 0, 0, - 0, 0, - 0x2005, 0, - 0x9041, 0, - 0x9042, 256, - 0x9043, 0, - 0x9044, 16, - 0x9045, 16, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - }; - - return copy_packages(code, pack, 2, space); -} - -static int modet_to_package(struct go7007 *go, __le16 *code, int space) -{ - bool has_modet0 = go->modet[0].enable; - bool has_modet1 = go->modet[1].enable; - bool has_modet2 = go->modet[2].enable; - bool has_modet3 = go->modet[3].enable; - int ret, mb, i, addr, cnt = 0; - u16 pack[32]; - u16 thresholds[] = { - 0x200e, 0, - 0xbf82, has_modet0 ? go->modet[0].pixel_threshold : 32767, - 0xbf83, has_modet1 ? go->modet[1].pixel_threshold : 32767, - 0xbf84, has_modet2 ? go->modet[2].pixel_threshold : 32767, - 0xbf85, has_modet3 ? go->modet[3].pixel_threshold : 32767, - 0xbf86, has_modet0 ? go->modet[0].motion_threshold : 32767, - 0xbf87, has_modet1 ? go->modet[1].motion_threshold : 32767, - 0xbf88, has_modet2 ? go->modet[2].motion_threshold : 32767, - 0xbf89, has_modet3 ? go->modet[3].motion_threshold : 32767, - 0xbf8a, has_modet0 ? go->modet[0].mb_threshold : 32767, - 0xbf8b, has_modet1 ? go->modet[1].mb_threshold : 32767, - 0xbf8c, has_modet2 ? go->modet[2].mb_threshold : 32767, - 0xbf8d, has_modet3 ? go->modet[3].mb_threshold : 32767, - 0xbf8e, 0, - 0xbf8f, 0, - 0, 0, - }; - - ret = copy_packages(code, thresholds, 1, space); - if (ret < 0) - return -1; - cnt += ret; - - addr = 0xbac0; - memset(pack, 0, 64); - i = 0; - for (mb = 0; mb < 1624; ++mb) { - pack[i * 2 + 3] <<= 2; - pack[i * 2 + 3] |= go->modet_map[mb]; - if (mb % 8 != 7) - continue; - pack[i * 2 + 2] = addr++; - ++i; - if (i == 10 || mb == 1623) { - pack[0] = 0x2000 | i; - ret = copy_packages(code + cnt, pack, 1, space - cnt); - if (ret < 0) - return -1; - cnt += ret; - i = 0; - memset(pack, 0, 64); - } - pack[i * 2 + 3] = 0; - } - - memset(pack, 0, 64); - i = 0; - for (addr = 0xbb90; addr < 0xbbfa; ++addr) { - pack[i * 2 + 2] = addr; - pack[i * 2 + 3] = 0; - ++i; - if (i == 10 || addr == 0xbbf9) { - pack[0] = 0x2000 | i; - ret = copy_packages(code + cnt, pack, 1, space - cnt); - if (ret < 0) - return -1; - cnt += ret; - i = 0; - memset(pack, 0, 64); - } - } - return cnt; -} - -static int do_special(struct go7007 *go, u16 type, __le16 *code, int space, - int *framelen) -{ - switch (type) { - case SPECIAL_FRM_HEAD: - switch (go->format) { - case V4L2_PIX_FMT_MJPEG: - return gen_mjpeghdr_to_package(go, code, space); - case V4L2_PIX_FMT_MPEG1: - case V4L2_PIX_FMT_MPEG2: - return gen_mpeg1hdr_to_package(go, code, space, - framelen); - case V4L2_PIX_FMT_MPEG4: - return gen_mpeg4hdr_to_package(go, code, space, - framelen); - } - case SPECIAL_BRC_CTRL: - return brctrl_to_package(go, code, space, framelen); - case SPECIAL_CONFIG: - return config_package(go, code, space); - case SPECIAL_SEQHEAD: - switch (go->format) { - case V4L2_PIX_FMT_MPEG1: - case V4L2_PIX_FMT_MPEG2: - return seqhead_to_package(go, code, space, - mpeg1_sequence_header); - case V4L2_PIX_FMT_MPEG4: - return seqhead_to_package(go, code, space, - mpeg4_sequence_header); - default: - return 0; - } - case SPECIAL_AV_SYNC: - return avsync_to_package(go, code, space); - case SPECIAL_FINAL: - return final_package(go, code, space); - case SPECIAL_AUDIO: - return audio_to_package(go, code, space); - case SPECIAL_MODET: - return modet_to_package(go, code, space); - } - dev_err(go->dev, - "firmware file contains unsupported feature %04x\n", type); - return -1; -} - -int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen) -{ - const struct firmware *fw_entry; - __le16 *code, *src; - int framelen[8] = { }; /* holds the lengths of empty frame templates */ - int codespace = 64 * 1024, i = 0, srclen, chunk_len, chunk_flags; - int mode_flag; - int ret; - - switch (go->format) { - case V4L2_PIX_FMT_MJPEG: - mode_flag = FLAG_MODE_MJPEG; - break; - case V4L2_PIX_FMT_MPEG1: - mode_flag = FLAG_MODE_MPEG1; - break; - case V4L2_PIX_FMT_MPEG2: - mode_flag = FLAG_MODE_MPEG2; - break; - case V4L2_PIX_FMT_MPEG4: - mode_flag = FLAG_MODE_MPEG4; - break; - default: - return -1; - } - if (request_firmware(&fw_entry, GO7007_FW_NAME, go->dev)) { - dev_err(go->dev, - "unable to load firmware from file \"%s\"\n", - GO7007_FW_NAME); - return -1; - } - code = kzalloc(codespace * 2, GFP_KERNEL); - if (code == NULL) - goto fw_failed; - - src = (__le16 *)fw_entry->data; - srclen = fw_entry->size / 2; - while (srclen >= 2) { - chunk_flags = __le16_to_cpu(src[0]); - chunk_len = __le16_to_cpu(src[1]); - if (chunk_len + 2 > srclen) { - dev_err(go->dev, - "firmware file \"%s\" appears to be corrupted\n", - GO7007_FW_NAME); - goto fw_failed; - } - if (chunk_flags & mode_flag) { - if (chunk_flags & FLAG_SPECIAL) { - ret = do_special(go, __le16_to_cpu(src[2]), - &code[i], codespace - i, framelen); - if (ret < 0) { - dev_err(go->dev, - "insufficient memory for firmware construction\n"); - goto fw_failed; - } - i += ret; - } else { - if (codespace - i < chunk_len) { - dev_err(go->dev, - "insufficient memory for firmware construction\n"); - goto fw_failed; - } - memcpy(&code[i], &src[2], chunk_len * 2); - i += chunk_len; - } - } - srclen -= chunk_len + 2; - src += chunk_len + 2; - } - release_firmware(fw_entry); - *fw = (u8 *)code; - *fwlen = i * 2; - return 0; - -fw_failed: - kfree(code); - release_firmware(fw_entry); - return -1; -} - -MODULE_FIRMWARE(GO7007_FW_NAME); diff --git a/drivers/staging/media/go7007/go7007-i2c.c b/drivers/staging/media/go7007/go7007-i2c.c deleted file mode 100644 index 55addfa..0000000 --- a/drivers/staging/media/go7007/go7007-i2c.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007-priv.h" - -/********************* Driver for on-board I2C adapter *********************/ - -/* #define GO7007_I2C_DEBUG */ - -#define SPI_I2C_ADDR_BASE 0x1400 -#define STATUS_REG_ADDR (SPI_I2C_ADDR_BASE + 0x2) -#define I2C_CTRL_REG_ADDR (SPI_I2C_ADDR_BASE + 0x6) -#define I2C_DEV_UP_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x7) -#define I2C_LO_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x8) -#define I2C_DATA_REG_ADDR (SPI_I2C_ADDR_BASE + 0x9) -#define I2C_CLKFREQ_REG_ADDR (SPI_I2C_ADDR_BASE + 0xa) - -#define I2C_STATE_MASK 0x0007 -#define I2C_READ_READY_MASK 0x0008 - -/* There is only one I2C port on the TW2804 that feeds all four GO7007 VIPs - * on the Adlink PCI-MPG24, so access is shared between all of them. */ -static DEFINE_MUTEX(adlink_mpg24_i2c_lock); - -static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, - u16 command, int flags, u8 *data) -{ - int i, ret = -EIO; - u16 val; - - if (go->status == STATUS_SHUTDOWN) - return -ENODEV; - -#ifdef GO7007_I2C_DEBUG - if (read) - dev_dbg(go->dev, "go7007-i2c: reading 0x%02x on 0x%02x\n", - command, addr); - else - dev_dbg(go->dev, - "go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n", - *data, command, addr); -#endif - - mutex_lock(&go->hw_lock); - - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { - /* Bridge the I2C port on this GO7007 to the shared bus */ - mutex_lock(&adlink_mpg24_i2c_lock); - go7007_write_addr(go, 0x3c82, 0x0020); - } - - /* Wait for I2C adapter to be ready */ - for (i = 0; i < 10; ++i) { - if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0) - goto i2c_done; - if (!(val & I2C_STATE_MASK)) - break; - msleep(100); - } - if (i == 10) { - dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); - goto i2c_done; - } - - /* Set target register (command) */ - go7007_write_addr(go, I2C_CTRL_REG_ADDR, flags); - go7007_write_addr(go, I2C_LO_ADDR_REG_ADDR, command); - - /* If we're writing, send the data and target address and we're done */ - if (!read) { - go7007_write_addr(go, I2C_DATA_REG_ADDR, *data); - go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR, - (addr << 9) | (command >> 8)); - ret = 0; - goto i2c_done; - } - - /* Otherwise, we're reading. First clear i2c_rx_data_rdy. */ - if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0) - goto i2c_done; - - /* Send the target address plus read flag */ - go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR, - (addr << 9) | 0x0100 | (command >> 8)); - - /* Wait for i2c_rx_data_rdy */ - for (i = 0; i < 10; ++i) { - if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0) - goto i2c_done; - if (val & I2C_READ_READY_MASK) - break; - msleep(100); - } - if (i == 10) { - dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); - goto i2c_done; - } - - /* Retrieve the read byte */ - if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0) - goto i2c_done; - *data = val; - ret = 0; - -i2c_done: - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { - /* Isolate the I2C port on this GO7007 from the shared bus */ - go7007_write_addr(go, 0x3c82, 0x0000); - mutex_unlock(&adlink_mpg24_i2c_lock); - } - mutex_unlock(&go->hw_lock); - return ret; -} - -static int go7007_smbus_xfer(struct i2c_adapter *adapter, u16 addr, - unsigned short flags, char read_write, - u8 command, int size, union i2c_smbus_data *data) -{ - struct go7007 *go = i2c_get_adapdata(adapter); - - if (size != I2C_SMBUS_BYTE_DATA) - return -EIO; - return go7007_i2c_xfer(go, addr, read_write == I2C_SMBUS_READ, command, - flags & I2C_CLIENT_SCCB ? 0x10 : 0x00, &data->byte); -} - -/* VERY LIMITED I2C master xfer function -- only needed because the - * SMBus functions only support 8-bit commands and the SAA7135 uses - * 16-bit commands. The I2C interface on the GO7007, as limited as - * it is, does support this mode. */ - -static int go7007_i2c_master_xfer(struct i2c_adapter *adapter, - struct i2c_msg msgs[], int num) -{ - struct go7007 *go = i2c_get_adapdata(adapter); - int i; - - for (i = 0; i < num; ++i) { - /* We can only do two things here -- write three bytes, or - * write two bytes and read one byte. */ - if (msgs[i].len == 2) { - if (i + 1 == num || msgs[i].addr != msgs[i + 1].addr || - (msgs[i].flags & I2C_M_RD) || - !(msgs[i + 1].flags & I2C_M_RD) || - msgs[i + 1].len != 1) - return -EIO; - if (go7007_i2c_xfer(go, msgs[i].addr, 1, - (msgs[i].buf[0] << 8) | msgs[i].buf[1], - 0x01, &msgs[i + 1].buf[0]) < 0) - return -EIO; - ++i; - } else if (msgs[i].len == 3) { - if (msgs[i].flags & I2C_M_RD) - return -EIO; - if (msgs[i].len != 3) - return -EIO; - if (go7007_i2c_xfer(go, msgs[i].addr, 0, - (msgs[i].buf[0] << 8) | msgs[i].buf[1], - 0x01, &msgs[i].buf[2]) < 0) - return -EIO; - } else - return -EIO; - } - - return num; -} - -static u32 go7007_functionality(struct i2c_adapter *adapter) -{ - return I2C_FUNC_SMBUS_BYTE_DATA; -} - -static struct i2c_algorithm go7007_algo = { - .smbus_xfer = go7007_smbus_xfer, - .master_xfer = go7007_i2c_master_xfer, - .functionality = go7007_functionality, -}; - -static struct i2c_adapter go7007_adap_templ = { - .owner = THIS_MODULE, - .name = "WIS GO7007SB", - .algo = &go7007_algo, -}; - -int go7007_i2c_init(struct go7007 *go) -{ - memcpy(&go->i2c_adapter, &go7007_adap_templ, - sizeof(go7007_adap_templ)); - go->i2c_adapter.dev.parent = go->dev; - i2c_set_adapdata(&go->i2c_adapter, go); - if (i2c_add_adapter(&go->i2c_adapter) < 0) { - dev_err(go->dev, - "go7007-i2c: error: i2c_add_adapter failed\n"); - return -1; - } - return 0; -} diff --git a/drivers/staging/media/go7007/go7007-loader.c b/drivers/staging/media/go7007/go7007-loader.c deleted file mode 100644 index 042f78a..0000000 --- a/drivers/staging/media/go7007/go7007-loader.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2008 Sensoray Company Inc. - * - * 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. - */ - -#include -#include -#include -#include -#include - -struct fw_config { - u16 vendor; - u16 product; - const char * const fw_name1; - const char * const fw_name2; -}; - -static struct fw_config fw_configs[] = { - { 0x1943, 0xa250, "go7007/s2250-1.fw", "go7007/s2250-2.fw" }, - { 0x093b, 0xa002, "go7007/px-m402u.fw", NULL }, - { 0x093b, 0xa004, "go7007/px-tv402u.fw", NULL }, - { 0x0eb1, 0x6666, "go7007/lr192.fw", NULL }, - { 0x0eb1, 0x6668, "go7007/wis-startrek.fw", NULL }, - { 0, 0, NULL, NULL } -}; -MODULE_FIRMWARE("go7007/s2250-1.fw"); -MODULE_FIRMWARE("go7007/s2250-2.fw"); -MODULE_FIRMWARE("go7007/px-m402u.fw"); -MODULE_FIRMWARE("go7007/px-tv402u.fw"); -MODULE_FIRMWARE("go7007/lr192.fw"); -MODULE_FIRMWARE("go7007/wis-startrek.fw"); - -static int go7007_loader_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *usbdev; - const struct firmware *fw; - u16 vendor, product; - const char *fw1, *fw2; - int ret; - int i; - - usbdev = usb_get_dev(interface_to_usbdev(interface)); - if (!usbdev) - goto failed2; - - if (usbdev->descriptor.bNumConfigurations != 1) { - dev_err(&interface->dev, "can't handle multiple config\n"); - goto failed2; - } - - vendor = le16_to_cpu(usbdev->descriptor.idVendor); - product = le16_to_cpu(usbdev->descriptor.idProduct); - - for (i = 0; fw_configs[i].fw_name1; i++) - if (fw_configs[i].vendor == vendor && - fw_configs[i].product == product) - break; - - /* Should never happen */ - if (fw_configs[i].fw_name1 == NULL) - goto failed2; - - fw1 = fw_configs[i].fw_name1; - fw2 = fw_configs[i].fw_name2; - - dev_info(&interface->dev, "loading firmware %s\n", fw1); - - if (request_firmware(&fw, fw1, &usbdev->dev)) { - dev_err(&interface->dev, - "unable to load firmware from file \"%s\"\n", fw1); - goto failed2; - } - ret = cypress_load_firmware(usbdev, fw, CYPRESS_FX2); - release_firmware(fw); - if (0 != ret) { - dev_err(&interface->dev, "loader download failed\n"); - goto failed2; - } - - if (fw2 == NULL) - return 0; - - if (request_firmware(&fw, fw2, &usbdev->dev)) { - dev_err(&interface->dev, - "unable to load firmware from file \"%s\"\n", fw2); - goto failed2; - } - ret = cypress_load_firmware(usbdev, fw, CYPRESS_FX2); - release_firmware(fw); - if (0 != ret) { - dev_err(&interface->dev, "firmware download failed\n"); - goto failed2; - } - return 0; - -failed2: - usb_put_dev(usbdev); - dev_err(&interface->dev, "probe failed\n"); - return -ENODEV; -} - -static void go7007_loader_disconnect(struct usb_interface *interface) -{ - dev_info(&interface->dev, "disconnect\n"); - usb_put_dev(interface_to_usbdev(interface)); - usb_set_intfdata(interface, NULL); -} - -static const struct usb_device_id go7007_loader_ids[] = { - { USB_DEVICE(0x1943, 0xa250) }, - { USB_DEVICE(0x093b, 0xa002) }, - { USB_DEVICE(0x093b, 0xa004) }, - { USB_DEVICE(0x0eb1, 0x6666) }, - { USB_DEVICE(0x0eb1, 0x6668) }, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, go7007_loader_ids); - -static struct usb_driver go7007_loader_driver = { - .name = "go7007-loader", - .probe = go7007_loader_probe, - .disconnect = go7007_loader_disconnect, - .id_table = go7007_loader_ids, -}; - -module_usb_driver(go7007_loader_driver); - -MODULE_AUTHOR(""); -MODULE_DESCRIPTION("firmware loader for go7007-usb"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/go7007-priv.h b/drivers/staging/media/go7007/go7007-priv.h deleted file mode 100644 index 2251c3f..0000000 --- a/drivers/staging/media/go7007/go7007-priv.h +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * 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. - */ - -/* - * This is the private include file for the go7007 driver. It should not - * be included by anybody but the driver itself, and especially not by - * user-space applications. - */ - -#include -#include -#include -#include - -struct go7007; - -/* IDs to activate board-specific support code */ -#define GO7007_BOARDID_MATRIX_II 0 -#define GO7007_BOARDID_MATRIX_RELOAD 1 -#define GO7007_BOARDID_STAR_TREK 2 -#define GO7007_BOARDID_PCI_VOYAGER 3 -#define GO7007_BOARDID_XMEN 4 -#define GO7007_BOARDID_XMEN_II 5 -#define GO7007_BOARDID_XMEN_III 6 -#define GO7007_BOARDID_MATRIX_REV 7 -#define GO7007_BOARDID_PX_M402U 8 -#define GO7007_BOARDID_PX_TV402U 9 -#define GO7007_BOARDID_LIFEVIEW_LR192 10 /* TV Walker Ultra */ -#define GO7007_BOARDID_ENDURA 11 -#define GO7007_BOARDID_ADLINK_MPG24 12 -#define GO7007_BOARDID_SENSORAY_2250 13 /* Sensoray 2250/2251 */ -#define GO7007_BOARDID_ADS_USBAV_709 14 - -/* Various characteristics of each board */ -#define GO7007_BOARD_HAS_AUDIO (1<<0) -#define GO7007_BOARD_USE_ONBOARD_I2C (1<<1) -#define GO7007_BOARD_HAS_TUNER (1<<2) - -/* Characteristics of sensor devices */ -#define GO7007_SENSOR_VALID_POLAR (1<<0) -#define GO7007_SENSOR_HREF_POLAR (1<<1) -#define GO7007_SENSOR_VREF_POLAR (1<<2) -#define GO7007_SENSOR_FIELD_ID_POLAR (1<<3) -#define GO7007_SENSOR_BIT_WIDTH (1<<4) -#define GO7007_SENSOR_VALID_ENABLE (1<<5) -#define GO7007_SENSOR_656 (1<<6) -#define GO7007_SENSOR_CONFIG_MASK 0x7f -#define GO7007_SENSOR_TV (1<<7) -#define GO7007_SENSOR_VBI (1<<8) -#define GO7007_SENSOR_SCALING (1<<9) -#define GO7007_SENSOR_SAA7115 (1<<10) - -/* Characteristics of audio sensor devices */ -#define GO7007_AUDIO_I2S_MODE_1 (1) -#define GO7007_AUDIO_I2S_MODE_2 (2) -#define GO7007_AUDIO_I2S_MODE_3 (3) -#define GO7007_AUDIO_BCLK_POLAR (1<<2) -#define GO7007_AUDIO_WORD_14 (14<<4) -#define GO7007_AUDIO_WORD_16 (16<<4) -#define GO7007_AUDIO_ONE_CHANNEL (1<<11) -#define GO7007_AUDIO_I2S_MASTER (1<<16) -#define GO7007_AUDIO_OKI_MODE (1<<17) - -#define GO7007_CID_CUSTOM_BASE (V4L2_CID_DETECT_CLASS_BASE + 0x1000) -#define V4L2_CID_PIXEL_THRESHOLD0 (GO7007_CID_CUSTOM_BASE+1) -#define V4L2_CID_MOTION_THRESHOLD0 (GO7007_CID_CUSTOM_BASE+2) -#define V4L2_CID_MB_THRESHOLD0 (GO7007_CID_CUSTOM_BASE+3) -#define V4L2_CID_PIXEL_THRESHOLD1 (GO7007_CID_CUSTOM_BASE+4) -#define V4L2_CID_MOTION_THRESHOLD1 (GO7007_CID_CUSTOM_BASE+5) -#define V4L2_CID_MB_THRESHOLD1 (GO7007_CID_CUSTOM_BASE+6) -#define V4L2_CID_PIXEL_THRESHOLD2 (GO7007_CID_CUSTOM_BASE+7) -#define V4L2_CID_MOTION_THRESHOLD2 (GO7007_CID_CUSTOM_BASE+8) -#define V4L2_CID_MB_THRESHOLD2 (GO7007_CID_CUSTOM_BASE+9) -#define V4L2_CID_PIXEL_THRESHOLD3 (GO7007_CID_CUSTOM_BASE+10) -#define V4L2_CID_MOTION_THRESHOLD3 (GO7007_CID_CUSTOM_BASE+11) -#define V4L2_CID_MB_THRESHOLD3 (GO7007_CID_CUSTOM_BASE+12) - -struct go7007_board_info { - unsigned int flags; - int hpi_buffer_cap; - unsigned int sensor_flags; - int sensor_width; - int sensor_height; - int sensor_framerate; - int sensor_h_offset; - int sensor_v_offset; - unsigned int audio_flags; - int audio_rate; - int audio_bclk_div; - int audio_main_div; - int num_i2c_devs; - struct go_i2c { - const char *type; - unsigned int is_video:1; - unsigned int is_audio:1; - int addr; - u32 flags; - } i2c_devs[5]; - int num_inputs; - struct { - int video_input; - int audio_index; - char *name; - } inputs[4]; - int video_config; - int num_aud_inputs; - struct { - int audio_input; - char *name; - } aud_inputs[3]; -}; - -struct go7007_hpi_ops { - int (*interface_reset)(struct go7007 *go); - int (*write_interrupt)(struct go7007 *go, int addr, int data); - int (*read_interrupt)(struct go7007 *go); - int (*stream_start)(struct go7007 *go); - int (*stream_stop)(struct go7007 *go); - int (*send_firmware)(struct go7007 *go, u8 *data, int len); - int (*send_command)(struct go7007 *go, unsigned int cmd, void *arg); - void (*release)(struct go7007 *go); -}; - -/* The video buffer size must be a multiple of PAGE_SIZE */ -#define GO7007_BUF_PAGES (128 * 1024 / PAGE_SIZE) -#define GO7007_BUF_SIZE (GO7007_BUF_PAGES << PAGE_SHIFT) - -struct go7007_buffer { - struct vb2_buffer vb; - struct list_head list; - unsigned int frame_offset; - u32 modet_active; -}; - -#define GO7007_RATIO_1_1 0 -#define GO7007_RATIO_4_3 1 -#define GO7007_RATIO_16_9 2 - -enum go7007_parser_state { - STATE_DATA, - STATE_00, - STATE_00_00, - STATE_00_00_01, - STATE_FF, - STATE_VBI_LEN_A, - STATE_VBI_LEN_B, - STATE_MODET_MAP, - STATE_UNPARSED, -}; - -struct go7007 { - struct device *dev; - u8 bus_info[32]; - const struct go7007_board_info *board_info; - unsigned int board_id; - int tuner_type; - int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */ - char name[64]; - struct video_device vdev; - void *boot_fw; - unsigned boot_fw_len; - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler hdl; - struct v4l2_ctrl *mpeg_video_encoding; - struct v4l2_ctrl *mpeg_video_gop_size; - struct v4l2_ctrl *mpeg_video_gop_closure; - struct v4l2_ctrl *mpeg_video_bitrate; - struct v4l2_ctrl *mpeg_video_aspect_ratio; - struct v4l2_ctrl *mpeg_video_b_frames; - struct v4l2_ctrl *mpeg_video_rep_seqheader; - struct v4l2_ctrl *modet_mode; - enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status; - spinlock_t spinlock; - struct mutex hw_lock; - struct mutex serialize_lock; - int audio_enabled; - struct v4l2_subdev *sd_video; - struct v4l2_subdev *sd_audio; - u8 usb_buf[16]; - - /* Video input */ - int input; - int aud_input; - enum { GO7007_STD_NTSC, GO7007_STD_PAL, GO7007_STD_OTHER } standard; - v4l2_std_id std; - int sensor_framerate; - int width; - int height; - int encoder_h_offset; - int encoder_v_offset; - unsigned int encoder_h_halve:1; - unsigned int encoder_v_halve:1; - unsigned int encoder_subsample:1; - - /* Encoder config */ - u32 format; - int bitrate; - int fps_scale; - int pali; - int aspect_ratio; - int gop_size; - unsigned int ipb:1; - unsigned int closed_gop:1; - unsigned int repeat_seqhead:1; - unsigned int seq_header_enable:1; - unsigned int gop_header_enable:1; - unsigned int dvd_mode:1; - unsigned int interlace_coding:1; - - /* Motion detection */ - unsigned int modet_enable:1; - struct { - unsigned int enable:1; - int pixel_threshold; - int motion_threshold; - int mb_threshold; - } modet[4]; - unsigned char modet_map[1624]; - unsigned char active_map[216]; - u32 modet_event_status; - - /* Video streaming */ - struct mutex queue_lock; - struct vb2_queue vidq; - enum go7007_parser_state state; - int parse_length; - u16 modet_word; - int seen_frame; - u32 next_seq; - struct list_head vidq_active; - wait_queue_head_t frame_waitq; - struct go7007_buffer *active_buf; - - /* Audio streaming */ - void (*audio_deliver)(struct go7007 *go, u8 *buf, int length); - void *snd_context; - - /* I2C */ - int i2c_adapter_online; - struct i2c_adapter i2c_adapter; - - /* HPI driver */ - struct go7007_hpi_ops *hpi_ops; - void *hpi_context; - int interrupt_available; - wait_queue_head_t interrupt_waitq; - unsigned short interrupt_value; - unsigned short interrupt_data; -}; - -static inline struct go7007 *to_go7007(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct go7007, v4l2_dev); -} - -/* All of these must be called with the hpi_lock mutex held! */ -#define go7007_interface_reset(go) \ - ((go)->hpi_ops->interface_reset(go)) -#define go7007_write_interrupt(go, x, y) \ - ((go)->hpi_ops->write_interrupt)((go), (x), (y)) -#define go7007_stream_start(go) \ - ((go)->hpi_ops->stream_start(go)) -#define go7007_stream_stop(go) \ - ((go)->hpi_ops->stream_stop(go)) -#define go7007_send_firmware(go, x, y) \ - ((go)->hpi_ops->send_firmware)((go), (x), (y)) -#define go7007_write_addr(go, x, y) \ - ((go)->hpi_ops->write_interrupt)((go), (x)|0x8000, (y)) - -/* go7007-driver.c */ -int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data); -int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data); -int go7007_boot_encoder(struct go7007 *go, int init_i2c); -int go7007_reset_encoder(struct go7007 *go); -int go7007_register_encoder(struct go7007 *go, unsigned num_i2c_devs); -int go7007_start_encoder(struct go7007 *go); -void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length); -struct go7007 *go7007_alloc(const struct go7007_board_info *board, - struct device *dev); -void go7007_update_board(struct go7007 *go); - -/* go7007-fw.c */ -int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen); - -/* go7007-i2c.c */ -int go7007_i2c_init(struct go7007 *go); -int go7007_i2c_remove(struct go7007 *go); - -/* go7007-v4l2.c */ -int go7007_v4l2_init(struct go7007 *go); -int go7007_v4l2_ctrl_init(struct go7007 *go); -void go7007_v4l2_remove(struct go7007 *go); - -/* snd-go7007.c */ -int go7007_snd_init(struct go7007 *go); -int go7007_snd_remove(struct go7007 *go); diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c deleted file mode 100644 index ece27ec..0000000 --- a/drivers/staging/media/go7007/go7007-usb.c +++ /dev/null @@ -1,1345 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * 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. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007-priv.h" - -static unsigned int assume_endura; -module_param(assume_endura, int, 0644); -MODULE_PARM_DESC(assume_endura, - "when probing fails, hardware is a Pelco Endura"); - -/* #define GO7007_I2C_DEBUG */ /* for debugging the EZ-USB I2C adapter */ - -#define HPI_STATUS_ADDR 0xFFF4 -#define INT_PARAM_ADDR 0xFFF6 -#define INT_INDEX_ADDR 0xFFF8 - -/* - * Pipes on EZ-USB interface: - * 0 snd - Control - * 0 rcv - Control - * 2 snd - Download firmware (control) - * 4 rcv - Read Interrupt (interrupt) - * 6 rcv - Read Video (bulk) - * 8 rcv - Read Audio (bulk) - */ - -#define GO7007_USB_EZUSB (1<<0) -#define GO7007_USB_EZUSB_I2C (1<<1) - -struct go7007_usb_board { - unsigned int flags; - struct go7007_board_info main_info; -}; - -struct go7007_usb { - const struct go7007_usb_board *board; - struct mutex i2c_lock; - struct usb_device *usbdev; - struct urb *video_urbs[8]; - struct urb *audio_urbs[8]; - struct urb *intr_urb; -}; - -/*********************** Product specification data ***********************/ - -static const struct go7007_usb_board board_matrix_ii = { - .flags = GO7007_USB_EZUSB, - .main_info = { - .flags = GO7007_BOARD_HAS_AUDIO | - GO7007_BOARD_USE_ONBOARD_I2C, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_VALID_ENABLE | - GO7007_SENSOR_TV | - GO7007_SENSOR_SAA7115 | - GO7007_SENSOR_VBI | - GO7007_SENSOR_SCALING, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "saa7115", - .addr = 0x20, - .is_video = 1, - }, - }, - .num_inputs = 2, - .inputs = { - { - .video_input = 0, - .name = "Composite", - }, - { - .video_input = 9, - .name = "S-Video", - }, - }, - .video_config = SAA7115_IDQ_IS_DEFAULT, - }, -}; - -static const struct go7007_usb_board board_matrix_reload = { - .flags = GO7007_USB_EZUSB, - .main_info = { - .flags = GO7007_BOARD_HAS_AUDIO | - GO7007_BOARD_USE_ONBOARD_I2C, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_TV, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "saa7113", - .addr = 0x25, - .is_video = 1, - }, - }, - .num_inputs = 2, - .inputs = { - { - .video_input = 0, - .name = "Composite", - }, - { - .video_input = 9, - .name = "S-Video", - }, - }, - .video_config = SAA7115_IDQ_IS_DEFAULT, - }, -}; - -static const struct go7007_usb_board board_star_trek = { - .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, - .main_info = { - .flags = GO7007_BOARD_HAS_AUDIO, /* | - GO7007_BOARD_HAS_TUNER, */ - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_VALID_ENABLE | - GO7007_SENSOR_TV | - GO7007_SENSOR_SAA7115 | - GO7007_SENSOR_VBI | - GO7007_SENSOR_SCALING, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_WORD_16, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "saa7115", - .addr = 0x20, - .is_video = 1, - }, - }, - .num_inputs = 2, - .inputs = { - /* { - * .video_input = 3, - * .audio_index = AUDIO_TUNER, - * .name = "Tuner", - * }, - */ - { - .video_input = 1, - /* .audio_index = AUDIO_EXTERN, */ - .name = "Composite", - }, - { - .video_input = 8, - /* .audio_index = AUDIO_EXTERN, */ - .name = "S-Video", - }, - }, - .video_config = SAA7115_IDQ_IS_DEFAULT, - }, -}; - -static const struct go7007_usb_board board_px_tv402u = { - .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, - .main_info = { - .flags = GO7007_BOARD_HAS_AUDIO | - GO7007_BOARD_HAS_TUNER, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_VALID_ENABLE | - GO7007_SENSOR_TV | - GO7007_SENSOR_SAA7115 | - GO7007_SENSOR_VBI | - GO7007_SENSOR_SCALING, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_WORD_16, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .num_i2c_devs = 5, - .i2c_devs = { - { - .type = "saa7115", - .addr = 0x20, - .is_video = 1, - }, - { - .type = "uda1342", - .addr = 0x1a, - .is_audio = 1, - }, - { - .type = "tuner", - .addr = 0x60, - }, - { - .type = "tuner", - .addr = 0x43, - }, - { - .type = "sony-btf-mpx", - .addr = 0x44, - }, - }, - .num_inputs = 3, - .inputs = { - { - .video_input = 3, - .audio_index = 0, - .name = "Tuner", - }, - { - .video_input = 1, - .audio_index = 1, - .name = "Composite", - }, - { - .video_input = 8, - .audio_index = 1, - .name = "S-Video", - }, - }, - .video_config = SAA7115_IDQ_IS_DEFAULT, - .num_aud_inputs = 2, - .aud_inputs = { - { - .audio_input = UDA1342_IN2, - .name = "Tuner", - }, - { - .audio_input = UDA1342_IN1, - .name = "Line In", - }, - }, - }, -}; - -static const struct go7007_usb_board board_xmen = { - .flags = 0, - .main_info = { - .flags = GO7007_BOARD_USE_ONBOARD_I2C, - .hpi_buffer_cap = 0, - .sensor_flags = GO7007_SENSOR_VREF_POLAR, - .sensor_width = 320, - .sensor_height = 240, - .sensor_framerate = 30030, - .audio_flags = GO7007_AUDIO_ONE_CHANNEL | - GO7007_AUDIO_I2S_MODE_3 | - GO7007_AUDIO_WORD_14 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_BCLK_POLAR | - GO7007_AUDIO_OKI_MODE, - .audio_rate = 8000, - .audio_bclk_div = 48, - .audio_main_div = 1, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "ov7640", - .addr = 0x21, - }, - }, - .num_inputs = 1, - .inputs = { - { - .name = "Camera", - }, - }, - }, -}; - -static const struct go7007_usb_board board_matrix_revolution = { - .flags = GO7007_USB_EZUSB, - .main_info = { - .flags = GO7007_BOARD_HAS_AUDIO | - GO7007_BOARD_USE_ONBOARD_I2C, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "tw9903", - .is_video = 1, - .addr = 0x44, - }, - }, - .num_inputs = 2, - .inputs = { - { - .video_input = 2, - .name = "Composite", - }, - { - .video_input = 8, - .name = "S-Video", - }, - }, - }, -}; - -static const struct go7007_usb_board board_lifeview_lr192 = { - .flags = GO7007_USB_EZUSB, - .main_info = { - .flags = GO7007_BOARD_HAS_AUDIO | - GO7007_BOARD_USE_ONBOARD_I2C, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_VALID_ENABLE | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI | - GO7007_SENSOR_SCALING, - .num_i2c_devs = 0, - .num_inputs = 1, - .inputs = { - { - .video_input = 0, - .name = "Composite", - }, - }, - }, -}; - -static const struct go7007_usb_board board_endura = { - .flags = 0, - .main_info = { - .flags = 0, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_WORD_16, - .audio_rate = 8000, - .audio_bclk_div = 48, - .audio_main_div = 8, - .hpi_buffer_cap = 0, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_TV, - .sensor_h_offset = 8, - .num_i2c_devs = 0, - .num_inputs = 1, - .inputs = { - { - .name = "Camera", - }, - }, - }, -}; - -static const struct go7007_usb_board board_adlink_mpg24 = { - .flags = 0, - .main_info = { - .flags = GO7007_BOARD_USE_ONBOARD_I2C, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 0, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "tw2804", - .addr = 0x00, /* yes, really */ - .flags = I2C_CLIENT_TEN, - .is_video = 1, - }, - }, - .num_inputs = 1, - .inputs = { - { - .name = "Composite", - }, - }, - }, -}; - -static const struct go7007_usb_board board_sensoray_2250 = { - .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, - .main_info = { - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_WORD_16, - .flags = GO7007_BOARD_HAS_AUDIO, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_TV, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "s2250", - .addr = 0x43, - .is_video = 1, - .is_audio = 1, - }, - }, - .num_inputs = 2, - .inputs = { - { - .video_input = 0, - .name = "Composite", - }, - { - .video_input = 1, - .name = "S-Video", - }, - }, - .num_aud_inputs = 3, - .aud_inputs = { - { - .audio_input = 0, - .name = "Line In", - }, - { - .audio_input = 1, - .name = "Mic", - }, - { - .audio_input = 2, - .name = "Mic Boost", - }, - }, - }, -}; - -static const struct go7007_usb_board board_ads_usbav_709 = { - .flags = GO7007_USB_EZUSB, - .main_info = { - .flags = GO7007_BOARD_HAS_AUDIO | - GO7007_BOARD_USE_ONBOARD_I2C, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "tw9906", - .is_video = 1, - .addr = 0x44, - }, - }, - .num_inputs = 2, - .inputs = { - { - .video_input = 0, - .name = "Composite", - }, - { - .video_input = 10, - .name = "S-Video", - }, - }, - }, -}; - -static const struct usb_device_id go7007_usb_id_table[] = { - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | - USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x200, /* Revision number of XMen */ - .bcdDevice_hi = 0x200, - .bInterfaceClass = 255, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 255, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x202, /* Revision number of Matrix II */ - .bcdDevice_hi = 0x202, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_II, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x204, /* Revision number of Matrix */ - .bcdDevice_hi = 0x204, /* Reloaded */ - .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_RELOAD, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | - USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x205, /* Revision number of XMen-II */ - .bcdDevice_hi = 0x205, - .bInterfaceClass = 255, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 255, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_II, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x208, /* Revision number of Star Trek */ - .bcdDevice_hi = 0x208, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_STAR_TREK, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | - USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x209, /* Revision number of XMen-III */ - .bcdDevice_hi = 0x209, - .bInterfaceClass = 255, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 255, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_III, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x210, /* Revision number of Matrix */ - .bcdDevice_hi = 0x210, /* Revolution */ - .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_REV, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x093b, /* Vendor ID of Plextor */ - .idProduct = 0xa102, /* Product ID of M402U */ - .bcdDevice_lo = 0x1, /* revision number of Blueberry */ - .bcdDevice_hi = 0x1, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_M402U, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x093b, /* Vendor ID of Plextor */ - .idProduct = 0xa104, /* Product ID of TV402U */ - .bcdDevice_lo = 0x1, - .bcdDevice_hi = 0x1, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_TV402U, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x10fd, /* Vendor ID of Anubis Electronics */ - .idProduct = 0xde00, /* Product ID of Lifeview LR192 */ - .bcdDevice_lo = 0x1, - .bcdDevice_hi = 0x1, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_LIFEVIEW_LR192, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x1943, /* Vendor ID Sensoray */ - .idProduct = 0x2250, /* Product ID of 2250/2251 */ - .bcdDevice_lo = 0x1, - .bcdDevice_hi = 0x1, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_SENSORAY_2250, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x06e1, /* Vendor ID of ADS Technologies */ - .idProduct = 0x0709, /* Product ID of DVD Xpress DX2 */ - .bcdDevice_lo = 0x204, - .bcdDevice_hi = 0x204, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_ADS_USBAV_709, - }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, go7007_usb_id_table); - -/********************* Driver for EZ-USB HPI interface *********************/ - -static int go7007_usb_vendor_request(struct go7007 *go, int request, - int value, int index, void *transfer_buffer, int length, int in) -{ - struct go7007_usb *usb = go->hpi_context; - int timeout = 5000; - - if (in) { - return usb_control_msg(usb->usbdev, - usb_rcvctrlpipe(usb->usbdev, 0), request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - value, index, transfer_buffer, length, timeout); - } else { - return usb_control_msg(usb->usbdev, - usb_sndctrlpipe(usb->usbdev, 0), request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, transfer_buffer, length, timeout); - } -} - -static int go7007_usb_interface_reset(struct go7007 *go) -{ - struct go7007_usb *usb = go->hpi_context; - u16 intr_val, intr_data; - - if (go->status == STATUS_SHUTDOWN) - return -1; - /* Reset encoder */ - if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0) - return -1; - msleep(100); - - if (usb->board->flags & GO7007_USB_EZUSB) { - /* Reset buffer in EZ-USB */ - pr_debug("resetting EZ-USB buffers\n"); - if (go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0 || - go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0) - return -1; - - /* Reset encoder again */ - if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0) - return -1; - msleep(100); - } - - /* Wait for an interrupt to indicate successful hardware reset */ - if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || - (intr_val & ~0x1) != 0x55aa) { - dev_err(go->dev, "unable to reset the USB interface\n"); - return -1; - } - return 0; -} - -static int go7007_usb_ezusb_write_interrupt(struct go7007 *go, - int addr, int data) -{ - struct go7007_usb *usb = go->hpi_context; - int i, r; - u16 status_reg = 0; - int timeout = 500; - - pr_debug("WriteInterrupt: %04x %04x\n", addr, data); - - for (i = 0; i < 100; ++i) { - r = usb_control_msg(usb->usbdev, - usb_rcvctrlpipe(usb->usbdev, 0), 0x14, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0, HPI_STATUS_ADDR, go->usb_buf, - sizeof(status_reg), timeout); - if (r < 0) - break; - status_reg = le16_to_cpu(*((u16 *)go->usb_buf)); - if (!(status_reg & 0x0010)) - break; - msleep(10); - } - if (r < 0) - goto write_int_error; - if (i == 100) { - dev_err(go->dev, "device is hung, status reg = 0x%04x\n", status_reg); - return -1; - } - r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), 0x12, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, data, - INT_PARAM_ADDR, NULL, 0, timeout); - if (r < 0) - goto write_int_error; - r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), - 0x12, USB_TYPE_VENDOR | USB_RECIP_DEVICE, addr, - INT_INDEX_ADDR, NULL, 0, timeout); - if (r < 0) - goto write_int_error; - return 0; - -write_int_error: - dev_err(go->dev, "error in WriteInterrupt: %d\n", r); - return r; -} - -static int go7007_usb_onboard_write_interrupt(struct go7007 *go, - int addr, int data) -{ - struct go7007_usb *usb = go->hpi_context; - int r; - int timeout = 500; - - pr_debug("WriteInterrupt: %04x %04x\n", addr, data); - - go->usb_buf[0] = data & 0xff; - go->usb_buf[1] = data >> 8; - go->usb_buf[2] = addr & 0xff; - go->usb_buf[3] = addr >> 8; - go->usb_buf[4] = go->usb_buf[5] = go->usb_buf[6] = go->usb_buf[7] = 0; - r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 2), 0x00, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0x55aa, - 0xf0f0, go->usb_buf, 8, timeout); - if (r < 0) { - dev_err(go->dev, "error in WriteInterrupt: %d\n", r); - return r; - } - return 0; -} - -static void go7007_usb_readinterrupt_complete(struct urb *urb) -{ - struct go7007 *go = (struct go7007 *)urb->context; - u16 *regs = (u16 *)urb->transfer_buffer; - int status = urb->status; - - if (status) { - if (status != -ESHUTDOWN && - go->status != STATUS_SHUTDOWN) { - dev_err(go->dev, "error in read interrupt: %d\n", urb->status); - } else { - wake_up(&go->interrupt_waitq); - return; - } - } else if (urb->actual_length != urb->transfer_buffer_length) { - dev_err(go->dev, "short read in interrupt pipe!\n"); - } else { - go->interrupt_available = 1; - go->interrupt_data = __le16_to_cpu(regs[0]); - go->interrupt_value = __le16_to_cpu(regs[1]); - pr_debug("ReadInterrupt: %04x %04x\n", - go->interrupt_value, go->interrupt_data); - } - - wake_up(&go->interrupt_waitq); -} - -static int go7007_usb_read_interrupt(struct go7007 *go) -{ - struct go7007_usb *usb = go->hpi_context; - int r; - - r = usb_submit_urb(usb->intr_urb, GFP_KERNEL); - if (r < 0) { - dev_err(go->dev, "unable to submit interrupt urb: %d\n", r); - return r; - } - return 0; -} - -static void go7007_usb_read_video_pipe_complete(struct urb *urb) -{ - struct go7007 *go = (struct go7007 *)urb->context; - int r, status = urb->status; - - if (!vb2_is_streaming(&go->vidq)) { - wake_up_interruptible(&go->frame_waitq); - return; - } - if (status) { - dev_err(go->dev, "error in video pipe: %d\n", status); - return; - } - if (urb->actual_length != urb->transfer_buffer_length) { - dev_err(go->dev, "short read in video pipe!\n"); - return; - } - go7007_parse_video_stream(go, urb->transfer_buffer, urb->actual_length); - r = usb_submit_urb(urb, GFP_ATOMIC); - if (r < 0) - dev_err(go->dev, "error in video pipe: %d\n", r); -} - -static void go7007_usb_read_audio_pipe_complete(struct urb *urb) -{ - struct go7007 *go = (struct go7007 *)urb->context; - int r, status = urb->status; - - if (!vb2_is_streaming(&go->vidq)) - return; - if (status) { - dev_err(go->dev, "error in audio pipe: %d\n", - status); - return; - } - if (urb->actual_length != urb->transfer_buffer_length) { - dev_err(go->dev, "short read in audio pipe!\n"); - return; - } - if (go->audio_deliver != NULL) - go->audio_deliver(go, urb->transfer_buffer, urb->actual_length); - r = usb_submit_urb(urb, GFP_ATOMIC); - if (r < 0) - dev_err(go->dev, "error in audio pipe: %d\n", r); -} - -static int go7007_usb_stream_start(struct go7007 *go) -{ - struct go7007_usb *usb = go->hpi_context; - int i, r; - - for (i = 0; i < 8; ++i) { - r = usb_submit_urb(usb->video_urbs[i], GFP_KERNEL); - if (r < 0) { - dev_err(go->dev, "error submitting video urb %d: %d\n", i, r); - goto video_submit_failed; - } - } - if (!go->audio_enabled) - return 0; - - for (i = 0; i < 8; ++i) { - r = usb_submit_urb(usb->audio_urbs[i], GFP_KERNEL); - if (r < 0) { - dev_err(go->dev, "error submitting audio urb %d: %d\n", i, r); - goto audio_submit_failed; - } - } - return 0; - -audio_submit_failed: - for (i = 0; i < 7; ++i) - usb_kill_urb(usb->audio_urbs[i]); -video_submit_failed: - for (i = 0; i < 8; ++i) - usb_kill_urb(usb->video_urbs[i]); - return -1; -} - -static int go7007_usb_stream_stop(struct go7007 *go) -{ - struct go7007_usb *usb = go->hpi_context; - int i; - - if (go->status == STATUS_SHUTDOWN) - return 0; - for (i = 0; i < 8; ++i) - usb_kill_urb(usb->video_urbs[i]); - if (go->audio_enabled) - for (i = 0; i < 8; ++i) - usb_kill_urb(usb->audio_urbs[i]); - return 0; -} - -static int go7007_usb_send_firmware(struct go7007 *go, u8 *data, int len) -{ - struct go7007_usb *usb = go->hpi_context; - int transferred, pipe; - int timeout = 500; - - pr_debug("DownloadBuffer sending %d bytes\n", len); - - if (usb->board->flags & GO7007_USB_EZUSB) - pipe = usb_sndbulkpipe(usb->usbdev, 2); - else - pipe = usb_sndbulkpipe(usb->usbdev, 3); - - return usb_bulk_msg(usb->usbdev, pipe, data, len, - &transferred, timeout); -} - -static void go7007_usb_release(struct go7007 *go) -{ - struct go7007_usb *usb = go->hpi_context; - struct urb *vurb, *aurb; - int i; - - if (usb->intr_urb) { - usb_kill_urb(usb->intr_urb); - kfree(usb->intr_urb->transfer_buffer); - usb_free_urb(usb->intr_urb); - } - - /* Free USB-related structs */ - for (i = 0; i < 8; ++i) { - vurb = usb->video_urbs[i]; - if (vurb) { - usb_kill_urb(vurb); - kfree(vurb->transfer_buffer); - usb_free_urb(vurb); - } - aurb = usb->audio_urbs[i]; - if (aurb) { - usb_kill_urb(aurb); - kfree(aurb->transfer_buffer); - usb_free_urb(aurb); - } - } - - kfree(go->hpi_context); -} - -static struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = { - .interface_reset = go7007_usb_interface_reset, - .write_interrupt = go7007_usb_ezusb_write_interrupt, - .read_interrupt = go7007_usb_read_interrupt, - .stream_start = go7007_usb_stream_start, - .stream_stop = go7007_usb_stream_stop, - .send_firmware = go7007_usb_send_firmware, - .release = go7007_usb_release, -}; - -static struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = { - .interface_reset = go7007_usb_interface_reset, - .write_interrupt = go7007_usb_onboard_write_interrupt, - .read_interrupt = go7007_usb_read_interrupt, - .stream_start = go7007_usb_stream_start, - .stream_stop = go7007_usb_stream_stop, - .send_firmware = go7007_usb_send_firmware, - .release = go7007_usb_release, -}; - -/********************* Driver for EZ-USB I2C adapter *********************/ - -static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter, - struct i2c_msg msgs[], int num) -{ - struct go7007 *go = i2c_get_adapdata(adapter); - struct go7007_usb *usb = go->hpi_context; - u8 *buf = go->usb_buf; - int buf_len, i; - int ret = -EIO; - - if (go->status == STATUS_SHUTDOWN) - return -ENODEV; - - mutex_lock(&usb->i2c_lock); - - for (i = 0; i < num; ++i) { - /* The hardware command is "write some bytes then read some - * bytes", so we try to coalesce a write followed by a read - * into a single USB transaction */ - if (i + 1 < num && msgs[i].addr == msgs[i + 1].addr && - !(msgs[i].flags & I2C_M_RD) && - (msgs[i + 1].flags & I2C_M_RD)) { -#ifdef GO7007_I2C_DEBUG - pr_debug("i2c write/read %d/%d bytes on %02x\n", - msgs[i].len, msgs[i + 1].len, msgs[i].addr); -#endif - buf[0] = 0x01; - buf[1] = msgs[i].len + 1; - buf[2] = msgs[i].addr << 1; - memcpy(&buf[3], msgs[i].buf, msgs[i].len); - buf_len = msgs[i].len + 3; - buf[buf_len++] = msgs[++i].len; - } else if (msgs[i].flags & I2C_M_RD) { -#ifdef GO7007_I2C_DEBUG - pr_debug("i2c read %d bytes on %02x\n", - msgs[i].len, msgs[i].addr); -#endif - buf[0] = 0x01; - buf[1] = 1; - buf[2] = msgs[i].addr << 1; - buf[3] = msgs[i].len; - buf_len = 4; - } else { -#ifdef GO7007_I2C_DEBUG - pr_debug("i2c write %d bytes on %02x\n", - msgs[i].len, msgs[i].addr); -#endif - buf[0] = 0x00; - buf[1] = msgs[i].len + 1; - buf[2] = msgs[i].addr << 1; - memcpy(&buf[3], msgs[i].buf, msgs[i].len); - buf_len = msgs[i].len + 3; - buf[buf_len++] = 0; - } - if (go7007_usb_vendor_request(go, 0x24, 0, 0, - buf, buf_len, 0) < 0) - goto i2c_done; - if (msgs[i].flags & I2C_M_RD) { - memset(buf, 0, msgs[i].len + 1); - if (go7007_usb_vendor_request(go, 0x25, 0, 0, buf, - msgs[i].len + 1, 1) < 0) - goto i2c_done; - memcpy(msgs[i].buf, buf + 1, msgs[i].len); - } - } - ret = num; - -i2c_done: - mutex_unlock(&usb->i2c_lock); - return ret; -} - -static u32 go7007_usb_functionality(struct i2c_adapter *adapter) -{ - /* No errors are reported by the hardware, so we don't bother - * supporting quick writes to avoid confusing probing */ - return (I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK; -} - -static struct i2c_algorithm go7007_usb_algo = { - .master_xfer = go7007_usb_i2c_master_xfer, - .functionality = go7007_usb_functionality, -}; - -static struct i2c_adapter go7007_usb_adap_templ = { - .owner = THIS_MODULE, - .name = "WIS GO7007SB EZ-USB", - .algo = &go7007_usb_algo, -}; - -/********************* USB add/remove functions *********************/ - -static int go7007_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct go7007 *go; - struct go7007_usb *usb; - const struct go7007_usb_board *board; - struct usb_device *usbdev = interface_to_usbdev(intf); - unsigned num_i2c_devs; - char *name; - int video_pipe, i, v_urb_len; - - pr_debug("probing new GO7007 USB board\n"); - - switch (id->driver_info) { - case GO7007_BOARDID_MATRIX_II: - name = "WIS Matrix II or compatible"; - board = &board_matrix_ii; - break; - case GO7007_BOARDID_MATRIX_RELOAD: - name = "WIS Matrix Reloaded or compatible"; - board = &board_matrix_reload; - break; - case GO7007_BOARDID_MATRIX_REV: - name = "WIS Matrix Revolution or compatible"; - board = &board_matrix_revolution; - break; - case GO7007_BOARDID_STAR_TREK: - name = "WIS Star Trek or compatible"; - board = &board_star_trek; - break; - case GO7007_BOARDID_XMEN: - name = "WIS XMen or compatible"; - board = &board_xmen; - break; - case GO7007_BOARDID_XMEN_II: - name = "WIS XMen II or compatible"; - board = &board_xmen; - break; - case GO7007_BOARDID_XMEN_III: - name = "WIS XMen III or compatible"; - board = &board_xmen; - break; - case GO7007_BOARDID_PX_M402U: - name = "Plextor PX-M402U"; - board = &board_matrix_ii; - break; - case GO7007_BOARDID_PX_TV402U: - name = "Plextor PX-TV402U (unknown tuner)"; - board = &board_px_tv402u; - break; - case GO7007_BOARDID_LIFEVIEW_LR192: - dev_err(&intf->dev, "The Lifeview TV Walker Ultra is not supported. Sorry!\n"); - return -ENODEV; - name = "Lifeview TV Walker Ultra"; - board = &board_lifeview_lr192; - break; - case GO7007_BOARDID_SENSORAY_2250: - dev_info(&intf->dev, "Sensoray 2250 found\n"); - name = "Sensoray 2250/2251"; - board = &board_sensoray_2250; - break; - case GO7007_BOARDID_ADS_USBAV_709: - name = "ADS Tech DVD Xpress DX2"; - board = &board_ads_usbav_709; - break; - default: - dev_err(&intf->dev, "unknown board ID %d!\n", - (unsigned int)id->driver_info); - return -ENODEV; - } - - go = go7007_alloc(&board->main_info, &intf->dev); - if (go == NULL) - return -ENOMEM; - - usb = kzalloc(sizeof(struct go7007_usb), GFP_KERNEL); - if (usb == NULL) { - kfree(go); - return -ENOMEM; - } - - usb->board = board; - usb->usbdev = usbdev; - usb_make_path(usbdev, go->bus_info, sizeof(go->bus_info)); - go->board_id = id->driver_info; - strncpy(go->name, name, sizeof(go->name)); - if (board->flags & GO7007_USB_EZUSB) - go->hpi_ops = &go7007_usb_ezusb_hpi_ops; - else - go->hpi_ops = &go7007_usb_onboard_hpi_ops; - go->hpi_context = usb; - - /* Allocate the URB and buffer for receiving incoming interrupts */ - usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); - if (usb->intr_urb == NULL) - goto allocfail; - usb->intr_urb->transfer_buffer = kmalloc(2*sizeof(u16), GFP_KERNEL); - if (usb->intr_urb->transfer_buffer == NULL) - goto allocfail; - - if (go->board_id == GO7007_BOARDID_SENSORAY_2250) - usb_fill_bulk_urb(usb->intr_urb, usb->usbdev, - usb_rcvbulkpipe(usb->usbdev, 4), - usb->intr_urb->transfer_buffer, 2*sizeof(u16), - go7007_usb_readinterrupt_complete, go); - else - usb_fill_int_urb(usb->intr_urb, usb->usbdev, - usb_rcvintpipe(usb->usbdev, 4), - usb->intr_urb->transfer_buffer, 2*sizeof(u16), - go7007_usb_readinterrupt_complete, go, 8); - usb_set_intfdata(intf, &go->v4l2_dev); - - /* Boot the GO7007 */ - if (go7007_boot_encoder(go, go->board_info->flags & - GO7007_BOARD_USE_ONBOARD_I2C) < 0) - goto allocfail; - - /* Register the EZ-USB I2C adapter, if we're using it */ - if (board->flags & GO7007_USB_EZUSB_I2C) { - memcpy(&go->i2c_adapter, &go7007_usb_adap_templ, - sizeof(go7007_usb_adap_templ)); - mutex_init(&usb->i2c_lock); - go->i2c_adapter.dev.parent = go->dev; - i2c_set_adapdata(&go->i2c_adapter, go); - if (i2c_add_adapter(&go->i2c_adapter) < 0) { - dev_err(go->dev, "error: i2c_add_adapter failed\n"); - goto allocfail; - } - go->i2c_adapter_online = 1; - } - - /* Pelco and Adlink reused the XMen and XMen-III vendor and product - * IDs for their own incompatible designs. We can detect XMen boards - * by probing the sensor, but there is no way to probe the sensors on - * the Pelco and Adlink designs so we default to the Adlink. If it - * is actually a Pelco, the user must set the assume_endura module - * parameter. */ - if ((go->board_id == GO7007_BOARDID_XMEN || - go->board_id == GO7007_BOARDID_XMEN_III) && - go->i2c_adapter_online) { - union i2c_smbus_data data; - - /* Check to see if register 0x0A is 0x76 */ - i2c_smbus_xfer(&go->i2c_adapter, 0x21, I2C_CLIENT_SCCB, - I2C_SMBUS_READ, 0x0A, I2C_SMBUS_BYTE_DATA, &data); - if (data.byte != 0x76) { - if (assume_endura) { - go->board_id = GO7007_BOARDID_ENDURA; - usb->board = board = &board_endura; - go->board_info = &board->main_info; - strncpy(go->name, "Pelco Endura", - sizeof(go->name)); - } else { - u16 channel; - - /* read channel number from GPIO[1:0] */ - go7007_read_addr(go, 0x3c81, &channel); - channel &= 0x3; - go->board_id = GO7007_BOARDID_ADLINK_MPG24; - usb->board = board = &board_adlink_mpg24; - go->board_info = &board->main_info; - go->channel_number = channel; - snprintf(go->name, sizeof(go->name), - "Adlink PCI-MPG24, channel #%d", - channel); - } - go7007_update_board(go); - } - } - - num_i2c_devs = go->board_info->num_i2c_devs; - - /* Probe the tuner model on the TV402U */ - if (go->board_id == GO7007_BOARDID_PX_TV402U) { - /* Board strapping indicates tuner model */ - if (go7007_usb_vendor_request(go, 0x41, 0, 0, go->usb_buf, 3, - 1) < 0) { - dev_err(go->dev, "GPIO read failed!\n"); - goto allocfail; - } - switch (go->usb_buf[0] >> 6) { - case 1: - go->tuner_type = TUNER_SONY_BTF_PG472Z; - go->std = V4L2_STD_PAL; - strncpy(go->name, "Plextor PX-TV402U-EU", - sizeof(go->name)); - break; - case 2: - go->tuner_type = TUNER_SONY_BTF_PK467Z; - go->std = V4L2_STD_NTSC_M_JP; - num_i2c_devs -= 2; - strncpy(go->name, "Plextor PX-TV402U-JP", - sizeof(go->name)); - break; - case 3: - go->tuner_type = TUNER_SONY_BTF_PB463Z; - num_i2c_devs -= 2; - strncpy(go->name, "Plextor PX-TV402U-NA", - sizeof(go->name)); - break; - default: - pr_debug("unable to detect tuner type!\n"); - break; - } - /* Configure tuner mode selection inputs connected - * to the EZ-USB GPIO output pins */ - if (go7007_usb_vendor_request(go, 0x40, 0x7f02, 0, - NULL, 0, 0) < 0) { - dev_err(go->dev, "GPIO write failed!\n"); - goto allocfail; - } - } - - /* Print a nasty message if the user attempts to use a USB2.0 device in - * a USB1.1 port. There will be silent corruption of the stream. */ - if ((board->flags & GO7007_USB_EZUSB) && - usbdev->speed != USB_SPEED_HIGH) - dev_err(go->dev, "*** WARNING *** This device must be connected to a USB 2.0 port! Attempting to capture video through a USB 1.1 port will result in stream corruption, even at low bitrates!\n"); - - /* Allocate the URBs and buffers for receiving the video stream */ - if (board->flags & GO7007_USB_EZUSB) { - v_urb_len = 1024; - video_pipe = usb_rcvbulkpipe(usb->usbdev, 6); - } else { - v_urb_len = 512; - video_pipe = usb_rcvbulkpipe(usb->usbdev, 1); - } - for (i = 0; i < 8; ++i) { - usb->video_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); - if (usb->video_urbs[i] == NULL) - goto allocfail; - usb->video_urbs[i]->transfer_buffer = - kmalloc(v_urb_len, GFP_KERNEL); - if (usb->video_urbs[i]->transfer_buffer == NULL) - goto allocfail; - usb_fill_bulk_urb(usb->video_urbs[i], usb->usbdev, video_pipe, - usb->video_urbs[i]->transfer_buffer, v_urb_len, - go7007_usb_read_video_pipe_complete, go); - } - - /* Allocate the URBs and buffers for receiving the audio stream */ - if ((board->flags & GO7007_USB_EZUSB) && - (board->flags & GO7007_BOARD_HAS_AUDIO)) { - for (i = 0; i < 8; ++i) { - usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); - if (usb->audio_urbs[i] == NULL) - goto allocfail; - usb->audio_urbs[i]->transfer_buffer = kmalloc(4096, - GFP_KERNEL); - if (usb->audio_urbs[i]->transfer_buffer == NULL) - goto allocfail; - usb_fill_bulk_urb(usb->audio_urbs[i], usb->usbdev, - usb_rcvbulkpipe(usb->usbdev, 8), - usb->audio_urbs[i]->transfer_buffer, 4096, - go7007_usb_read_audio_pipe_complete, go); - } - } - - /* Do any final GO7007 initialization, then register the - * V4L2 and ALSA interfaces */ - if (go7007_register_encoder(go, num_i2c_devs) < 0) - goto allocfail; - - go->status = STATUS_ONLINE; - return 0; - -allocfail: - go7007_usb_release(go); - kfree(go); - return -ENOMEM; -} - -static void go7007_usb_disconnect(struct usb_interface *intf) -{ - struct go7007 *go = to_go7007(usb_get_intfdata(intf)); - - mutex_lock(&go->queue_lock); - mutex_lock(&go->serialize_lock); - - if (go->audio_enabled) - go7007_snd_remove(go); - - go->status = STATUS_SHUTDOWN; - v4l2_device_disconnect(&go->v4l2_dev); - video_unregister_device(&go->vdev); - mutex_unlock(&go->serialize_lock); - mutex_unlock(&go->queue_lock); - - v4l2_device_put(&go->v4l2_dev); -} - -static struct usb_driver go7007_usb_driver = { - .name = "go7007", - .probe = go7007_usb_probe, - .disconnect = go7007_usb_disconnect, - .id_table = go7007_usb_id_table, -}; - -module_usb_driver(go7007_usb_driver); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c deleted file mode 100644 index ec799b4..0000000 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ /dev/null @@ -1,1173 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007-priv.h" - -#define call_all(dev, o, f, args...) \ - v4l2_device_call_until_err(dev, 0, o, f, ##args) - -static bool valid_pixelformat(u32 pixelformat) -{ - switch (pixelformat) { - case V4L2_PIX_FMT_MJPEG: - case V4L2_PIX_FMT_MPEG1: - case V4L2_PIX_FMT_MPEG2: - case V4L2_PIX_FMT_MPEG4: - return true; - default: - return false; - } -} - -static u32 get_frame_type_flag(struct go7007_buffer *vb, int format) -{ - u8 *ptr = vb2_plane_vaddr(&vb->vb, 0); - - switch (format) { - case V4L2_PIX_FMT_MJPEG: - return V4L2_BUF_FLAG_KEYFRAME; - case V4L2_PIX_FMT_MPEG4: - switch ((ptr[vb->frame_offset + 4] >> 6) & 0x3) { - case 0: - return V4L2_BUF_FLAG_KEYFRAME; - case 1: - return V4L2_BUF_FLAG_PFRAME; - case 2: - return V4L2_BUF_FLAG_BFRAME; - default: - return 0; - } - case V4L2_PIX_FMT_MPEG1: - case V4L2_PIX_FMT_MPEG2: - switch ((ptr[vb->frame_offset + 5] >> 3) & 0x7) { - case 1: - return V4L2_BUF_FLAG_KEYFRAME; - case 2: - return V4L2_BUF_FLAG_PFRAME; - case 3: - return V4L2_BUF_FLAG_BFRAME; - default: - return 0; - } - } - - return 0; -} - -static void get_resolution(struct go7007 *go, int *width, int *height) -{ - switch (go->standard) { - case GO7007_STD_NTSC: - *width = 720; - *height = 480; - break; - case GO7007_STD_PAL: - *width = 720; - *height = 576; - break; - case GO7007_STD_OTHER: - default: - *width = go->board_info->sensor_width; - *height = go->board_info->sensor_height; - break; - } -} - -static void set_formatting(struct go7007 *go) -{ - if (go->format == V4L2_PIX_FMT_MJPEG) { - go->pali = 0; - go->aspect_ratio = GO7007_RATIO_1_1; - go->gop_size = 0; - go->ipb = 0; - go->closed_gop = 0; - go->repeat_seqhead = 0; - go->seq_header_enable = 0; - go->gop_header_enable = 0; - go->dvd_mode = 0; - return; - } - - switch (go->format) { - case V4L2_PIX_FMT_MPEG1: - go->pali = 0; - break; - default: - case V4L2_PIX_FMT_MPEG2: - go->pali = 0x48; - break; - case V4L2_PIX_FMT_MPEG4: - /* For future reference: this is the list of MPEG4 - * profiles that are available, although they are - * untested: - * - * Profile pali - * -------------- ---- - * PROFILE_S_L0 0x08 - * PROFILE_S_L1 0x01 - * PROFILE_S_L2 0x02 - * PROFILE_S_L3 0x03 - * PROFILE_ARTS_L1 0x91 - * PROFILE_ARTS_L2 0x92 - * PROFILE_ARTS_L3 0x93 - * PROFILE_ARTS_L4 0x94 - * PROFILE_AS_L0 0xf0 - * PROFILE_AS_L1 0xf1 - * PROFILE_AS_L2 0xf2 - * PROFILE_AS_L3 0xf3 - * PROFILE_AS_L4 0xf4 - * PROFILE_AS_L5 0xf5 - */ - go->pali = 0xf5; - break; - } - go->gop_size = v4l2_ctrl_g_ctrl(go->mpeg_video_gop_size); - go->closed_gop = v4l2_ctrl_g_ctrl(go->mpeg_video_gop_closure); - go->ipb = v4l2_ctrl_g_ctrl(go->mpeg_video_b_frames) != 0; - go->bitrate = v4l2_ctrl_g_ctrl(go->mpeg_video_bitrate); - go->repeat_seqhead = v4l2_ctrl_g_ctrl(go->mpeg_video_rep_seqheader); - go->gop_header_enable = 1; - go->dvd_mode = 0; - if (go->format == V4L2_PIX_FMT_MPEG2) - go->dvd_mode = - go->bitrate == 9800000 && - go->gop_size == 15 && - go->ipb == 0 && - go->repeat_seqhead == 1 && - go->closed_gop; - - switch (v4l2_ctrl_g_ctrl(go->mpeg_video_aspect_ratio)) { - default: - case V4L2_MPEG_VIDEO_ASPECT_1x1: - go->aspect_ratio = GO7007_RATIO_1_1; - break; - case V4L2_MPEG_VIDEO_ASPECT_4x3: - go->aspect_ratio = GO7007_RATIO_4_3; - break; - case V4L2_MPEG_VIDEO_ASPECT_16x9: - go->aspect_ratio = GO7007_RATIO_16_9; - break; - } -} - -static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) -{ - int sensor_height = 0, sensor_width = 0; - int width, height; - - if (fmt != NULL && !valid_pixelformat(fmt->fmt.pix.pixelformat)) - return -EINVAL; - - get_resolution(go, &sensor_width, &sensor_height); - - if (fmt == NULL) { - width = sensor_width; - height = sensor_height; - } else if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { - if (fmt->fmt.pix.width > sensor_width) - width = sensor_width; - else if (fmt->fmt.pix.width < 144) - width = 144; - else - width = fmt->fmt.pix.width & ~0x0f; - - if (fmt->fmt.pix.height > sensor_height) - height = sensor_height; - else if (fmt->fmt.pix.height < 96) - height = 96; - else - height = fmt->fmt.pix.height & ~0x0f; - } else { - width = fmt->fmt.pix.width; - - if (width <= sensor_width / 4) { - width = sensor_width / 4; - height = sensor_height / 4; - } else if (width <= sensor_width / 2) { - width = sensor_width / 2; - height = sensor_height / 2; - } else { - width = sensor_width; - height = sensor_height; - } - width &= ~0xf; - height &= ~0xf; - } - - if (fmt != NULL) { - u32 pixelformat = fmt->fmt.pix.pixelformat; - - memset(fmt, 0, sizeof(*fmt)); - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt->fmt.pix.width = width; - fmt->fmt.pix.height = height; - fmt->fmt.pix.pixelformat = pixelformat; - fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - } - - if (try) - return 0; - - if (fmt) - go->format = fmt->fmt.pix.pixelformat; - go->width = width; - go->height = height; - go->encoder_h_offset = go->board_info->sensor_h_offset; - go->encoder_v_offset = go->board_info->sensor_v_offset; - - if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { - struct v4l2_mbus_framefmt mbus_fmt; - - mbus_fmt.code = V4L2_MBUS_FMT_FIXED; - mbus_fmt.width = fmt ? fmt->fmt.pix.width : width; - mbus_fmt.height = height; - go->encoder_h_halve = 0; - go->encoder_v_halve = 0; - go->encoder_subsample = 0; - call_all(&go->v4l2_dev, video, s_mbus_fmt, &mbus_fmt); - } else { - if (width <= sensor_width / 4) { - go->encoder_h_halve = 1; - go->encoder_v_halve = 1; - go->encoder_subsample = 1; - } else if (width <= sensor_width / 2) { - go->encoder_h_halve = 1; - go->encoder_v_halve = 1; - go->encoder_subsample = 0; - } else { - go->encoder_h_halve = 0; - go->encoder_v_halve = 0; - go->encoder_subsample = 0; - } - } - return 0; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct go7007 *go = video_drvdata(file); - - strlcpy(cap->driver, "go7007", sizeof(cap->driver)); - strlcpy(cap->card, go->name, sizeof(cap->card)); - strlcpy(cap->bus_info, go->bus_info, sizeof(cap->bus_info)); - - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - - if (go->board_info->num_aud_inputs) - cap->device_caps |= V4L2_CAP_AUDIO; - if (go->board_info->flags & GO7007_BOARD_HAS_TUNER) - cap->device_caps |= V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *fmt) -{ - char *desc = NULL; - - switch (fmt->index) { - case 0: - fmt->pixelformat = V4L2_PIX_FMT_MJPEG; - desc = "Motion JPEG"; - break; - case 1: - fmt->pixelformat = V4L2_PIX_FMT_MPEG1; - desc = "MPEG-1 ES"; - break; - case 2: - fmt->pixelformat = V4L2_PIX_FMT_MPEG2; - desc = "MPEG-2 ES"; - break; - case 3: - fmt->pixelformat = V4L2_PIX_FMT_MPEG4; - desc = "MPEG-4 ES"; - break; - default: - return -EINVAL; - } - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt->flags = V4L2_FMT_FLAG_COMPRESSED; - - strncpy(fmt->description, desc, sizeof(fmt->description)); - - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct go7007 *go = video_drvdata(file); - - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt->fmt.pix.width = go->width; - fmt->fmt.pix.height = go->height; - fmt->fmt.pix.pixelformat = go->format; - fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct go7007 *go = video_drvdata(file); - - return set_capture_size(go, fmt, 1); -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct go7007 *go = video_drvdata(file); - - if (vb2_is_busy(&go->vidq)) - return -EBUSY; - - return set_capture_size(go, fmt, 0); -} - -static int go7007_queue_setup(struct vb2_queue *q, - const struct v4l2_format *fmt, - unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - sizes[0] = GO7007_BUF_SIZE; - *num_planes = 1; - - if (*num_buffers < 2) - *num_buffers = 2; - - return 0; -} - -static void go7007_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct go7007 *go = vb2_get_drv_priv(vq); - struct go7007_buffer *go7007_vb = - container_of(vb, struct go7007_buffer, vb); - unsigned long flags; - - spin_lock_irqsave(&go->spinlock, flags); - list_add_tail(&go7007_vb->list, &go->vidq_active); - spin_unlock_irqrestore(&go->spinlock, flags); -} - -static int go7007_buf_prepare(struct vb2_buffer *vb) -{ - struct go7007_buffer *go7007_vb = - container_of(vb, struct go7007_buffer, vb); - - go7007_vb->modet_active = 0; - go7007_vb->frame_offset = 0; - vb->v4l2_planes[0].bytesused = 0; - return 0; -} - -static void go7007_buf_finish(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct go7007 *go = vb2_get_drv_priv(vq); - struct go7007_buffer *go7007_vb = - container_of(vb, struct go7007_buffer, vb); - u32 frame_type_flag = get_frame_type_flag(go7007_vb, go->format); - struct v4l2_buffer *buf = &vb->v4l2_buf; - - buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_BFRAME | - V4L2_BUF_FLAG_PFRAME); - buf->flags |= frame_type_flag; - buf->field = V4L2_FIELD_NONE; -} - -static int go7007_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct go7007 *go = vb2_get_drv_priv(q); - int ret; - - set_formatting(go); - mutex_lock(&go->hw_lock); - go->next_seq = 0; - go->active_buf = NULL; - go->modet_event_status = 0; - q->streaming = 1; - if (go7007_start_encoder(go) < 0) - ret = -EIO; - else - ret = 0; - mutex_unlock(&go->hw_lock); - if (ret) { - q->streaming = 0; - return ret; - } - call_all(&go->v4l2_dev, video, s_stream, 1); - v4l2_ctrl_grab(go->mpeg_video_gop_size, true); - v4l2_ctrl_grab(go->mpeg_video_gop_closure, true); - v4l2_ctrl_grab(go->mpeg_video_bitrate, true); - v4l2_ctrl_grab(go->mpeg_video_aspect_ratio, true); - /* Turn on Capture LED */ - if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) - go7007_write_addr(go, 0x3c82, 0x0005); - return ret; -} - -static void go7007_stop_streaming(struct vb2_queue *q) -{ - struct go7007 *go = vb2_get_drv_priv(q); - unsigned long flags; - - q->streaming = 0; - go7007_stream_stop(go); - mutex_lock(&go->hw_lock); - go7007_reset_encoder(go); - mutex_unlock(&go->hw_lock); - call_all(&go->v4l2_dev, video, s_stream, 0); - - spin_lock_irqsave(&go->spinlock, flags); - INIT_LIST_HEAD(&go->vidq_active); - spin_unlock_irqrestore(&go->spinlock, flags); - v4l2_ctrl_grab(go->mpeg_video_gop_size, false); - v4l2_ctrl_grab(go->mpeg_video_gop_closure, false); - v4l2_ctrl_grab(go->mpeg_video_bitrate, false); - v4l2_ctrl_grab(go->mpeg_video_aspect_ratio, false); - /* Turn on Capture LED */ - if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) - go7007_write_addr(go, 0x3c82, 0x000d); -} - -static struct vb2_ops go7007_video_qops = { - .queue_setup = go7007_queue_setup, - .buf_queue = go7007_buf_queue, - .buf_prepare = go7007_buf_prepare, - .buf_finish = go7007_buf_finish, - .start_streaming = go7007_start_streaming, - .stop_streaming = go7007_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int vidioc_g_parm(struct file *filp, void *priv, - struct v4l2_streamparm *parm) -{ - struct go7007 *go = video_drvdata(filp); - struct v4l2_fract timeperframe = { - .numerator = 1001 * go->fps_scale, - .denominator = go->sensor_framerate, - }; - - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - parm->parm.capture.readbuffers = 2; - parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - parm->parm.capture.timeperframe = timeperframe; - - return 0; -} - -static int vidioc_s_parm(struct file *filp, void *priv, - struct v4l2_streamparm *parm) -{ - struct go7007 *go = video_drvdata(filp); - unsigned int n, d; - - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - n = go->sensor_framerate * - parm->parm.capture.timeperframe.numerator; - d = 1001 * parm->parm.capture.timeperframe.denominator; - if (n != 0 && d != 0 && n > d) - go->fps_scale = (n + d/2) / d; - else - go->fps_scale = 1; - - return vidioc_g_parm(filp, priv, parm); -} - -/* VIDIOC_ENUMSTD on go7007 were used for enumerating the supported fps and - its resolution, when the device is not connected to TV. - This is were an API abuse, probably used by the lack of specific IOCTL's to - enumerate it, by the time the driver was written. - - However, since kernel 2.6.19, two new ioctls (VIDIOC_ENUM_FRAMEINTERVALS - and VIDIOC_ENUM_FRAMESIZES) were added for this purpose. - - The two functions below implement the newer ioctls -*/ -static int vidioc_enum_framesizes(struct file *filp, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct go7007 *go = video_drvdata(filp); - int width, height; - - if (fsize->index > 2) - return -EINVAL; - - if (!valid_pixelformat(fsize->pixel_format)) - return -EINVAL; - - get_resolution(go, &width, &height); - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = (width >> fsize->index) & ~0xf; - fsize->discrete.height = (height >> fsize->index) & ~0xf; - return 0; -} - -static int vidioc_enum_frameintervals(struct file *filp, void *priv, - struct v4l2_frmivalenum *fival) -{ - struct go7007 *go = video_drvdata(filp); - int width, height; - int i; - - if (fival->index > 4) - return -EINVAL; - - if (!valid_pixelformat(fival->pixel_format)) - return -EINVAL; - - if (!(go->board_info->sensor_flags & GO7007_SENSOR_SCALING)) { - get_resolution(go, &width, &height); - for (i = 0; i <= 2; i++) - if (fival->width == ((width >> i) & ~0xf) && - fival->height == ((height >> i) & ~0xf)) - break; - if (i > 2) - return -EINVAL; - } - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete.numerator = 1001 * (fival->index + 1); - fival->discrete.denominator = go->sensor_framerate; - return 0; -} - -static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct go7007 *go = video_drvdata(file); - - *std = go->std; - return 0; -} - -static int go7007_s_std(struct go7007 *go) -{ - if (go->std & V4L2_STD_625_50) { - go->standard = GO7007_STD_PAL; - go->sensor_framerate = 25025; - } else { - go->standard = GO7007_STD_NTSC; - go->sensor_framerate = 30000; - } - - call_all(&go->v4l2_dev, video, s_std, go->std); - set_capture_size(go, NULL, 0); - return 0; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct go7007 *go = video_drvdata(file); - - if (vb2_is_busy(&go->vidq)) - return -EBUSY; - - go->std = std; - - return go7007_s_std(go); -} - -static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) -{ - struct go7007 *go = video_drvdata(file); - - return call_all(&go->v4l2_dev, video, querystd, std); -} - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct go7007 *go = video_drvdata(file); - - if (inp->index >= go->board_info->num_inputs) - return -EINVAL; - - strncpy(inp->name, go->board_info->inputs[inp->index].name, - sizeof(inp->name)); - - /* If this board has a tuner, it will be the first input */ - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && - inp->index == 0) - inp->type = V4L2_INPUT_TYPE_TUNER; - else - inp->type = V4L2_INPUT_TYPE_CAMERA; - - if (go->board_info->num_aud_inputs) - inp->audioset = (1 << go->board_info->num_aud_inputs) - 1; - else - inp->audioset = 0; - inp->tuner = 0; - if (go->board_info->sensor_flags & GO7007_SENSOR_TV) - inp->std = video_devdata(file)->tvnorms; - else - inp->std = 0; - - return 0; -} - - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *input) -{ - struct go7007 *go = video_drvdata(file); - - *input = go->input; - - return 0; -} - -static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) -{ - struct go7007 *go = video_drvdata(file); - - if (a->index >= go->board_info->num_aud_inputs) - return -EINVAL; - strlcpy(a->name, go->board_info->aud_inputs[a->index].name, - sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) -{ - struct go7007 *go = video_drvdata(file); - - a->index = go->aud_input; - strlcpy(a->name, go->board_info->aud_inputs[go->aud_input].name, - sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *fh, - const struct v4l2_audio *a) -{ - struct go7007 *go = video_drvdata(file); - - if (a->index >= go->board_info->num_aud_inputs) - return -EINVAL; - go->aud_input = a->index; - v4l2_subdev_call(go->sd_audio, audio, s_routing, - go->board_info->aud_inputs[go->aud_input].audio_input, 0, 0); - return 0; -} - -static void go7007_s_input(struct go7007 *go) -{ - unsigned int input = go->input; - - v4l2_subdev_call(go->sd_video, video, s_routing, - go->board_info->inputs[input].video_input, 0, - go->board_info->video_config); - if (go->board_info->num_aud_inputs) { - int aud_input = go->board_info->inputs[input].audio_index; - - v4l2_subdev_call(go->sd_audio, audio, s_routing, - go->board_info->aud_inputs[aud_input].audio_input, 0, 0); - go->aud_input = aud_input; - } -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int input) -{ - struct go7007 *go = video_drvdata(file); - - if (input >= go->board_info->num_inputs) - return -EINVAL; - if (vb2_is_busy(&go->vidq)) - return -EBUSY; - - go->input = input; - go7007_s_input(go); - - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct go7007 *go = video_drvdata(file); - - if (t->index != 0) - return -EINVAL; - - strlcpy(t->name, "Tuner", sizeof(t->name)); - return call_all(&go->v4l2_dev, tuner, g_tuner, t); -} - -static int vidioc_s_tuner(struct file *file, void *priv, - const struct v4l2_tuner *t) -{ - struct go7007 *go = video_drvdata(file); - - if (t->index != 0) - return -EINVAL; - - return call_all(&go->v4l2_dev, tuner, s_tuner, t); -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct go7007 *go = video_drvdata(file); - - if (f->tuner) - return -EINVAL; - - return call_all(&go->v4l2_dev, tuner, g_frequency, f); -} - -static int vidioc_s_frequency(struct file *file, void *priv, - const struct v4l2_frequency *f) -{ - struct go7007 *go = video_drvdata(file); - - if (f->tuner) - return -EINVAL; - - return call_all(&go->v4l2_dev, tuner, s_frequency, f); -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct go7007 *go = video_drvdata(file); - - v4l2_ctrl_log_status(file, priv); - return call_all(&go->v4l2_dev, core, log_status); -} - -static int vidioc_subscribe_event(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - - switch (sub->type) { - case V4L2_EVENT_CTRL: - return v4l2_ctrl_subscribe_event(fh, sub); - case V4L2_EVENT_MOTION_DET: - /* Allow for up to 30 events (1 second for NTSC) to be - * stored. */ - return v4l2_event_subscribe(fh, sub, 30, NULL); - } - return -EINVAL; -} - - -static int go7007_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct go7007 *go = - container_of(ctrl->handler, struct go7007, hdl); - unsigned y; - u8 *mt; - - switch (ctrl->id) { - case V4L2_CID_PIXEL_THRESHOLD0: - go->modet[0].pixel_threshold = ctrl->val; - break; - case V4L2_CID_MOTION_THRESHOLD0: - go->modet[0].motion_threshold = ctrl->val; - break; - case V4L2_CID_MB_THRESHOLD0: - go->modet[0].mb_threshold = ctrl->val; - break; - case V4L2_CID_PIXEL_THRESHOLD1: - go->modet[1].pixel_threshold = ctrl->val; - break; - case V4L2_CID_MOTION_THRESHOLD1: - go->modet[1].motion_threshold = ctrl->val; - break; - case V4L2_CID_MB_THRESHOLD1: - go->modet[1].mb_threshold = ctrl->val; - break; - case V4L2_CID_PIXEL_THRESHOLD2: - go->modet[2].pixel_threshold = ctrl->val; - break; - case V4L2_CID_MOTION_THRESHOLD2: - go->modet[2].motion_threshold = ctrl->val; - break; - case V4L2_CID_MB_THRESHOLD2: - go->modet[2].mb_threshold = ctrl->val; - break; - case V4L2_CID_PIXEL_THRESHOLD3: - go->modet[3].pixel_threshold = ctrl->val; - break; - case V4L2_CID_MOTION_THRESHOLD3: - go->modet[3].motion_threshold = ctrl->val; - break; - case V4L2_CID_MB_THRESHOLD3: - go->modet[3].mb_threshold = ctrl->val; - break; - case V4L2_CID_DETECT_MD_REGION_GRID: - mt = go->modet_map; - for (y = 0; y < go->height / 16; y++, mt += go->width / 16) - memcpy(mt, ctrl->p_new.p_u8 + y * (720 / 16), go->width / 16); - break; - default: - return -EINVAL; - } - return 0; -} - -static struct v4l2_file_operations go7007_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .unlocked_ioctl = video_ioctl2, - .read = vb2_fop_read, - .mmap = vb2_fop_mmap, - .poll = vb2_fop_poll, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_g_std = vidioc_g_std, - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_enumaudio = vidioc_enumaudio, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_g_parm = vidioc_g_parm, - .vidioc_s_parm = vidioc_s_parm, - .vidioc_enum_framesizes = vidioc_enum_framesizes, - .vidioc_enum_frameintervals = vidioc_enum_frameintervals, - .vidioc_log_status = vidioc_log_status, - .vidioc_subscribe_event = vidioc_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static struct video_device go7007_template = { - .name = "go7007", - .fops = &go7007_fops, - .release = video_device_release_empty, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = V4L2_STD_ALL, -}; - -static const struct v4l2_ctrl_ops go7007_ctrl_ops = { - .s_ctrl = go7007_s_ctrl, -}; - -static const struct v4l2_ctrl_config go7007_pixel_threshold0_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_PIXEL_THRESHOLD0, - .name = "Pixel Threshold Region 0", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 20, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_motion_threshold0_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_MOTION_THRESHOLD0, - .name = "Motion Threshold Region 0", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 80, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_mb_threshold0_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_MB_THRESHOLD0, - .name = "MB Threshold Region 0", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 200, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_pixel_threshold1_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_PIXEL_THRESHOLD1, - .name = "Pixel Threshold Region 1", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 20, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_motion_threshold1_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_MOTION_THRESHOLD1, - .name = "Motion Threshold Region 1", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 80, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_mb_threshold1_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_MB_THRESHOLD1, - .name = "MB Threshold Region 1", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 200, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_pixel_threshold2_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_PIXEL_THRESHOLD2, - .name = "Pixel Threshold Region 2", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 20, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_motion_threshold2_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_MOTION_THRESHOLD2, - .name = "Motion Threshold Region 2", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 80, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_mb_threshold2_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_MB_THRESHOLD2, - .name = "MB Threshold Region 2", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 200, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_pixel_threshold3_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_PIXEL_THRESHOLD3, - .name = "Pixel Threshold Region 3", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 20, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_motion_threshold3_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_MOTION_THRESHOLD3, - .name = "Motion Threshold Region 3", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 80, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_mb_threshold3_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_MB_THRESHOLD3, - .name = "MB Threshold Region 3", - .type = V4L2_CTRL_TYPE_INTEGER, - .def = 200, - .max = 32767, - .step = 1, -}; - -static const struct v4l2_ctrl_config go7007_mb_regions_ctrl = { - .ops = &go7007_ctrl_ops, - .id = V4L2_CID_DETECT_MD_REGION_GRID, - .dims = { 576 / 16, 720 / 16 }, - .max = 3, - .step = 1, -}; - -int go7007_v4l2_ctrl_init(struct go7007 *go) -{ - struct v4l2_ctrl_handler *hdl = &go->hdl; - struct v4l2_ctrl *ctrl; - - v4l2_ctrl_handler_init(hdl, 22); - go->mpeg_video_gop_size = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, 34, 1, 15); - go->mpeg_video_gop_closure = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1); - go->mpeg_video_bitrate = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_MPEG_VIDEO_BITRATE, - 64000, 10000000, 1, 9800000); - go->mpeg_video_b_frames = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 2, 2, 0); - go->mpeg_video_rep_seqheader = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, 0, 1, 1, 1); - - go->mpeg_video_aspect_ratio = v4l2_ctrl_new_std_menu(hdl, NULL, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_MPEG_VIDEO_ASPECT_16x9, 0, - V4L2_MPEG_VIDEO_ASPECT_1x1); - ctrl = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_JPEG_ACTIVE_MARKER, 0, - V4L2_JPEG_ACTIVE_MARKER_DQT | - V4L2_JPEG_ACTIVE_MARKER_DHT, 0, - V4L2_JPEG_ACTIVE_MARKER_DQT | - V4L2_JPEG_ACTIVE_MARKER_DHT); - if (ctrl) - ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold0_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold0_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold0_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold1_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold1_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold1_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold2_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold2_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold2_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold3_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold3_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold3_ctrl, NULL); - v4l2_ctrl_new_custom(hdl, &go7007_mb_regions_ctrl, NULL); - go->modet_mode = v4l2_ctrl_new_std_menu(hdl, NULL, - V4L2_CID_DETECT_MD_MODE, - V4L2_DETECT_MD_MODE_REGION_GRID, - 1 << V4L2_DETECT_MD_MODE_THRESHOLD_GRID, - V4L2_DETECT_MD_MODE_DISABLED); - if (hdl->error) { - int rv = hdl->error; - - v4l2_err(&go->v4l2_dev, "Could not register controls\n"); - return rv; - } - go->v4l2_dev.ctrl_handler = hdl; - return 0; -} - -int go7007_v4l2_init(struct go7007 *go) -{ - struct video_device *vdev = &go->vdev; - int rv; - - mutex_init(&go->serialize_lock); - mutex_init(&go->queue_lock); - - INIT_LIST_HEAD(&go->vidq_active); - go->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - go->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - go->vidq.ops = &go7007_video_qops; - go->vidq.mem_ops = &vb2_vmalloc_memops; - go->vidq.drv_priv = go; - go->vidq.buf_struct_size = sizeof(struct go7007_buffer); - go->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - go->vidq.lock = &go->queue_lock; - rv = vb2_queue_init(&go->vidq); - if (rv) - return rv; - *vdev = go7007_template; - vdev->lock = &go->serialize_lock; - vdev->queue = &go->vidq; - video_set_drvdata(vdev, go); - vdev->v4l2_dev = &go->v4l2_dev; - if (!v4l2_device_has_op(&go->v4l2_dev, video, querystd)) - v4l2_disable_ioctl(vdev, VIDIOC_QUERYSTD); - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) { - v4l2_disable_ioctl(vdev, VIDIOC_S_FREQUENCY); - v4l2_disable_ioctl(vdev, VIDIOC_G_FREQUENCY); - v4l2_disable_ioctl(vdev, VIDIOC_S_TUNER); - v4l2_disable_ioctl(vdev, VIDIOC_G_TUNER); - } else { - struct v4l2_frequency f = { - .type = V4L2_TUNER_ANALOG_TV, - .frequency = 980, - }; - - call_all(&go->v4l2_dev, tuner, s_frequency, &f); - } - if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV)) { - v4l2_disable_ioctl(vdev, VIDIOC_G_STD); - v4l2_disable_ioctl(vdev, VIDIOC_S_STD); - vdev->tvnorms = 0; - } - if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) - v4l2_disable_ioctl(vdev, VIDIOC_ENUM_FRAMESIZES); - if (go->board_info->num_aud_inputs == 0) { - v4l2_disable_ioctl(vdev, VIDIOC_G_AUDIO); - v4l2_disable_ioctl(vdev, VIDIOC_S_AUDIO); - v4l2_disable_ioctl(vdev, VIDIOC_ENUMAUDIO); - } - /* Setup correct crystal frequency on this board */ - if (go->board_info->sensor_flags & GO7007_SENSOR_SAA7115) - v4l2_subdev_call(go->sd_video, video, s_crystal_freq, - SAA7115_FREQ_24_576_MHZ, - SAA7115_FREQ_FL_APLL | SAA7115_FREQ_FL_UCGC | - SAA7115_FREQ_FL_DOUBLE_ASCLK); - go7007_s_input(go); - if (go->board_info->sensor_flags & GO7007_SENSOR_TV) - go7007_s_std(go); - rv = video_register_device(vdev, VFL_TYPE_GRABBER, -1); - if (rv < 0) - return rv; - dev_info(go->dev, "registered device %s [v4l2]\n", - video_device_node_name(vdev)); - - return 0; -} - -void go7007_v4l2_remove(struct go7007 *go) -{ - v4l2_ctrl_handler_free(&go->hdl); -} diff --git a/drivers/staging/media/go7007/go7007.txt b/drivers/staging/media/go7007/go7007.txt deleted file mode 100644 index c8e5eb0..0000000 --- a/drivers/staging/media/go7007/go7007.txt +++ /dev/null @@ -1,478 +0,0 @@ -This is a driver for the WIS GO7007SB multi-format video encoder. - -Pete Eberlein - -The driver was originally released under the GPL and is currently hosted at: -http://nikosapi.org/wiki/index.php/WIS_Go7007_Linux_driver -The go7007 firmware can be acquired from the package on the site above. - -I've modified the driver to support the following Video4Linux2 MPEG -controls, with acceptable values: - -V4L2_CID_MPEG_STREAM_TYPE V4L2_MPEG_STREAM_TYPE_MPEG2_DVD - V4L2_MPEG_STREAM_TYPE_MPEG_ELEM -V4L2_CID_MPEG_VIDEO_ENCODING V4L2_MPEG_VIDEO_ENCODING_MPEG_1 - V4L2_MPEG_VIDEO_ENCODING_MPEG_2 - V4L2_MPEG_VIDEO_ENCODING_MPEG_4 -V4L2_CID_MPEG_VIDEO_ASPECT V4L2_MPEG_VIDEO_ASPECT_1x1 - V4L2_MPEG_VIDEO_ASPECT_4x3 - V4L2_MPEG_VIDEO_ASPECT_16x9 -V4L2_CID_MPEG_VIDEO_GOP_SIZE integer -V4L2_CID_MPEG_VIDEO_BITRATE 64000 .. 10000000 - -These should be used instead of the non-standard GO7007 ioctls described -below. - - -The README files from the orignal package appear below: - ---------------------------------------------------------------------------- - WIS GO7007SB Public Linux Driver ---------------------------------------------------------------------------- - - -*** Please see the file RELEASE-NOTES for important last-minute updates *** - - - 0. OVERVIEW AND LICENSING/DISCLAIMER - - -This driver kit contains Linux drivers for the WIS GO7007SB multi-format -video encoder. Only kernel version 2.6.x is supported. The video stream -is available through the Video4Linux2 API and the audio stream is available -through the ALSA API (or the OSS emulation layer of the ALSA system). - -The files in kernel/ and hotplug/ are licensed under the GNU General Public -License Version 2 from the Free Software Foundation. A copy of the license -is included in the file COPYING. - -The example applications in apps/ and C header files in include/ are -licensed under a permissive license included in the source files which -allows copying, modification and redistribution for any purpose without -attribution. - -The firmware files included in the firmware/ directory may be freely -redistributed only in conjunction with this document; but modification, -tampering and reverse engineering are prohibited. - -MICRONAS USA, INC., MAKES NO WARRANTIES TO ANY PERSON OR ENTITY WITH -RESPECT TO THE SOFTWARE OR ANY DERIVATIVES THEREOF OR ANY SERVICES OR -LICENSES AND DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING WITHOUT LIMITATION -WARRANTIES OF MERCHANTABILITY, SUPPORT, AND FITNESS FOR A PARTICULAR -PURPOSE AND NON-INFRINGEMENT. - - - 1. SYSTEM REQUIREMENTS - - -This driver requires Linux kernel 2.6. Kernel 2.4 is not supported. Using -kernel 2.6.10 or later is recommended, as earlier kernels are known to have -unstable USB 2.0 support. - -A fully built kernel source tree must be available. Typically this will be -linked from "/lib/modules//build" for convenience. If this -link does not exist, an extra parameter will need to be passed to the -`make` command. - -All vendor-built kernels should already be configured properly. However, -for custom-built kernels, the following options need to be enabled in the -kernel as built-in or modules: - - CONFIG_MODULES - Enable loadable module support - CONFIG_FW_LOADER - Hotplug firmware loading support - CONFIG_I2C - I2C support - CONFIG_VIDEO_DEV - Video For Linux - CONFIG_SOUND - Sound card support - CONFIG_SND - Advanced Linux Sound Architecture - CONFIG_USB - Support for Host-side USB - CONFIG_USB_EHCI_HCD - EHCI HCD (USB 2.0) support - -Additionally, to use the example application, the following options need to -be enabled in the ALSA section: - - CONFIG_SND_MIXER_OSS - OSS Mixer API - CONFIG_SND_PCM_OSS - OSS PCM (digital audio) API - -The hotplug scripts, along with the fxload utility, must also be installed. -These scripts can be obtained from . -Hotplugging is used for loading firmware into the Cypruss EZ-USB chip using -fxload and for loading firmware into the driver using the firmware agent. - - - 2. COMPILING AND INSTALLING THE DRIVER - - -Most users should be able to compile the driver by simply running: - - $ make - -in the top-level directory of the driver kit. First the kernel modules -will be built, followed by the example applications. - -If the build system is unable to locate the kernel source tree for the -currently-running kernel, or if the module should be built for a kernel -other than the currently-running kernel, an additional parameter will need -to be passed to make to specify the appropriate kernel source directory: - - $ make KERNELSRC=/usr/src/linux-2.6.10-custom3 - -Once the compile completes, the driver and firmware files should be -installed by running: - - $ make install - -The kernel modules will be placed in "/lib/modules//extra" -and the firmware files will be placed in the appropriate hotplug firmware -directory, usually /lib/firmware. In addition, USB maps and scripts will -be placed in /etc/hotplug/usb to enable fxload to initialize the EZ-USB -control chip when the device is connected. - - - 3. PAL/SECAM TUNER CONFIGURATION (TV402U-EU only) - - -The PAL model of the Plextor ConvertX TV402U may require additional -configuration to correctly select the appropriate TV frequency band and -audio subchannel. - -Users with a device other than the Plextor ConvertX TV402U-EU should skip -this section. - -The wide variety of PAL TV systems used in Europe requires that additional -information about the local TV standards be passed to the driver in order -to properly tune TV channels. The two necessary parameters are (a) the PAL -TV band, and (b) the audio subchannel format in use. - -In many cases, the appropriate TV band selection is passed to the driver -from applications. However, in some cases, the application only specifies -that the driver should use PAL but not the specific information about the -appropriate TV band. To work around this issue, the correct TV band may be -specified in the "force_band" parameter to the wis-sony-tuner module: - - TV band force_band - ------- ---------- - PAL B/G B - PAL I I - PAL D/K D - SECAM L L - -If the "force_band" parameter is specified, the driver will ignore any TV -band specified by applications and will always use the band provided in the -module parameter. - -The other parameter that can be specified is the audio subchannel format. -There are several stereo audio carrier systems in use, including NICAM and -three varieties of A2. To receive audio broadcast on one of these stereo -carriers, the "force_mpx_mode" parameter must be specified to the -wis-sony-tuner module. - - TV band Audio subcarrier force_mpx_mode - ------- ---------------- -------------- - PAL B/G Mono (default) 1 - PAL B/G A2 2 - PAL B/G NICAM 3 - PAL I Mono (default) 4 - PAL I NICAM 5 - PAL D/K Mono (default) 6 - PAL D/K A2 (1) 7 - PAL D/K A2 (2) 8 - PAL D/K A2 (3) 9 - PAL D/K NICAM 10 - SECAM L Mono (default) 11 - SECAM L NICAM 12 - -If the "force_mpx_mode" parameter is not specified, the correct mono-only -mode will be chosen based on the TV band. However, the tuner will not -receive stereo audio or bilingual broadcasts correctly. - -To pass the "force_band" or "force_mpx_mode" parameters to the -wis-sony-tuner module, the following line must be added to the modprobe -configuration file, which varies from one Linux distribution to another. - - options wis-sony-tuner force_band=B force_mpx_mode=2 - -The above example would force the tuner to the PAL B/G TV band and receive -stereo audio broadcasts on the A2 carrier. - -To verify that the configuration has been placed in the correct location, -execute: - - $ modprobe -c | grep wis-sony-tuner - -If the configuration line appears, then modprobe will pass the parameters -correctly the next time the wis-sony-tuner module is loaded into the -kernel. - - - 4. TESTING THE DRIVER - - -Because few Linux applications are able to correctly capture from -Video4Linux2 devices with only compressed formats supported, the new driver -should be tested with the "gorecord" application in the apps/ directory. - -First connect a video source to the device, such as a DVD player or VCR. -This will be captured to a file for testing the driver. If an input source -is unavailable, a test file can still be captured, but the video will be -black and the audio will be silent. - -This application will auto-detect the V4L2 and ALSA/OSS device names of the -hardware and will record video and audio to an AVI file for a specified -number of seconds. For example: - - $ apps/gorecord -duration 60 capture.avi - -If this application does not successfully record an AVI file, the error -messages produced by gorecord and recorded in the system log (usually in -/var/log/messages) should provide information to help resolve the problem. - -Supplying no parameters to gorecord will cause it to probe the available -devices and exit. Use the -help flag for usage information. - - - 5. USING THE DRIVER - - -The V4L2 device implemented by the driver provides a standard compressed -format API, within the following criteria: - - * Applications that only support the original Video4Linux1 API will not - be able to communicate with this driver at all. - - * No raw video modes are supported, so applications like xawtv that - expect only uncompressed video will not function. - - * Supported compression formats are: Motion-JPEG, MPEG1, MPEG2 and MPEG4. - - * MPEG video formats are delivered as Video Elementary Streams only. - Program Stream (PS), Transport Stream (TS) and Packetized Elementary - Stream (PES) formats are not supported. - - * Video parameters such as format and input port may not be changed while - the encoder is active. - - * The audio capture device only functions when the video encoder is - actively capturing video. Attempts to read from the audio device when - the encoder is inactive will result in an I/O error. - - * The native format of the audio device is 48Khz 2-channel 16-bit - little-endian PCM, delivered through the ALSA system. No audio - compression is implemented in the hardware. ALSA may convert to other - uncompressed formats on the fly. - -The include/ directory contains a C header file describing non-standard -features of the GO7007SB encoder, which are described below: - - - GO7007IOC_S_COMP_PARAMS, GO7007IOC_G_COMP_PARAMS - - These ioctls are used to negotiate general compression parameters. - - To query the current parameters, call the GO7007IOC_G_COMP_PARAMS ioctl - with a pointer to a struct go7007_comp_params. If the driver is not - set to MPEG format, the EINVAL error code will be returned. - - To change the current parameters, initialize all fields of a struct - go7007_comp_params and call the GO7007_IOC_S_COMP_PARAMS ioctl with a - pointer to this structure. The driver will return the current - parameters with any necessary changes to conform to the limitations of - the hardware or current compression mode. Any or all fields can be set - to zero to request a reasonable default value. If the driver is not - set to MPEG format, the EINVAL error code will be returned. When I/O - is in progress, the EBUSY error code will be returned. - - Fields in struct go7007_comp_params: - - __u32 The maximum number of frames in each - gop_size Group Of Pictures; i.e. the maximum - number of frames minus one between - each key frame. - - __u32 The maximum number of sequential - max_b_frames bidirectionally-predicted frames. - (B-frames are not yet supported.) - - enum go7007_aspect_ratio The aspect ratio to be encoded in the - aspect_ratio meta-data of the compressed format. - - Choices are: - GO7007_ASPECT_RATIO_1_1 - GO7007_ASPECT_RATIO_4_3_NTSC - GO7007_ASPECT_RATIO_4_3_PAL - GO7007_ASPECT_RATIO_16_9_NTSC - GO7007_ASPECT_RATIO_16_9_PAL - - __u32 Bit-wise OR of control flags (below) - flags - - Flags in struct go7007_comp_params: - - GO7007_COMP_CLOSED_GOP Only produce self-contained GOPs, used - to produce streams appropriate for - random seeking. - - GO7007_COMP_OMIT_SEQ_HEADER Omit the stream sequence header. - - - GO7007IOC_S_MPEG_PARAMS, GO7007IOC_G_MPEG_PARAMS - - These ioctls are used to negotiate MPEG-specific stream parameters when - the pixelformat has been set to V4L2_PIX_FMT_MPEG. - - To query the current parameters, call the GO7007IOC_G_MPEG_PARAMS ioctl - with a pointer to a struct go7007_mpeg_params. If the driver is not - set to MPEG format, the EINVAL error code will be returned. - - To change the current parameters, initialize all fields of a struct - go7007_mpeg_params and call the GO7007_IOC_S_MPEG_PARAMS ioctl with a - pointer to this structure. The driver will return the current - parameters with any necessary changes to conform to the limitations of - the hardware or selected MPEG mode. Any or all fields can be set to - zero to request a reasonable default value. If the driver is not set - to MPEG format, the EINVAL error code will be returned. When I/O is in - progress, the EBUSY error code will be returned. - - Fields in struct go7007_mpeg_params: - - enum go7007_mpeg_video_standard - mpeg_video_standard The MPEG video standard in which to - compress the video. - - Choices are: - GO7007_MPEG_VIDEO_MPEG1 - GO7007_MPEG_VIDEO_MPEG2 - GO7007_MPEG_VIDEO_MPEG4 - - __u32 Bit-wise OR of control flags (below) - flags - - __u32 The profile and level indication to be - pali stored in the sequence header. This - is only used as an indicator to the - decoder, and does not affect the MPEG - features used in the video stream. - Not valid for MPEG1. - - Choices for MPEG2 are: - GO7007_MPEG2_PROFILE_MAIN_MAIN - - Choices for MPEG4 are: - GO7007_MPEG4_PROFILE_S_L0 - GO7007_MPEG4_PROFILE_S_L1 - GO7007_MPEG4_PROFILE_S_L2 - GO7007_MPEG4_PROFILE_S_L3 - GO7007_MPEG4_PROFILE_ARTS_L1 - GO7007_MPEG4_PROFILE_ARTS_L2 - GO7007_MPEG4_PROFILE_ARTS_L3 - GO7007_MPEG4_PROFILE_ARTS_L4 - GO7007_MPEG4_PROFILE_AS_L0 - GO7007_MPEG4_PROFILE_AS_L1 - GO7007_MPEG4_PROFILE_AS_L2 - GO7007_MPEG4_PROFILE_AS_L3 - GO7007_MPEG4_PROFILE_AS_L4 - GO7007_MPEG4_PROFILE_AS_L5 - - Flags in struct go7007_mpeg_params: - - GO7007_MPEG_FORCE_DVD_MODE Force all compression parameters and - bitrate control settings to comply - with DVD MPEG2 stream requirements. - This overrides most compression and - bitrate settings! - - GO7007_MPEG_OMIT_GOP_HEADER Omit the GOP header. - - GO7007_MPEG_REPEAT_SEQHEADER Repeat the MPEG sequence header at - the start of each GOP. - - - GO7007IOC_S_BITRATE, GO7007IOC_G_BITRATE - - These ioctls are used to set and query the target bitrate value for the - compressed video stream. The bitrate may be selected by storing the - target bits per second in an int and calling GO7007IOC_S_BITRATE with a - pointer to the int. The bitrate may be queried by calling - GO7007IOC_G_BITRATE with a pointer to an int where the current bitrate - will be stored. - - Note that this is the primary means of controlling the video quality - for all compression modes, including V4L2_PIX_FMT_MJPEG. The - VIDIOC_S_JPEGCOMP ioctl is not supported. - - ----------------------------------------------------------------------------- - Installing the WIS PCI Voyager Driver ---------------------------------------------------------------------------- - -The WIS PCI Voyager driver requires several patches to the Linux 2.6.11.x -kernel source tree before compiling the driver. These patches update the -in-kernel SAA7134 driver to the newest development version and patch bugs -in the TDA8290/TDA8275 tuner driver. - -The following patches must be downloaded from Gerd Knorr's website and -applied in the order listed: - - http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner - http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner2 - http://dl.bytesex.org/patches/2.6.11-2/v4l2-api-mpeg - http://dl.bytesex.org/patches/2.6.11-2/saa7134-update - -The following patches are included with this SDK and can be applied in any -order: - - patches/2.6.11/saa7134-voyager.diff - patches/2.6.11/tda8275-newaddr.diff - patches/2.6.11/tda8290-ntsc.diff - -Check to make sure the CONFIG_VIDEO_SAA7134 option is enabled in the kernel -configuration, and build and install the kernel. - -After rebooting into the new kernel, the GO7007 driver can be compiled and -installed: - - $ make SAA7134_BUILD=y - $ make install - $ modprobe saa7134-go7007 - -There will be two V4L video devices associated with the PCI Voyager. The -first device (most likely /dev/video0) provides access to the raw video -capture mode of the SAA7133 device and is used to configure the source -video parameters and tune the TV tuner. This device can be used with xawtv -or other V4L(2) video software as a standard uncompressed device. - -The second device (most likely /dev/video1) provides access to the -compression functions of the GO7007. It can be tested using the gorecord -application in the apps/ directory of this SDK: - - $ apps/gorecord -vdevice /dev/video1 -noaudio test.avi - -Currently the frame resolution is fixed at 720x480 (NTSC) or 720x576 (PAL), -and the video standard must be specified to both the raw and the compressed -video devices (xawtv and gorecord, for example). - - --------------------------------------------------------------------------- -RELEASE NOTES FOR WIS GO7007SB LINUX DRIVER ---------------------------------------------------------------------------- - -Last updated: 5 November 2005 - - - Release 0.9.7 includes new support for using udev to run fxload. The - install script should automatically detect whether the old hotplug - scripts or the new udev rules should be used. To force the use of - hotplug, run "make install USE_UDEV=n". To force the use of udev, run - "make install USE_UDEV=y". - - - Motion detection is supported but undocumented. Try the `modet` app - for a demonstration of how to use the facility. - - - Using USB2.0 devices such as the TV402U with USB1.1 HCDs or hubs can - cause buffer overruns and frame drops, even at low framerates, due to - inconsistency in the bitrate control mechanism. - - - On devices with an SAA7115, including the Plextor ConvertX, video height - values of 96, 128, 160, 192, 256, 320, and 384 do not work in NTSC mode. - All valid heights up to 512 work correctly in PAL mode. - - - The WIS Star Trek and PCI Voyager boards have no support yet for audio - or the TV tuner. diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c deleted file mode 100644 index bb84668..0000000 --- a/drivers/staging/media/go7007/s2250-board.c +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Copyright (C) 2008 Sensoray Company Inc. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "go7007-priv.h" - -MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver"); -MODULE_LICENSE("GPL v2"); - -/* - * Note: this board has two i2c devices: a vpx3226f and a tlv320aic23b. - * Due to the unusual way these are accessed on this device we do not - * reuse the i2c drivers, but instead they are implemented in this - * driver. It would be nice to improve on this, though. - */ - -#define TLV320_ADDRESS 0x34 -#define VPX322_ADDR_ANALOGCONTROL1 0x02 -#define VPX322_ADDR_BRIGHTNESS0 0x0127 -#define VPX322_ADDR_BRIGHTNESS1 0x0131 -#define VPX322_ADDR_CONTRAST0 0x0128 -#define VPX322_ADDR_CONTRAST1 0x0132 -#define VPX322_ADDR_HUE 0x00dc -#define VPX322_ADDR_SAT 0x0030 - -struct go7007_usb_board { - unsigned int flags; - struct go7007_board_info main_info; -}; - -struct go7007_usb { - struct go7007_usb_board *board; - struct mutex i2c_lock; - struct usb_device *usbdev; - struct urb *video_urbs[8]; - struct urb *audio_urbs[8]; - struct urb *intr_urb; -}; - -static unsigned char aud_regs[] = { - 0x1e, 0x00, - 0x00, 0x17, - 0x02, 0x17, - 0x04, 0xf9, - 0x06, 0xf9, - 0x08, 0x02, - 0x0a, 0x00, - 0x0c, 0x00, - 0x0a, 0x00, - 0x0c, 0x00, - 0x0e, 0x02, - 0x10, 0x00, - 0x12, 0x01, - 0x00, 0x00, -}; - - -static unsigned char vid_regs[] = { - 0xF2, 0x0f, - 0xAA, 0x00, - 0xF8, 0xff, - 0x00, 0x00, -}; - -static u16 vid_regs_fp[] = { - 0x028, 0x067, - 0x120, 0x016, - 0x121, 0xcF2, - 0x122, 0x0F2, - 0x123, 0x00c, - 0x124, 0x2d0, - 0x125, 0x2e0, - 0x126, 0x004, - 0x128, 0x1E0, - 0x12A, 0x016, - 0x12B, 0x0F2, - 0x12C, 0x0F2, - 0x12D, 0x00c, - 0x12E, 0x2d0, - 0x12F, 0x2e0, - 0x130, 0x004, - 0x132, 0x1E0, - 0x140, 0x060, - 0x153, 0x00C, - 0x154, 0x200, - 0x150, 0x801, - 0x000, 0x000 -}; - -/* PAL specific values */ -static u16 vid_regs_fp_pal[] = { - 0x120, 0x017, - 0x121, 0xd22, - 0x122, 0x122, - 0x12A, 0x017, - 0x12B, 0x122, - 0x12C, 0x122, - 0x140, 0x060, - 0x000, 0x000, -}; - -struct s2250 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - v4l2_std_id std; - int input; - int brightness; - int contrast; - int saturation; - int hue; - int reg12b_val; - int audio_input; - struct i2c_client *audio; -}; - -static inline struct s2250 *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct s2250, sd); -} - -/* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ -static int go7007_usb_vendor_request(struct go7007 *go, u16 request, - u16 value, u16 index, void *transfer_buffer, int length, int in) -{ - struct go7007_usb *usb = go->hpi_context; - int timeout = 5000; - - if (in) { - return usb_control_msg(usb->usbdev, - usb_rcvctrlpipe(usb->usbdev, 0), request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - value, index, transfer_buffer, length, timeout); - } else { - return usb_control_msg(usb->usbdev, - usb_sndctrlpipe(usb->usbdev, 0), request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, transfer_buffer, length, timeout); - } -} -/* end from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ - -static int write_reg(struct i2c_client *client, u8 reg, u8 value) -{ - struct go7007 *go = i2c_get_adapdata(client->adapter); - struct go7007_usb *usb; - int rc; - int dev_addr = client->addr << 1; /* firmware wants 8-bit address */ - u8 *buf; - - if (go == NULL) - return -ENODEV; - - if (go->status == STATUS_SHUTDOWN) - return -EBUSY; - - buf = kzalloc(16, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - usb = go->hpi_context; - if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - dev_info(&client->dev, "i2c lock failed\n"); - kfree(buf); - return -EINTR; - } - rc = go7007_usb_vendor_request(go, 0x55, dev_addr, - (reg<<8 | value), - buf, - 16, 1); - - mutex_unlock(&usb->i2c_lock); - kfree(buf); - return rc; -} - -static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) -{ - struct go7007 *go = i2c_get_adapdata(client->adapter); - struct go7007_usb *usb; - int rc; - u8 *buf; - struct s2250 *dec = i2c_get_clientdata(client); - - if (go == NULL) - return -ENODEV; - - if (go->status == STATUS_SHUTDOWN) - return -EBUSY; - - buf = kzalloc(16, GFP_KERNEL); - - if (buf == NULL) - return -ENOMEM; - - - - memset(buf, 0xcd, 6); - - usb = go->hpi_context; - if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - dev_info(&client->dev, "i2c lock failed\n"); - kfree(buf); - return -EINTR; - } - rc = go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1); - mutex_unlock(&usb->i2c_lock); - if (rc < 0) { - kfree(buf); - return rc; - } - - if (buf[0] == 0) { - unsigned int subaddr, val_read; - - subaddr = (buf[4] << 8) + buf[5]; - val_read = (buf[2] << 8) + buf[3]; - kfree(buf); - if (val_read != val) { - dev_info(&client->dev, "invalid fp write %x %x\n", - val_read, val); - return -EFAULT; - } - if (subaddr != addr) { - dev_info(&client->dev, "invalid fp write addr %x %x\n", - subaddr, addr); - return -EFAULT; - } - } else { - kfree(buf); - return -EFAULT; - } - - /* save last 12b value */ - if (addr == 0x12b) - dec->reg12b_val = val; - - return 0; -} - -static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) -{ - struct go7007 *go = i2c_get_adapdata(client->adapter); - struct go7007_usb *usb; - int rc; - u8 *buf; - - if (go == NULL) - return -ENODEV; - - if (go->status == STATUS_SHUTDOWN) - return -EBUSY; - - buf = kzalloc(16, GFP_KERNEL); - - if (buf == NULL) - return -ENOMEM; - - - - memset(buf, 0xcd, 6); - usb = go->hpi_context; - if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - dev_info(&client->dev, "i2c lock failed\n"); - kfree(buf); - return -EINTR; - } - rc = go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1); - mutex_unlock(&usb->i2c_lock); - if (rc < 0) { - kfree(buf); - return rc; - } - - *val = (buf[0] << 8) | buf[1]; - kfree(buf); - - return 0; -} - - -static int write_regs(struct i2c_client *client, u8 *regs) -{ - int i; - - for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { - if (write_reg(client, regs[i], regs[i+1]) < 0) { - dev_info(&client->dev, "failed\n"); - return -1; - } - } - return 0; -} - -static int write_regs_fp(struct i2c_client *client, u16 *regs) -{ - int i; - - for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { - if (write_reg_fp(client, regs[i], regs[i+1]) < 0) { - dev_info(&client->dev, "failed fp\n"); - return -1; - } - } - return 0; -} - - -/* ------------------------------------------------------------------------- */ - -static int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, - u32 config) -{ - struct s2250 *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int vidsys; - - vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00; - if (input == 0) { - /* composite */ - write_reg_fp(client, 0x20, 0x020 | vidsys); - write_reg_fp(client, 0x21, 0x662); - write_reg_fp(client, 0x140, 0x060); - } else if (input == 1) { - /* S-Video */ - write_reg_fp(client, 0x20, 0x040 | vidsys); - write_reg_fp(client, 0x21, 0x666); - write_reg_fp(client, 0x140, 0x060); - } else { - return -EINVAL; - } - state->input = input; - return 0; -} - -static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - struct s2250 *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - u16 vidsource; - - vidsource = (state->input == 1) ? 0x040 : 0x020; - if (norm & V4L2_STD_625_50) { - write_regs_fp(client, vid_regs_fp); - write_regs_fp(client, vid_regs_fp_pal); - write_reg_fp(client, 0x20, vidsource); - } else { - write_regs_fp(client, vid_regs_fp); - write_reg_fp(client, 0x20, vidsource | 1); - } - state->std = norm; - return 0; -} - -static int s2250_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct s2250 *state = container_of(ctrl->handler, struct s2250, hdl); - struct i2c_client *client = v4l2_get_subdevdata(&state->sd); - u16 oldvalue; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue); - write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, - ctrl->val | (oldvalue & ~0xff)); - read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue); - write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, - ctrl->val | (oldvalue & ~0xff)); - write_reg_fp(client, 0x140, 0x60); - break; - case V4L2_CID_CONTRAST: - read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue); - write_reg_fp(client, VPX322_ADDR_CONTRAST0, - ctrl->val | (oldvalue & ~0x3f)); - read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue); - write_reg_fp(client, VPX322_ADDR_CONTRAST1, - ctrl->val | (oldvalue & ~0x3f)); - write_reg_fp(client, 0x140, 0x60); - break; - case V4L2_CID_SATURATION: - write_reg_fp(client, VPX322_ADDR_SAT, ctrl->val); - break; - case V4L2_CID_HUE: - write_reg_fp(client, VPX322_ADDR_HUE, ctrl->val); - break; - default: - return -EINVAL; - } - return 0; -} - -static int s2250_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct s2250 *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (fmt->height < 640) { - write_reg_fp(client, 0x12b, state->reg12b_val | 0x400); - write_reg_fp(client, 0x140, 0x060); - } else { - write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400); - write_reg_fp(client, 0x140, 0x060); - } - return 0; -} - -static int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, - u32 config) -{ - struct s2250 *state = to_state(sd); - - switch (input) { - case 0: - write_reg(state->audio, 0x08, 0x02); /* Line In */ - break; - case 1: - write_reg(state->audio, 0x08, 0x04); /* Mic */ - break; - case 2: - write_reg(state->audio, 0x08, 0x05); /* Mic Boost */ - break; - default: - return -EINVAL; - } - state->audio_input = input; - return 0; -} - - -static int s2250_log_status(struct v4l2_subdev *sd) -{ - struct s2250 *state = to_state(sd); - - v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" : - state->std == V4L2_STD_PAL ? "PAL" : - state->std == V4L2_STD_SECAM ? "SECAM" : - "unknown"); - v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" : - state->input == 1 ? "S-video" : - "error"); - v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" : - state->audio_input == 1 ? "Mic" : - state->audio_input == 2 ? "Mic Boost" : - "error"); - return v4l2_ctrl_subdev_log_status(sd); -} - -/* --------------------------------------------------------------------------*/ - -static const struct v4l2_ctrl_ops s2250_ctrl_ops = { - .s_ctrl = s2250_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops s2250_core_ops = { - .log_status = s2250_log_status, -}; - -static const struct v4l2_subdev_audio_ops s2250_audio_ops = { - .s_routing = s2250_s_audio_routing, -}; - -static const struct v4l2_subdev_video_ops s2250_video_ops = { - .s_std = s2250_s_std, - .s_routing = s2250_s_video_routing, - .s_mbus_fmt = s2250_s_mbus_fmt, -}; - -static const struct v4l2_subdev_ops s2250_ops = { - .core = &s2250_core_ops, - .audio = &s2250_audio_ops, - .video = &s2250_video_ops, -}; - -/* --------------------------------------------------------------------------*/ - -static int s2250_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_client *audio; - struct i2c_adapter *adapter = client->adapter; - struct s2250 *state; - struct v4l2_subdev *sd; - u8 *data; - struct go7007 *go = i2c_get_adapdata(adapter); - struct go7007_usb *usb = go->hpi_context; - - audio = i2c_new_dummy(adapter, TLV320_ADDRESS >> 1); - if (audio == NULL) - return -ENOMEM; - - state = kzalloc(sizeof(struct s2250), GFP_KERNEL); - if (state == NULL) { - i2c_unregister_device(audio); - return -ENOMEM; - } - - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &s2250_ops); - - v4l2_info(sd, "initializing %s at address 0x%x on %s\n", - "Sensoray 2250/2251", client->addr, client->adapter->name); - - v4l2_ctrl_handler_init(&state->hdl, 4); - v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, - V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, - V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x32); - v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, - V4L2_CID_SATURATION, 0, 4094, 1, 2070); - v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, - V4L2_CID_HUE, -512, 511, 1, 0); - sd->ctrl_handler = &state->hdl; - if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return err; - } - - state->std = V4L2_STD_NTSC; - state->brightness = 50; - state->contrast = 50; - state->saturation = 50; - state->hue = 0; - state->audio = audio; - - /* initialize the audio */ - if (write_regs(audio, aud_regs) < 0) { - dev_err(&client->dev, "error initializing audio\n"); - goto fail; - } - - if (write_regs(client, vid_regs) < 0) { - dev_err(&client->dev, "error initializing decoder\n"); - goto fail; - } - if (write_regs_fp(client, vid_regs_fp) < 0) { - dev_err(&client->dev, "error initializing decoder\n"); - goto fail; - } - /* set default channel */ - /* composite */ - write_reg_fp(client, 0x20, 0x020 | 1); - write_reg_fp(client, 0x21, 0x662); - write_reg_fp(client, 0x140, 0x060); - - /* set default audio input */ - state->audio_input = 0; - write_reg(client, 0x08, 0x02); /* Line In */ - - if (mutex_lock_interruptible(&usb->i2c_lock) == 0) { - data = kzalloc(16, GFP_KERNEL); - if (data != NULL) { - int rc = go7007_usb_vendor_request(go, 0x41, 0, 0, - data, 16, 1); - - if (rc > 0) { - u8 mask; - - data[0] = 0; - mask = 1<<5; - data[0] &= ~mask; - data[1] |= mask; - go7007_usb_vendor_request(go, 0x40, 0, - (data[1]<<8) - + data[1], - data, 16, 0); - } - kfree(data); - } - mutex_unlock(&usb->i2c_lock); - } - - v4l2_info(sd, "initialized successfully\n"); - return 0; - -fail: - i2c_unregister_device(audio); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return -EIO; -} - -static int s2250_remove(struct i2c_client *client) -{ - struct s2250 *state = to_state(i2c_get_clientdata(client)); - - v4l2_device_unregister_subdev(&state->sd); - v4l2_ctrl_handler_free(&state->hdl); - kfree(state); - return 0; -} - -static const struct i2c_device_id s2250_id[] = { - { "s2250", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, s2250_id); - -static struct i2c_driver s2250_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "s2250", - }, - .probe = s2250_probe, - .remove = s2250_remove, - .id_table = s2250_id, -}; - -module_i2c_driver(s2250_driver); diff --git a/drivers/staging/media/go7007/saa7134-go7007.c b/drivers/staging/media/go7007/saa7134-go7007.c deleted file mode 100644 index b137432..0000000 --- a/drivers/staging/media/go7007/saa7134-go7007.c +++ /dev/null @@ -1,560 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "saa7134.h" -#include "saa7134-reg.h" -#include "go7007-priv.h" - -/*#define GO7007_HPI_DEBUG*/ - -enum hpi_address { - HPI_ADDR_VIDEO_BUFFER = 0xe4, - HPI_ADDR_INIT_BUFFER = 0xea, - HPI_ADDR_INTR_RET_VALUE = 0xee, - HPI_ADDR_INTR_RET_DATA = 0xec, - HPI_ADDR_INTR_STATUS = 0xf4, - HPI_ADDR_INTR_WR_PARAM = 0xf6, - HPI_ADDR_INTR_WR_INDEX = 0xf8, -}; - -enum gpio_command { - GPIO_COMMAND_RESET = 0x00, /* 000b */ - GPIO_COMMAND_REQ1 = 0x04, /* 001b */ - GPIO_COMMAND_WRITE = 0x20, /* 010b */ - GPIO_COMMAND_REQ2 = 0x24, /* 011b */ - GPIO_COMMAND_READ = 0x80, /* 100b */ - GPIO_COMMAND_VIDEO = 0x84, /* 101b */ - GPIO_COMMAND_IDLE = 0xA0, /* 110b */ - GPIO_COMMAND_ADDR = 0xA4, /* 111b */ -}; - -struct saa7134_go7007 { - struct v4l2_subdev sd; - struct saa7134_dev *dev; - u8 *top; - u8 *bottom; - dma_addr_t top_dma; - dma_addr_t bottom_dma; -}; - -static inline struct saa7134_go7007 *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa7134_go7007, sd); -} - -static const struct go7007_board_info board_voyager = { - .flags = 0, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_VALID_ENABLE | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .num_inputs = 1, - .inputs = { - { - .name = "SAA7134", - }, - }, -}; - -/********************* Driver for GPIO HPI interface *********************/ - -static int gpio_write(struct saa7134_dev *dev, u8 addr, u16 data) -{ - saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); - - /* Write HPI address */ - saa_writeb(SAA7134_GPIO_GPSTATUS0, addr); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - /* Write low byte */ - saa_writeb(SAA7134_GPIO_GPSTATUS0, data & 0xff); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - /* Write high byte */ - saa_writeb(SAA7134_GPIO_GPSTATUS0, data >> 8); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - return 0; -} - -static int gpio_read(struct saa7134_dev *dev, u8 addr, u16 *data) -{ - saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); - - /* Write HPI address */ - saa_writeb(SAA7134_GPIO_GPSTATUS0, addr); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - saa_writeb(SAA7134_GPIO_GPMODE0, 0x00); - - /* Read low byte */ - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ); - saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - *data = saa_readb(SAA7134_GPIO_GPSTATUS0); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - /* Read high byte */ - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ); - saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - *data |= saa_readb(SAA7134_GPIO_GPSTATUS0) << 8; - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - return 0; -} - -static int saa7134_go7007_interface_reset(struct go7007 *go) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - u32 status; - u16 intr_val, intr_data; - int count = 20; - - saa_clearb(SAA7134_TS_PARALLEL, 0x80); /* Disable TS interface */ - saa_writeb(SAA7134_GPIO_GPMODE2, 0xa4); - saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); - - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_RESET); - msleep(1); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2); - msleep(10); - - saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - - status = saa_readb(SAA7134_GPIO_GPSTATUS2); - /*pr_debug("status is %s\n", status & 0x40 ? "OK" : "not OK"); */ - - /* enter command mode...(?) */ - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2); - - do { - saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - status = saa_readb(SAA7134_GPIO_GPSTATUS2); - /*pr_info("gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */ - } while (--count > 0); - - /* Wait for an interrupt to indicate successful hardware reset */ - if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || - (intr_val & ~0x1) != 0x55aa) { - pr_err("saa7134-go7007: unable to reset the GO7007\n"); - return -1; - } - return 0; -} - -static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - int i; - u16 status_reg; - -#ifdef GO7007_HPI_DEBUG - pr_debug("saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data); -#endif - - for (i = 0; i < 100; ++i) { - gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg); - if (!(status_reg & 0x0010)) - break; - msleep(10); - } - if (i == 100) { - pr_err("saa7134-go7007: device is hung, status reg = 0x%04x\n", - status_reg); - return -1; - } - gpio_write(dev, HPI_ADDR_INTR_WR_PARAM, data); - gpio_write(dev, HPI_ADDR_INTR_WR_INDEX, addr); - - return 0; -} - -static int saa7134_go7007_read_interrupt(struct go7007 *go) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - - /* XXX we need to wait if there is no interrupt available */ - go->interrupt_available = 1; - gpio_read(dev, HPI_ADDR_INTR_RET_VALUE, &go->interrupt_value); - gpio_read(dev, HPI_ADDR_INTR_RET_DATA, &go->interrupt_data); -#ifdef GO7007_HPI_DEBUG - pr_debug("saa7134-go7007: ReadInterrupt: %04x %04x\n", - go->interrupt_value, go->interrupt_data); -#endif - return 0; -} - -static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev, - unsigned long status) -{ - struct go7007 *go = video_get_drvdata(dev->empress_dev); - struct saa7134_go7007 *saa = go->hpi_context; - - if (!vb2_is_streaming(&go->vidq)) - return; - if (0 != (status & 0x000f0000)) - pr_debug("saa7134-go7007: irq: lost %ld\n", - (status >> 16) & 0x0f); - if (status & 0x100000) { - dma_sync_single_for_cpu(&dev->pci->dev, - saa->bottom_dma, PAGE_SIZE, DMA_FROM_DEVICE); - go7007_parse_video_stream(go, saa->bottom, PAGE_SIZE); - saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma)); - } else { - dma_sync_single_for_cpu(&dev->pci->dev, - saa->top_dma, PAGE_SIZE, DMA_FROM_DEVICE); - go7007_parse_video_stream(go, saa->top, PAGE_SIZE); - saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma)); - } -} - -static int saa7134_go7007_stream_start(struct go7007 *go) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - - saa->top_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->top), - 0, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(&dev->pci->dev, saa->top_dma)) - return -ENOMEM; - saa->bottom_dma = dma_map_page(&dev->pci->dev, - virt_to_page(saa->bottom), - 0, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(&dev->pci->dev, saa->bottom_dma)) { - dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE, - DMA_FROM_DEVICE); - return -ENOMEM; - } - - saa_writel(SAA7134_VIDEO_PORT_CTRL0 >> 2, 0xA300B000); - saa_writel(SAA7134_VIDEO_PORT_CTRL4 >> 2, 0x40000200); - - /* Set HPI interface for video */ - saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); - saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_VIDEO_BUFFER); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); - saa_writeb(SAA7134_GPIO_GPMODE0, 0x00); - - /* Enable TS interface */ - saa_writeb(SAA7134_TS_PARALLEL, 0xe6); - - /* Reset TS interface */ - saa_setb(SAA7134_TS_SERIAL1, 0x01); - saa_clearb(SAA7134_TS_SERIAL1, 0x01); - - /* Set up transfer block size */ - saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 128 - 1); - saa_writeb(SAA7134_TS_DMA0, (PAGE_SIZE >> 7) - 1); - saa_writeb(SAA7134_TS_DMA1, 0); - saa_writeb(SAA7134_TS_DMA2, 0); - - /* Enable video streaming mode */ - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_VIDEO); - - saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma)); - saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma)); - saa_writel(SAA7134_RS_PITCH(5), 128); - saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_MAX); - - /* Enable TS FIFO */ - saa_setl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5); - - /* Enable DMA IRQ */ - saa_setl(SAA7134_IRQ1, - SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0); - - return 0; -} - -static int saa7134_go7007_stream_stop(struct go7007 *go) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev; - - if (!saa) - return -EINVAL; - dev = saa->dev; - if (!dev) - return -EINVAL; - - /* Shut down TS FIFO */ - saa_clearl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5); - - /* Disable DMA IRQ */ - saa_clearl(SAA7134_IRQ1, - SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0); - - /* Disable TS interface */ - saa_clearb(SAA7134_TS_PARALLEL, 0x80); - - dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE, - DMA_FROM_DEVICE); - dma_unmap_page(&dev->pci->dev, saa->bottom_dma, PAGE_SIZE, - DMA_FROM_DEVICE); - - return 0; -} - -static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - u16 status_reg; - int i; - -#ifdef GO7007_HPI_DEBUG - pr_debug("saa7134-go7007: DownloadBuffer sending %d bytes\n", len); -#endif - - while (len > 0) { - i = len > 64 ? 64 : len; - saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); - saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_INIT_BUFFER); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - while (i-- > 0) { - saa_writeb(SAA7134_GPIO_GPSTATUS0, *data); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - ++data; - --len; - } - for (i = 0; i < 100; ++i) { - gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg); - if (!(status_reg & 0x0002)) - break; - } - if (i == 100) { - pr_err("saa7134-go7007: device is hung, status reg = 0x%04x\n", - status_reg); - return -1; - } - } - return 0; -} - -static struct go7007_hpi_ops saa7134_go7007_hpi_ops = { - .interface_reset = saa7134_go7007_interface_reset, - .write_interrupt = saa7134_go7007_write_interrupt, - .read_interrupt = saa7134_go7007_read_interrupt, - .stream_start = saa7134_go7007_stream_start, - .stream_stop = saa7134_go7007_stream_stop, - .send_firmware = saa7134_go7007_send_firmware, -}; -MODULE_FIRMWARE("go7007/go7007tv.bin"); - -/* --------------------------------------------------------------------------*/ - -static int saa7134_go7007_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - struct saa7134_go7007 *saa = to_state(sd); - struct saa7134_dev *dev = saa->dev; - - return saa7134_s_std_internal(dev, NULL, norm); -} - -static int saa7134_go7007_queryctrl(struct v4l2_subdev *sd, - struct v4l2_queryctrl *query) -{ - return saa7134_queryctrl(NULL, NULL, query); -} -static int saa7134_go7007_s_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - struct saa7134_go7007 *saa = to_state(sd); - struct saa7134_dev *dev = saa->dev; - - return saa7134_s_ctrl_internal(dev, NULL, ctrl); -} - -static int saa7134_go7007_g_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - struct saa7134_go7007 *saa = to_state(sd); - struct saa7134_dev *dev = saa->dev; - - return saa7134_g_ctrl_internal(dev, NULL, ctrl); -} - -/* --------------------------------------------------------------------------*/ - -static const struct v4l2_subdev_core_ops saa7134_go7007_core_ops = { - .g_ctrl = saa7134_go7007_g_ctrl, - .s_ctrl = saa7134_go7007_s_ctrl, - .queryctrl = saa7134_go7007_queryctrl, -}; - -static const struct v4l2_subdev_video_ops saa7134_go7007_video_ops = { - .s_std = saa7134_go7007_s_std, -}; - -static const struct v4l2_subdev_ops saa7134_go7007_sd_ops = { - .core = &saa7134_go7007_core_ops, - .video = &saa7134_go7007_video_ops, -}; - -/* --------------------------------------------------------------------------*/ - - -/********************* Add/remove functions *********************/ - -static int saa7134_go7007_init(struct saa7134_dev *dev) -{ - struct go7007 *go; - struct saa7134_go7007 *saa; - struct v4l2_subdev *sd; - - pr_debug("saa7134-go7007: probing new SAA713X board\n"); - - go = go7007_alloc(&board_voyager, &dev->pci->dev); - if (go == NULL) - return -ENOMEM; - - saa = kzalloc(sizeof(struct saa7134_go7007), GFP_KERNEL); - if (saa == NULL) { - kfree(go); - return -ENOMEM; - } - - go->board_id = GO7007_BOARDID_PCI_VOYAGER; - snprintf(go->bus_info, sizeof(go->bus_info), "PCI:%s", pci_name(dev->pci)); - strlcpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name)); - go->hpi_ops = &saa7134_go7007_hpi_ops; - go->hpi_context = saa; - saa->dev = dev; - - /* Init the subdevice interface */ - sd = &saa->sd; - v4l2_subdev_init(sd, &saa7134_go7007_sd_ops); - v4l2_set_subdevdata(sd, saa); - strncpy(sd->name, "saa7134-go7007", sizeof(sd->name)); - - /* Allocate a couple pages for receiving the compressed stream */ - saa->top = (u8 *)get_zeroed_page(GFP_KERNEL); - if (!saa->top) - goto allocfail; - saa->bottom = (u8 *)get_zeroed_page(GFP_KERNEL); - if (!saa->bottom) - goto allocfail; - - /* Boot the GO7007 */ - if (go7007_boot_encoder(go, go->board_info->flags & - GO7007_BOARD_USE_ONBOARD_I2C) < 0) - goto allocfail; - - /* Do any final GO7007 initialization, then register the - * V4L2 and ALSA interfaces */ - if (go7007_register_encoder(go, go->board_info->num_i2c_devs) < 0) - goto allocfail; - - /* Register the subdevice interface with the go7007 device */ - if (v4l2_device_register_subdev(&go->v4l2_dev, sd) < 0) - pr_info("saa7134-go7007: register subdev failed\n"); - - dev->empress_dev = &go->vdev; - - go->status = STATUS_ONLINE; - return 0; - -allocfail: - if (saa->top) - free_page((unsigned long)saa->top); - if (saa->bottom) - free_page((unsigned long)saa->bottom); - kfree(saa); - kfree(go); - return -ENOMEM; -} - -static int saa7134_go7007_fini(struct saa7134_dev *dev) -{ - struct go7007 *go; - struct saa7134_go7007 *saa; - - if (NULL == dev->empress_dev) - return 0; - - go = video_get_drvdata(dev->empress_dev); - if (go->audio_enabled) - go7007_snd_remove(go); - - saa = go->hpi_context; - go->status = STATUS_SHUTDOWN; - free_page((unsigned long)saa->top); - free_page((unsigned long)saa->bottom); - v4l2_device_unregister_subdev(&saa->sd); - kfree(saa); - video_unregister_device(&go->vdev); - - v4l2_device_put(&go->v4l2_dev); - dev->empress_dev = NULL; - - return 0; -} - -static struct saa7134_mpeg_ops saa7134_go7007_ops = { - .type = SAA7134_MPEG_GO7007, - .init = saa7134_go7007_init, - .fini = saa7134_go7007_fini, - .irq_ts_done = saa7134_go7007_irq_ts_done, -}; - -static int __init saa7134_go7007_mod_init(void) -{ - return saa7134_ts_register(&saa7134_go7007_ops); -} - -static void __exit saa7134_go7007_mod_cleanup(void) -{ - saa7134_ts_unregister(&saa7134_go7007_ops); -} - -module_init(saa7134_go7007_mod_init); -module_exit(saa7134_go7007_mod_cleanup); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/snd-go7007.c b/drivers/staging/media/go7007/snd-go7007.c deleted file mode 100644 index d22d7d5..0000000 --- a/drivers/staging/media/go7007/snd-go7007.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007-priv.h" - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; -static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; - -module_param_array(index, int, NULL, 0444); -module_param_array(id, charp, NULL, 0444); -module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(index, "Index value for the go7007 audio driver"); -MODULE_PARM_DESC(id, "ID string for the go7007 audio driver"); -MODULE_PARM_DESC(enable, "Enable for the go7007 audio driver"); - -struct go7007_snd { - struct snd_card *card; - struct snd_pcm *pcm; - struct snd_pcm_substream *substream; - spinlock_t lock; - int w_idx; - int hw_ptr; - int avail; - int capturing; -}; - -static struct snd_pcm_hardware go7007_snd_capture_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = (128*1024), - .period_bytes_min = 4096, - .period_bytes_max = (128*1024), - .periods_min = 1, - .periods_max = 32, -}; - -static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length) -{ - struct go7007_snd *gosnd = go->snd_context; - struct snd_pcm_runtime *runtime = gosnd->substream->runtime; - int frames = bytes_to_frames(runtime, length); - - spin_lock(&gosnd->lock); - gosnd->hw_ptr += frames; - if (gosnd->hw_ptr >= runtime->buffer_size) - gosnd->hw_ptr -= runtime->buffer_size; - gosnd->avail += frames; - spin_unlock(&gosnd->lock); - if (gosnd->w_idx + length > runtime->dma_bytes) { - int cpy = runtime->dma_bytes - gosnd->w_idx; - - memcpy(runtime->dma_area + gosnd->w_idx, buf, cpy); - length -= cpy; - buf += cpy; - gosnd->w_idx = 0; - } - memcpy(runtime->dma_area + gosnd->w_idx, buf, length); - gosnd->w_idx += length; - spin_lock(&gosnd->lock); - if (gosnd->avail < runtime->period_size) { - spin_unlock(&gosnd->lock); - return; - } - gosnd->avail -= runtime->period_size; - spin_unlock(&gosnd->lock); - if (gosnd->capturing) - snd_pcm_period_elapsed(gosnd->substream); -} - -static int go7007_snd_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - unsigned int bytes; - - bytes = params_buffer_bytes(hw_params); - if (substream->runtime->dma_bytes > 0) - vfree(substream->runtime->dma_area); - substream->runtime->dma_bytes = 0; - substream->runtime->dma_area = vmalloc(bytes); - if (substream->runtime->dma_area == NULL) - return -ENOMEM; - substream->runtime->dma_bytes = bytes; - go->audio_deliver = parse_audio_stream_data; - return 0; -} - -static int go7007_snd_hw_free(struct snd_pcm_substream *substream) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - - go->audio_deliver = NULL; - if (substream->runtime->dma_bytes > 0) - vfree(substream->runtime->dma_area); - substream->runtime->dma_bytes = 0; - return 0; -} - -static int go7007_snd_capture_open(struct snd_pcm_substream *substream) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - struct go7007_snd *gosnd = go->snd_context; - unsigned long flags; - int r; - - spin_lock_irqsave(&gosnd->lock, flags); - if (gosnd->substream == NULL) { - gosnd->substream = substream; - substream->runtime->hw = go7007_snd_capture_hw; - r = 0; - } else - r = -EBUSY; - spin_unlock_irqrestore(&gosnd->lock, flags); - return r; -} - -static int go7007_snd_capture_close(struct snd_pcm_substream *substream) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - struct go7007_snd *gosnd = go->snd_context; - - gosnd->substream = NULL; - return 0; -} - -static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream) -{ - return 0; -} - -static int go7007_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - struct go7007_snd *gosnd = go->snd_context; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - /* Just set a flag to indicate we should signal ALSA when - * sound comes in */ - gosnd->capturing = 1; - return 0; - case SNDRV_PCM_TRIGGER_STOP: - gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0; - gosnd->capturing = 0; - return 0; - default: - return -EINVAL; - } -} - -static snd_pcm_uframes_t go7007_snd_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - struct go7007_snd *gosnd = go->snd_context; - - return gosnd->hw_ptr; -} - -static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream, - unsigned long offset) -{ - return vmalloc_to_page(substream->runtime->dma_area + offset); -} - -static struct snd_pcm_ops go7007_snd_capture_ops = { - .open = go7007_snd_capture_open, - .close = go7007_snd_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = go7007_snd_hw_params, - .hw_free = go7007_snd_hw_free, - .prepare = go7007_snd_pcm_prepare, - .trigger = go7007_snd_pcm_trigger, - .pointer = go7007_snd_pcm_pointer, - .page = go7007_snd_pcm_page, -}; - -static int go7007_snd_free(struct snd_device *device) -{ - struct go7007 *go = device->device_data; - - kfree(go->snd_context); - go->snd_context = NULL; - return 0; -} - -static struct snd_device_ops go7007_snd_device_ops = { - .dev_free = go7007_snd_free, -}; - -int go7007_snd_init(struct go7007 *go) -{ - static int dev; - struct go7007_snd *gosnd; - int ret = 0; - - if (dev >= SNDRV_CARDS) - return -ENODEV; - if (!enable[dev]) { - dev++; - return -ENOENT; - } - gosnd = kmalloc(sizeof(struct go7007_snd), GFP_KERNEL); - if (gosnd == NULL) - return -ENOMEM; - spin_lock_init(&gosnd->lock); - gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0; - gosnd->capturing = 0; - ret = snd_card_new(go->dev, index[dev], id[dev], THIS_MODULE, 0, - &gosnd->card); - if (ret < 0) { - kfree(gosnd); - return ret; - } - ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go, - &go7007_snd_device_ops); - if (ret < 0) { - kfree(gosnd); - return ret; - } - ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm); - if (ret < 0) { - snd_card_free(gosnd->card); - kfree(gosnd); - return ret; - } - strlcpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver)); - strlcpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver)); - strlcpy(gosnd->card->longname, gosnd->card->shortname, - sizeof(gosnd->card->longname)); - - gosnd->pcm->private_data = go; - snd_pcm_set_ops(gosnd->pcm, SNDRV_PCM_STREAM_CAPTURE, - &go7007_snd_capture_ops); - - ret = snd_card_register(gosnd->card); - if (ret < 0) { - snd_card_free(gosnd->card); - kfree(gosnd); - return ret; - } - - gosnd->substream = NULL; - go->snd_context = gosnd; - v4l2_device_get(&go->v4l2_dev); - ++dev; - - return 0; -} -EXPORT_SYMBOL(go7007_snd_init); - -int go7007_snd_remove(struct go7007 *go) -{ - struct go7007_snd *gosnd = go->snd_context; - - snd_card_disconnect(gosnd->card); - snd_card_free_when_closed(gosnd->card); - v4l2_device_put(&go->v4l2_dev); - return 0; -} -EXPORT_SYMBOL(go7007_snd_remove); - -MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From e23cf7f358554139a7c9f0d2739eec187599c670 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jul 2014 14:22:01 -0300 Subject: [media] tuners/Kconfig: fix build when just DTV or SDR is enabled As reported by Kbuildtest: warning: (VIDEO_PVRUSB2 && VIDEO_TLG2300 && VIDEO_USBVISION && VIDEO_GO7007 && VIDEO_AU0828_V4L2 && VIDEO_CX231XX && VIDEO_TM6000 && VIDEO_EM28XX && VIDEO_IVTV && VIDEO_MXB && VIDEO_CX18 && VIDEO_CX23885 && VIDEO_CX88 && VIDEO_BT848 && VIDEO_SAA7134 && VIDEO_SAA7164) selects VIDEO_TUNER which has unmet direct dependencies (MEDIA_SUPPORT && MEDIA_TUNER) That happens when: # CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y # CONFIG_MEDIA_RADIO_SUPPORT is not set # CONFIG_MEDIA_SDR_SUPPORT is not set CONFIG_VIDEO_AU0828_V4L2=y CONFIG_VIDEO_CX231XX=y CONFIG_VIDEO_TM6000=y CONFIG_VIDEO_EM28XX=y CONFIG_VIDEO_TUNER=y CONFIG_MEDIA_SUPPORT=y With means that we need to enable MEDIA_TUNER also when DTV is enabled. While the above config doesn't cover, if we enable SDR, the same error can also happen. Reported-by: kbuild test robot Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 906461d..51edd10 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -1,7 +1,7 @@ # Analog TV tuners, auto-loaded via tuner.ko config MEDIA_TUNER tristate - depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT) && I2C + depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT) && I2C default y select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT -- cgit v0.10.2 From fcb388ce6c293b36a71a24c0bfe526c13774302a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jul 2014 16:25:33 -0300 Subject: [media] si2168: Fix a badly solved merge conflict changeset a733291d6934 didn't merge the fixes well. It ended by restoring some bad logic removed there. Signed-off-by: Mauro Carvalho Chehab Acked-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 41bdbc4d..8f81d97 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -381,20 +381,6 @@ static int si2168_init(struct dvb_frontend *fe) if (ret) goto err; - cmd.args[0] = 0x05; - cmd.args[1] = 0x00; - cmd.args[2] = 0xaa; - cmd.args[3] = 0x4d; - cmd.args[4] = 0x56; - cmd.args[5] = 0x40; - cmd.args[6] = 0x00; - cmd.args[7] = 0x00; - cmd.wlen = 8; - cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) - goto err; - chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | cmd.args[4] << 0; -- cgit v0.10.2 From 58632818ab7bc627eb1273c1f3525e2819a07b01 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 6 Feb 2014 04:49:47 -0300 Subject: [media] dvb-frontends: decimal vs hex typo in ChannelConfiguration() >From the context this should be hex 0x80 instead of decimal 80. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c index 2c54586..de0a1c1 100644 --- a/drivers/media/dvb-frontends/tda18271c2dd.c +++ b/drivers/media/dvb-frontends/tda18271c2dd.c @@ -1030,7 +1030,7 @@ static int ChannelConfiguration(struct tda_state *state, state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; if ((Standard == HF_FM_Radio) && state->m_bFMInput) - state->m_Regs[EP4] |= 80; + state->m_Regs[EP4] |= 0x80; state->m_Regs[MPD] &= ~0x80; if (Standard > HF_AnalogMax) -- cgit v0.10.2 From 18d75a09e954b72de6e85a8fb172835be547a48a Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 19 Jun 2014 11:49:40 -0300 Subject: [media] media: dib9000: avoid out of bound access This updated patch also fixes out of bound access to b[]. In dib9000_risc_apb_access_write() an out of bound access to mb[]. The current test to avoid out of bound access to mb[] is insufficient. For len = 19 non-existent mb[10] will be accessed. For odd values of len b[] is accessed out of bounds. For large values of len an of bound access to mb[] may occur in dib9000_mbx_send_attr. Signed-off-by: Heinrich Schuchardt Reviewed-by: Kees Cook Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c index e540cfb..f75dec4 100644 --- a/drivers/media/dvb-frontends/dib9000.c +++ b/drivers/media/dvb-frontends/dib9000.c @@ -1040,13 +1040,18 @@ static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 addres if (address >= 1024 || !state->platform.risc.fw_is_running) return -EINVAL; + if (len > 18) + return -EINVAL; + /* dprintk( "APB access thru wr fw %d %x", address, attribute); */ - mb[0] = (unsigned short)address; - for (i = 0; i < len && i < 20; i += 2) - mb[1 + (i / 2)] = (b[i] << 8 | b[i + 1]); + mb[0] = (u16)address; + for (i = 0; i + 1 < len; i += 2) + mb[1 + i / 2] = b[i] << 8 | b[i + 1]; + if (len & 1) + mb[1 + len / 2] = b[len - 1] << 8; - dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, 1 + len / 2, attribute); + dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, (3 + len) / 2, attribute); return dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute) == 1 ? 0 : -EINVAL; } -- cgit v0.10.2 From 20721185c9896570ec9d93175e10e62e1b517ebb Mon Sep 17 00:00:00 2001 From: Emil Goode Date: Tue, 24 Jun 2014 18:42:27 -0300 Subject: [media] Remove checks of struct member addresses This removes checks of struct member addresses since they likely result in the condition always being true. Also in the stb6100_get_bandwidth and tda8261_get_bandwidth functions the pointers frontend_ops and tuner_ops are assigned the same addresses twice. Signed-off-by: Emil Goode Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/stb6100_cfg.h b/drivers/media/dvb-frontends/stb6100_cfg.h index 6314d18..0e10ad89 100644 --- a/drivers/media/dvb-frontends/stb6100_cfg.h +++ b/drivers/media/dvb-frontends/stb6100_cfg.h @@ -21,15 +21,11 @@ static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) { - struct dvb_frontend_ops *frontend_ops = NULL; - struct dvb_tuner_ops *tuner_ops = NULL; + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; struct tuner_state t_state; int err = 0; - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->get_state) { if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { printk("%s: Invalid parameter\n", __func__); @@ -42,16 +38,13 @@ static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency) { - struct dvb_frontend_ops *frontend_ops = NULL; - struct dvb_tuner_ops *tuner_ops = NULL; + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; struct tuner_state t_state; int err = 0; t_state.frequency = frequency; - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { printk("%s: Invalid parameter\n", __func__); @@ -68,10 +61,6 @@ static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) struct tuner_state t_state; int err = 0; - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->get_state) { if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { printk("%s: Invalid parameter\n", __func__); @@ -84,16 +73,13 @@ static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) { - struct dvb_frontend_ops *frontend_ops = NULL; - struct dvb_tuner_ops *tuner_ops = NULL; + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; struct tuner_state t_state; int err = 0; t_state.bandwidth = bandwidth; - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { printk("%s: Invalid parameter\n", __func__); diff --git a/drivers/media/dvb-frontends/stb6100_proc.h b/drivers/media/dvb-frontends/stb6100_proc.h index 112163a..bd8a0ec 100644 --- a/drivers/media/dvb-frontends/stb6100_proc.h +++ b/drivers/media/dvb-frontends/stb6100_proc.h @@ -19,15 +19,11 @@ static int stb6100_get_freq(struct dvb_frontend *fe, u32 *frequency) { - struct dvb_frontend_ops *frontend_ops = NULL; - struct dvb_tuner_ops *tuner_ops = NULL; + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; struct tuner_state state; int err = 0; - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->get_state) { if (frontend_ops->i2c_gate_ctrl) frontend_ops->i2c_gate_ctrl(fe, 1); @@ -49,16 +45,13 @@ static int stb6100_get_freq(struct dvb_frontend *fe, u32 *frequency) static int stb6100_set_freq(struct dvb_frontend *fe, u32 frequency) { - struct dvb_frontend_ops *frontend_ops = NULL; - struct dvb_tuner_ops *tuner_ops = NULL; + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; struct tuner_state state; int err = 0; state.frequency = frequency; - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { if (frontend_ops->i2c_gate_ctrl) frontend_ops->i2c_gate_ctrl(fe, 1); @@ -79,15 +72,11 @@ static int stb6100_set_freq(struct dvb_frontend *fe, u32 frequency) static int stb6100_get_bandw(struct dvb_frontend *fe, u32 *bandwidth) { - struct dvb_frontend_ops *frontend_ops = NULL; - struct dvb_tuner_ops *tuner_ops = NULL; + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; struct tuner_state state; int err = 0; - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->get_state) { if (frontend_ops->i2c_gate_ctrl) frontend_ops->i2c_gate_ctrl(fe, 1); @@ -109,16 +98,13 @@ static int stb6100_get_bandw(struct dvb_frontend *fe, u32 *bandwidth) static int stb6100_set_bandw(struct dvb_frontend *fe, u32 bandwidth) { - struct dvb_frontend_ops *frontend_ops = NULL; - struct dvb_tuner_ops *tuner_ops = NULL; + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; struct tuner_state state; int err = 0; state.bandwidth = bandwidth; - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { if (frontend_ops->i2c_gate_ctrl) frontend_ops->i2c_gate_ctrl(fe, 1); diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c index 4587727..59b6e66 100644 --- a/drivers/media/dvb-frontends/stv0367.c +++ b/drivers/media/dvb-frontends/stv0367.c @@ -922,18 +922,13 @@ static int stv0367ter_gate_ctrl(struct dvb_frontend *fe, int enable) static u32 stv0367_get_tuner_freq(struct dvb_frontend *fe) { - struct dvb_frontend_ops *frontend_ops = NULL; - struct dvb_tuner_ops *tuner_ops = NULL; + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; u32 freq = 0; int err = 0; dprintk("%s:\n", __func__); - - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->get_frequency) { err = tuner_ops->get_frequency(fe, &freq); if (err < 0) { diff --git a/drivers/media/dvb-frontends/tda8261_cfg.h b/drivers/media/dvb-frontends/tda8261_cfg.h index 4671074..7de65c3 100644 --- a/drivers/media/dvb-frontends/tda8261_cfg.h +++ b/drivers/media/dvb-frontends/tda8261_cfg.h @@ -19,15 +19,11 @@ static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency) { - struct dvb_frontend_ops *frontend_ops = NULL; - struct dvb_tuner_ops *tuner_ops = NULL; + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; struct tuner_state t_state; int err = 0; - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->get_state) { if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { printk("%s: Invalid parameter\n", __func__); @@ -41,16 +37,13 @@ static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency) static int tda8261_set_frequency(struct dvb_frontend *fe, u32 frequency) { - struct dvb_frontend_ops *frontend_ops = NULL; - struct dvb_tuner_ops *tuner_ops = NULL; + struct dvb_frontend_ops *frontend_ops = &fe->ops; + struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; struct tuner_state t_state; int err = 0; t_state.frequency = frequency; - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { printk("%s: Invalid parameter\n", __func__); @@ -68,10 +61,6 @@ static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) struct tuner_state t_state; int err = 0; - if (&fe->ops) - frontend_ops = &fe->ops; - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->get_state) { if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { printk("%s: Invalid parameter\n", __func__); -- cgit v0.10.2 From 8f73b481b959dc5fcc04b6ceca6739494ab148dc Mon Sep 17 00:00:00 2001 From: Emil Goode Date: Tue, 24 Jun 2014 18:42:28 -0300 Subject: [media] Cleanup line > 80 character violations This cleans up some line over 80 character violations. Signed-off-by: Emil Goode Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/stb6100_cfg.h b/drivers/media/dvb-frontends/stb6100_cfg.h index 0e10ad89..6edc153 100644 --- a/drivers/media/dvb-frontends/stb6100_cfg.h +++ b/drivers/media/dvb-frontends/stb6100_cfg.h @@ -27,7 +27,8 @@ static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) int err = 0; if (tuner_ops->get_state) { - if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { + err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state); + if (err < 0) { printk("%s: Invalid parameter\n", __func__); return err; } @@ -46,7 +47,8 @@ static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency) t_state.frequency = frequency; if (tuner_ops->set_state) { - if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { + err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state); + if (err < 0) { printk("%s: Invalid parameter\n", __func__); return err; } @@ -62,7 +64,8 @@ static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) int err = 0; if (tuner_ops->get_state) { - if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { + err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state); + if (err < 0) { printk("%s: Invalid parameter\n", __func__); return err; } @@ -81,7 +84,8 @@ static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) t_state.bandwidth = bandwidth; if (tuner_ops->set_state) { - if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { + err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state); + if (err < 0) { printk("%s: Invalid parameter\n", __func__); return err; } diff --git a/drivers/media/dvb-frontends/tda8261_cfg.h b/drivers/media/dvb-frontends/tda8261_cfg.h index 7de65c3..04a19e1 100644 --- a/drivers/media/dvb-frontends/tda8261_cfg.h +++ b/drivers/media/dvb-frontends/tda8261_cfg.h @@ -25,7 +25,8 @@ static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency) int err = 0; if (tuner_ops->get_state) { - if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { + err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state); + if (err < 0) { printk("%s: Invalid parameter\n", __func__); return err; } @@ -45,7 +46,8 @@ static int tda8261_set_frequency(struct dvb_frontend *fe, u32 frequency) t_state.frequency = frequency; if (tuner_ops->set_state) { - if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { + err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state); + if (err < 0) { printk("%s: Invalid parameter\n", __func__); return err; } @@ -62,7 +64,8 @@ static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) int err = 0; if (tuner_ops->get_state) { - if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { + err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state); + if (err < 0) { printk("%s: Invalid parameter\n", __func__); return err; } -- cgit v0.10.2 From d616bec6b970632abfb117bae9e16a0d7db8d46d Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 8 Jul 2014 14:20:37 -0300 Subject: [media] r820t: remove unnecessary break after goto Cc: Antti Palosaari Cc: Michael Krufky Signed-off-by: Fabian Frederick Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index a0db64f..a759742 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -2300,7 +2300,6 @@ struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, case 0: /* memory allocation failure */ goto err_no_gate; - break; case 1: /* new tuner instance */ priv->cfg = cfg; -- cgit v0.10.2 From 435fce1b6cdd53a906d85a4416d89dfab1986043 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 8 Jul 2014 14:25:17 -0300 Subject: [media] xc2028: remove unnecessary break after goto Signed-off-by: Fabian Frederick Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 6ef93ee..565eeeb 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -1489,7 +1489,6 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, case 0: /* memory allocation failure */ goto fail; - break; case 1: /* new tuner instance */ priv->ctrl.max_len = 13; -- cgit v0.10.2 From 045655a94d7be211e72e9c5a181ced906e5e669a Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 8 Jul 2014 14:23:01 -0300 Subject: [media] dvb-frontends: remove unnecessary break after goto Cc: Antti Palosaari Signed-off-by: Fabian Frederick Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index fb504f1..ecf6388 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -470,7 +470,6 @@ static int af9013_statistics_snr_result(struct dvb_frontend *fe) break; default: goto err; - break; } for (i = 0; i < len; i++) { -- cgit v0.10.2 From 2442b6afd0911453f0bf412af4b420439861503d Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 8 Jul 2014 15:17:41 -0300 Subject: [media] xc5000: remove unnecessary break after goto Signed-off-by: Fabian Frederick Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index 2b3d514..f059ba2 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -1302,7 +1302,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, switch (instance) { case 0: goto fail; - break; case 1: /* new tuner instance */ priv->bandwidth = 6000000; -- cgit v0.10.2 From 5cb4e2276ccfc9fc1b9fdcf99d274dc199f153e3 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 8 Jul 2014 15:16:51 -0300 Subject: [media] xc4000: remove unnecessary break after goto Signed-off-by: Fabian Frederick Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index 2018bef..d2ee5df 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -1668,7 +1668,6 @@ struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe, switch (instance) { case 0: goto fail; - break; case 1: /* new tuner instance */ priv->bandwidth = 6000000; -- cgit v0.10.2 From 3ae266f80cd3684cb78b59ce26af70792ad6994c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 10 Jul 2014 08:17:58 -0300 Subject: [media] m88ds3103: fix SNR reporting on 32-bit arch There was 32-bit calculation overflow. Use div_u64. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 2ef8ce1..4176edf 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -879,7 +879,7 @@ static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr) /* SNR(X) dB = 10 * ln(X) / ln(10) dB */ tmp = DIV_ROUND_CLOSEST(tmp, 8 * M88DS3103_SNR_ITERATIONS); if (tmp) - *snr = 100ul * intlog2(tmp) / intlog2(10); + *snr = div_u64((u64) 100 * intlog2(tmp), intlog2(10)); else *snr = 0; break; @@ -908,7 +908,7 @@ static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr) /* SNR(X) dB = 10 * log10(X) dB */ if (signal > noise) { tmp = signal / noise; - *snr = 100ul * intlog10(tmp) / (1 << 24); + *snr = div_u64((u64) 100 * intlog10(tmp), (1 << 24)); } else { *snr = 0; } diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index 84c3c06..e73db5c 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -22,6 +22,7 @@ #include "dvb_math.h" #include #include +#include #define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" #define M88DS3103_MCLK_KHZ 96000 -- cgit v0.10.2 From 4423a2ba808d0df1b394b9313ea9a0bbad764e93 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 10 Jul 2014 08:17:59 -0300 Subject: [media] m88ds3103: implement BER Implement read_ber for BER estimate. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 4176edf..dfe0c2f 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -926,6 +926,86 @@ err: return ret; } +static int m88ds3103_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct m88ds3103_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + unsigned int utmp; + u8 buf[3], u8tmp; + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + + switch (c->delivery_system) { + case SYS_DVBS: + ret = m88ds3103_wr_reg(priv, 0xf9, 0x04); + if (ret) + goto err; + + ret = m88ds3103_rd_reg(priv, 0xf8, &u8tmp); + if (ret) + goto err; + + if (!(u8tmp & 0x10)) { + u8tmp |= 0x10; + + ret = m88ds3103_rd_regs(priv, 0xf6, buf, 2); + if (ret) + goto err; + + priv->ber = (buf[1] << 8) | (buf[0] << 0); + + /* restart counters */ + ret = m88ds3103_wr_reg(priv, 0xf8, u8tmp); + if (ret) + goto err; + } + break; + case SYS_DVBS2: + ret = m88ds3103_rd_regs(priv, 0xd5, buf, 3); + if (ret) + goto err; + + utmp = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0); + + if (utmp > 3000) { + ret = m88ds3103_rd_regs(priv, 0xf7, buf, 2); + if (ret) + goto err; + + priv->ber = (buf[1] << 8) | (buf[0] << 0); + + /* restart counters */ + ret = m88ds3103_wr_reg(priv, 0xd1, 0x01); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0xf9, 0x01); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0xf9, 0x00); + if (ret) + goto err; + + ret = m88ds3103_wr_reg(priv, 0xd1, 0x00); + if (ret) + goto err; + } + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", + __func__); + ret = -EINVAL; + goto err; + } + + *ber = priv->ber; + + return 0; +err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} static int m88ds3103_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t fe_sec_tone_mode) @@ -1284,6 +1364,7 @@ static struct dvb_frontend_ops m88ds3103_ops = { .read_status = m88ds3103_read_status, .read_snr = m88ds3103_read_snr, + .read_ber = m88ds3103_read_ber, .diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd, .diseqc_send_burst = m88ds3103_diseqc_send_burst, diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index e73db5c..9169fdd 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -35,6 +35,7 @@ struct m88ds3103_priv { struct dvb_frontend fe; fe_delivery_system_t delivery_system; fe_status_t fe_status; + u32 ber; bool warm; /* FW running */ struct i2c_adapter *i2c_adapter; }; -- cgit v0.10.2 From 276dfc4b99a65c3b05e37a267e5e36e0e8381b7a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Jul 2014 14:55:05 -0300 Subject: [media] mb86a20s: fix ISDB-T mode handling The driver was reporting an incorrect mode, when mode 2 is selected. While testing it, noticed that neither mode 1 or guard interval 1/32 is supported by this device. Document it, and ensure that it will report _AUTO when it doesn't lock, in order to not report a wrong detection to userspace. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 2f458bb..79bc1c9 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -721,11 +721,10 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) rc = mb86a20s_readreg(state, 0x07); if (rc < 0) return rc; + c->transmission_mode = TRANSMISSION_MODE_AUTO; if ((rc & 0x60) == 0x20) { - switch (rc & 0x0c >> 2) { - case 0: - c->transmission_mode = TRANSMISSION_MODE_2K; - break; + /* Only modes 2 and 3 are supported */ + switch ((rc >> 2) & 0x03) { case 1: c->transmission_mode = TRANSMISSION_MODE_4K; break; @@ -734,7 +733,9 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) break; } } + c->guard_interval = GUARD_INTERVAL_AUTO; if (!(rc & 0x10)) { + /* Guard interval 1/32 is not supported */ switch (rc & 0x3) { case 0: c->guard_interval = GUARD_INTERVAL_1_4; -- cgit v0.10.2 From 4247368bf76fcf40307e471333f18a07e0c9fe91 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 11 Jul 2014 21:37:47 -0300 Subject: [media] mb86a20s: Fix Interleaving Interleaving code was wrong at mb86a20s: instead, it was looking at the Guard Interval. Fix it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 79bc1c9..83fac78 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -459,6 +459,9 @@ static int mb86a20s_get_interleaving(struct mb86a20s_state *state, unsigned layer) { int rc; + int interleaving[] = { + 0, 1, 2, 4, 8 + }; static unsigned char reg[] = { [0] = 0x88, /* Layer A */ @@ -475,20 +478,7 @@ static int mb86a20s_get_interleaving(struct mb86a20s_state *state, if (rc < 0) return rc; - switch ((rc >> 4) & 0x07) { - case 1: - return GUARD_INTERVAL_1_4; - case 2: - return GUARD_INTERVAL_1_8; - case 3: - return GUARD_INTERVAL_1_16; - case 4: - return GUARD_INTERVAL_1_32; - - default: - case 0: - return GUARD_INTERVAL_AUTO; - } + return interleaving[(rc >> 4) & 0x07]; } static int mb86a20s_get_segment_count(struct mb86a20s_state *state, -- cgit v0.10.2 From 277bfd2f4dce200eff06cfa9e15abb094006bae8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 11 Jul 2014 21:37:48 -0300 Subject: [media] mb86a20s: Fix the code that estimates the measurement interval Instead of looking at the guard interval field, it was using the interval length, with is wrong. Fix it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 83fac78..b931179 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -556,7 +556,7 @@ static u32 isdbt_rate[3][5][4] = { static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, u32 modulation, u32 forward_error_correction, - u32 interleaving, + u32 guard_interval, u32 segment) { struct mb86a20s_state *state = fe->demodulator_priv; @@ -564,7 +564,7 @@ static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, int mod, fec, guard; /* - * If modulation/fec/interleaving is not detected, the default is + * If modulation/fec/guard is not detected, the default is * to consider the lowest bit rate, to avoid taking too long time * to get BER. */ @@ -602,7 +602,7 @@ static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, break; } - switch (interleaving) { + switch (guard_interval) { default: case GUARD_INTERVAL_1_4: guard = 0; @@ -693,7 +693,7 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) c->layer[layer].interleaving = rc; mb86a20s_layer_bitrate(fe, layer, c->layer[layer].modulation, c->layer[layer].fec, - c->layer[layer].interleaving, + c->guard_interval, c->layer[layer].segment_count); } -- cgit v0.10.2 From 157a5fe90ab92989c59b0494a51258e2dacf92f9 Mon Sep 17 00:00:00 2001 From: Himangi Saraogi Date: Tue, 15 Jul 2014 18:31:17 -0300 Subject: [media] dib7000m: Remove unnecessary null test This patch removes the null test on ch. ch is initialized at the beginning of the function to &demod->dtv_property_cache. Since demod is dereferenced prior to the null test, demod must be a valid pointer, and &demod->dtv_property_cache cannot be null. The following Coccinelle script is used for detecting the change: @r@ expression e,f; identifier g,y; statement S1,S2; @@ *e = &f->g <+... f->y ...+> *if (e != NULL || ...) S1 else S2 Signed-off-by: Himangi Saraogi Acked-by: Julia Lawall Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/dib7000m.c b/drivers/media/dvb-frontends/dib7000m.c index 148bf79..dcb9a15 100644 --- a/drivers/media/dvb-frontends/dib7000m.c +++ b/drivers/media/dvb-frontends/dib7000m.c @@ -1041,10 +1041,7 @@ static int dib7000m_tune(struct dvb_frontend *demod) u16 value; // we are already tuned - just resuming from suspend - if (ch != NULL) - dib7000m_set_channel(state, ch, 0); - else - return -EINVAL; + dib7000m_set_channel(state, ch, 0); // restart demod ret |= dib7000m_write_word(state, 898, 0x4000); -- cgit v0.10.2 From da7bfa2c5df3221c7b63be05d5110e21a11118dc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Jul 2014 13:28:13 -0300 Subject: [media] xc4000: Update firmware name The firmware name at: http://www.kernellabs.com/firmware/xc4000/ Is different from the one at the Kernel. Update it try first the new name, falling back to the previous one if the new name can't be found. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index d2ee5df..5fb2f11 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -116,6 +116,7 @@ struct xc4000_priv { #define XC4000_AUDIO_STD_MONO 32 #define XC4000_DEFAULT_FIRMWARE "dvb-fe-xc4000-1.4.fw" +#define XC4000_DEFAULT_FIRMWARE_NEW "dvb-fe-xc4000-1.4.1.fw" /* Misc Defines */ #define MAX_TV_STANDARD 24 @@ -730,13 +731,25 @@ static int xc4000_fwupload(struct dvb_frontend *fe) char name[33]; const char *fname; - if (firmware_name[0] != '\0') + if (firmware_name[0] != '\0') { fname = firmware_name; - else - fname = XC4000_DEFAULT_FIRMWARE; - dprintk(1, "Reading firmware %s\n", fname); - rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent); + dprintk(1, "Reading custom firmware %s\n", fname); + rc = request_firmware(&fw, fname, + priv->i2c_props.adap->dev.parent); + } else { + fname = XC4000_DEFAULT_FIRMWARE_NEW; + dprintk(1, "Trying to read firmware %s\n", fname); + rc = request_firmware(&fw, fname, + priv->i2c_props.adap->dev.parent); + if (rc == -ENOENT) { + fname = XC4000_DEFAULT_FIRMWARE; + dprintk(1, "Trying to read firmware %s\n", fname); + rc = request_firmware(&fw, fname, + priv->i2c_props.adap->dev.parent); + } + } + if (rc < 0) { if (rc == -ENOENT) printk(KERN_ERR "Error: firmware %s not found.\n", fname); @@ -746,6 +759,8 @@ static int xc4000_fwupload(struct dvb_frontend *fe) return rc; } + dprintk(1, "Loading Firmware: %s\n", fname); + p = fw->data; endp = p + fw->size; -- cgit v0.10.2 From 612ae142ac02e8b187f23b8c9f1cc9cd8c6a078d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Jul 2014 13:28:14 -0300 Subject: [media] xc4000: add module meta-tag with the firmware names This meta-tag is used by some distros to help them package the firmware and generate proper initrd images. So, add the firmware names there. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index 5fb2f11..17a936e 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -1769,3 +1769,5 @@ EXPORT_SYMBOL(xc4000_attach); MODULE_AUTHOR("Steven Toth, Davide Ferri"); MODULE_DESCRIPTION("Xceive xc4000 silicon tuner driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(XC4000_DEFAULT_FIRMWARE_NEW); +MODULE_FIRMWARE(XC4000_DEFAULT_FIRMWARE); -- cgit v0.10.2 From a3eec916cbc17dc1aaa3ddf120836cd5200eb4ef Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Jul 2014 14:21:18 -0300 Subject: [media] xc5000: Fix get_frequency() The programmed frequency on xc5000 is not the middle frequency, but the initial frequency on the bandwidth range. However, the DVB API works with the middle frequency. Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index f059ba2..ab7d444 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -56,7 +56,7 @@ struct xc5000_priv { u32 if_khz; u16 xtal_khz; - u32 freq_hz; + u32 freq_hz, freq_offset; u32 bandwidth; u8 video_standard; u8 rf_mode; @@ -749,13 +749,13 @@ static int xc5000_set_params(struct dvb_frontend *fe) case SYS_ATSC: dprintk(1, "%s() VSB modulation\n", __func__); priv->rf_mode = XC_RF_MODE_AIR; - priv->freq_hz = freq - 1750000; + priv->freq_offset = 1750000; priv->video_standard = DTV6; break; case SYS_DVBC_ANNEX_B: dprintk(1, "%s() QAM modulation\n", __func__); priv->rf_mode = XC_RF_MODE_CABLE; - priv->freq_hz = freq - 1750000; + priv->freq_offset = 1750000; priv->video_standard = DTV6; break; case SYS_ISDBT: @@ -770,15 +770,15 @@ static int xc5000_set_params(struct dvb_frontend *fe) switch (bw) { case 6000000: priv->video_standard = DTV6; - priv->freq_hz = freq - 1750000; + priv->freq_offset = 1750000; break; case 7000000: priv->video_standard = DTV7; - priv->freq_hz = freq - 2250000; + priv->freq_offset = 2250000; break; case 8000000: priv->video_standard = DTV8; - priv->freq_hz = freq - 2750000; + priv->freq_offset = 2750000; break; default: printk(KERN_ERR "xc5000 bandwidth not set!\n"); @@ -792,15 +792,15 @@ static int xc5000_set_params(struct dvb_frontend *fe) priv->rf_mode = XC_RF_MODE_CABLE; if (bw <= 6000000) { priv->video_standard = DTV6; - priv->freq_hz = freq - 1750000; + priv->freq_offset = 1750000; b = 6; } else if (bw <= 7000000) { priv->video_standard = DTV7; - priv->freq_hz = freq - 2250000; + priv->freq_offset = 2250000; b = 7; } else { priv->video_standard = DTV7_8; - priv->freq_hz = freq - 2750000; + priv->freq_offset = 2750000; b = 8; } dprintk(1, "%s() Bandwidth %dMHz (%d)\n", __func__, @@ -811,6 +811,8 @@ static int xc5000_set_params(struct dvb_frontend *fe) return -EINVAL; } + priv->freq_hz = freq - priv->freq_offset; + dprintk(1, "%s() frequency=%d (compensated to %d)\n", __func__, freq, priv->freq_hz); @@ -1061,7 +1063,7 @@ static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq) { struct xc5000_priv *priv = fe->tuner_priv; dprintk(1, "%s()\n", __func__); - *freq = priv->freq_hz; + *freq = priv->freq_hz + priv->freq_offset; return 0; } -- cgit v0.10.2 From 4c07e32884ab69574cfd9eb4de3334233c938071 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Jul 2014 13:28:15 -0300 Subject: [media] xc4000: Fix get_frequency() The programmed frequency on xc4000 is not the middle frequency, but the initial frequency on the bandwidth range. However, the DVB API works with the middle frequency. This works fine on set_frontend, as the device calculates the needed offset. However, at get_frequency(), the returned value is the initial frequency. That's generally not a big problem on most drivers, however, starting with changeset 6fe1099c7aec, the frequency drift is taken into account at dib7000p driver. This broke support for PCTV 340e, with uses dib7000p demod and xc4000 tuner. Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index 17a936e..f9ab79e 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -93,7 +93,7 @@ struct xc4000_priv { struct firmware_description *firm; int firm_size; u32 if_khz; - u32 freq_hz; + u32 freq_hz, freq_offset; u32 bandwidth; u8 video_standard; u8 rf_mode; @@ -1172,14 +1172,14 @@ static int xc4000_set_params(struct dvb_frontend *fe) case SYS_ATSC: dprintk(1, "%s() VSB modulation\n", __func__); priv->rf_mode = XC_RF_MODE_AIR; - priv->freq_hz = c->frequency - 1750000; + priv->freq_offset = 1750000; priv->video_standard = XC4000_DTV6; type = DTV6; break; case SYS_DVBC_ANNEX_B: dprintk(1, "%s() QAM modulation\n", __func__); priv->rf_mode = XC_RF_MODE_CABLE; - priv->freq_hz = c->frequency - 1750000; + priv->freq_offset = 1750000; priv->video_standard = XC4000_DTV6; type = DTV6; break; @@ -1188,23 +1188,23 @@ static int xc4000_set_params(struct dvb_frontend *fe) dprintk(1, "%s() OFDM\n", __func__); if (bw == 0) { if (c->frequency < 400000000) { - priv->freq_hz = c->frequency - 2250000; + priv->freq_offset = 2250000; } else { - priv->freq_hz = c->frequency - 2750000; + priv->freq_offset = 2750000; } priv->video_standard = XC4000_DTV7_8; type = DTV78; } else if (bw <= 6000000) { priv->video_standard = XC4000_DTV6; - priv->freq_hz = c->frequency - 1750000; + priv->freq_offset = 1750000; type = DTV6; } else if (bw <= 7000000) { priv->video_standard = XC4000_DTV7; - priv->freq_hz = c->frequency - 2250000; + priv->freq_offset = 2250000; type = DTV7; } else { priv->video_standard = XC4000_DTV8; - priv->freq_hz = c->frequency - 2750000; + priv->freq_offset = 2750000; type = DTV8; } priv->rf_mode = XC_RF_MODE_AIR; @@ -1215,6 +1215,8 @@ static int xc4000_set_params(struct dvb_frontend *fe) goto fail; } + priv->freq_hz = c->frequency - priv->freq_offset; + dprintk(1, "%s() frequency=%d (compensated)\n", __func__, priv->freq_hz); @@ -1535,7 +1537,7 @@ static int xc4000_get_frequency(struct dvb_frontend *fe, u32 *freq) { struct xc4000_priv *priv = fe->tuner_priv; - *freq = priv->freq_hz; + *freq = priv->freq_hz + priv->freq_offset; if (debug) { mutex_lock(&priv->lock); -- cgit v0.10.2 From 18b258d66a1878ae33284359b1d3e5b576fa767b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 11 Jul 2014 21:37:46 -0300 Subject: [media] DocBook: Fix ISDB-T Interleaving property The DocBook documentation is incorrect: on ISDB-T, interleaving time is always a power of 2. Fix it and provides a table showing the actual interleaving length for each mode. Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml index 24c22ca..948ddaa 100644 --- a/Documentation/DocBook/media/dvb/dvbproperty.xml +++ b/Documentation/DocBook/media/dvb/dvbproperty.xml @@ -555,10 +555,46 @@ typedef enum fe_delivery_system {
<constant>DTV_ISDBT_LAYER*_TIME_INTERLEAVING</constant> - Possible values: 0, 1, 2, 3, -1 (AUTO) - Note: The real inter-leaver depth-names depend on the mode (fft-size); the values - here are referring to what can be found in the TMCC-structure - - independent of the mode. + Valid values: 0, 1, 2, 4, -1 (AUTO) + when DTV_ISDBT_SOUND_BROADCASTING is active, value 8 is also valid. + Note: The real time interleaving length depends on the mode (fft-size). The values + here are referring to what can be found in the TMCC-structure, as shown in the table below. + + +
+ + DTV_ISDBT_LAYER*_TIME_INTERLEAVING + Mode 1 (2K FFT) + Mode 2 (4K FFT) + Mode 3 (8K FFT) + + + 0 + 0 + 0 + 0 + + + 1 + 4 + 2 + 1 + + + 2 + 8 + 4 + 2 + + + 4 + 16 + 8 + 4 + + + +
<constant>DTV_ATSCMH_FIC_VER</constant> -- cgit v0.10.2 From eb9da073bd002f2968c84129a5c49625911a3199 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 30 Jun 2014 11:56:33 -0300 Subject: [media] cxusb: increase buffer length to 80 bytes As reported by Vincent: [ 16.332247] xc2028 0-0061: Loading firmware for type=BASE F8MHZ (3), id 0000000000000000. [ 16.344378] cxusb: i2c wr: len=64 is too big! 64 bytes is too short for firmware load on this device. So, increase it to 80 bytes. Reported-by: Vincent McIntyre Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index b7461ac..f379f7e 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -46,7 +46,7 @@ #include "si2157.h" /* Max transfer size done by I2C transfer functions */ -#define MAX_XFER_SIZE 64 +#define MAX_XFER_SIZE 80 /* debug */ static int dvb_usb_cxusb_debug; -- cgit v0.10.2 From e9035152d72d010359383978c1126b5f8d93de5f Mon Sep 17 00:00:00 2001 From: Daeseok Youn Date: Wed, 2 Apr 2014 06:49:03 -0300 Subject: [media] staging: lirc: remove redundant NULL check in unregister_from_lirc() "ir" is already checked before calling unregister_from_lirc(). Signed-off-by: Daeseok Youn Reviewed-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/lirc/lirc_igorplugusb.c b/drivers/staging/media/lirc/lirc_igorplugusb.c index 44b0d07..431d1e8 100644 --- a/drivers/staging/media/lirc/lirc_igorplugusb.c +++ b/drivers/staging/media/lirc/lirc_igorplugusb.c @@ -209,12 +209,6 @@ static int unregister_from_lirc(struct igorplug *ir) struct lirc_driver *d; int devnum; - if (!ir) { - dev_err(&ir->usbdev->dev, - "%s: called with NULL device struct!\n", __func__); - return -EINVAL; - } - devnum = ir->devnum; d = ir->d; -- cgit v0.10.2 From 2886f013c312d288fdd4b8472b82a0a9590e3134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 3 Apr 2014 20:31:20 -0300 Subject: [media] bt8xx: fixup RC5 decoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bt8xx driver does RC5 decoding for Nebula digi hardware, but includes some pointless limitations (both start bits must be one, the device/address/system must be 0x00). Remove those limitations and update the keymap to use the full RC5 scancode (fortunately the 0x00 address means that this is perfectly backwards compatible). Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c index 5930bce..ffc0ee1 100644 --- a/drivers/media/pci/bt8xx/bttv-input.c +++ b/drivers/media/pci/bt8xx/bttv-input.c @@ -154,10 +154,10 @@ static void bttv_input_timer(unsigned long data) * testing. */ -#define RC5_START(x) (((x) >> 12) & 3) -#define RC5_TOGGLE(x) (((x) >> 11) & 1) -#define RC5_ADDR(x) (((x) >> 6) & 31) -#define RC5_INSTR(x) ((x) & 63) +#define RC5_START(x) (((x) >> 12) & 0x03) +#define RC5_TOGGLE(x) (((x) >> 11) & 0x01) +#define RC5_ADDR(x) (((x) >> 6) & 0x1f) +#define RC5_INSTR(x) (((x) >> 0) & 0x3f) /* decode raw bit pattern to RC5 code */ static u32 bttv_rc5_decode(unsigned int code) @@ -195,8 +195,8 @@ static void bttv_rc5_timer_end(unsigned long data) { struct bttv_ir *ir = (struct bttv_ir *)data; struct timeval tv; - u32 gap; - u32 rc5 = 0; + u32 gap, rc5, scancode; + u8 toggle, command, system; /* get time */ do_gettimeofday(&tv); @@ -221,26 +221,29 @@ static void bttv_rc5_timer_end(unsigned long data) if (ir->last_bit < 20) { /* ignore spurious codes (caused by light/other remotes) */ dprintk("short code: %x\n", ir->code); - } else { - ir->code = (ir->code << ir->shift_by) | 1; - rc5 = bttv_rc5_decode(ir->code); - - /* two start bits? */ - if (RC5_START(rc5) != ir->start) { - pr_info(DEVNAME ":" - " rc5 start bits invalid: %u\n", RC5_START(rc5)); - - /* right address? */ - } else if (RC5_ADDR(rc5) == ir->addr) { - u32 toggle = RC5_TOGGLE(rc5); - u32 instr = RC5_INSTR(rc5); - - /* Good code */ - rc_keydown(ir->dev, instr, toggle); - dprintk("instruction %x, toggle %x\n", - instr, toggle); - } + return; } + + ir->code = (ir->code << ir->shift_by) | 1; + rc5 = bttv_rc5_decode(ir->code); + + toggle = RC5_TOGGLE(rc5); + system = RC5_ADDR(rc5); + command = RC5_INSTR(rc5); + + switch (RC5_START(rc5)) { + case 0x3: + break; + case 0x2: + command += 0x40; + break; + default: + return; + } + + scancode = system << 8 | command; + rc_keydown(ir->dev, scancode, toggle); + dprintk("scancode %x, toggle %x\n", scancode, toggle); } static int bttv_rc5_irq(struct bttv *btv) @@ -310,8 +313,6 @@ static void bttv_ir_start(struct bttv *btv, struct bttv_ir *ir) /* set timer_end for code completion */ setup_timer(&ir->timer, bttv_rc5_timer_end, (unsigned long)ir); ir->shift_by = 1; - ir->start = 3; - ir->addr = 0x0; ir->rc5_remote_gap = ir_rc5_remote_gap; } } @@ -490,8 +491,8 @@ int bttv_input_init(struct bttv *btv) ir->polling = 50; // ms break; case BTTV_BOARD_NEBULA_DIGITV: - ir_codes = RC_MAP_NEBULA; - ir->rc5_gpio = true; + ir_codes = RC_MAP_NEBULA; + ir->rc5_gpio = true; break; case BTTV_BOARD_MACHTV_MAGICTV: ir_codes = RC_MAP_APAC_VIEWCOMP; @@ -514,7 +515,8 @@ int bttv_input_init(struct bttv *btv) ir->mask_keycode); break; } - if (NULL == ir_codes) { + + if (!ir_codes) { dprintk("Ooops: IR config error [card=%d]\n", btv->c.type); err = -ENODEV; goto err_out_free; diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h index 6eefb59..9fe19488 100644 --- a/drivers/media/pci/bt8xx/bttvp.h +++ b/drivers/media/pci/bt8xx/bttvp.h @@ -133,8 +133,6 @@ struct bttv_ir { u32 polling; u32 last_gpio; int shift_by; - int start; // What should RC5_START() be - int addr; // What RC5_ADDR() should be. int rc5_remote_gap; /* RC5 gpio */ diff --git a/drivers/media/rc/keymaps/rc-nebula.c b/drivers/media/rc/keymaps/rc-nebula.c index 8ec881a..4c50f33 100644 --- a/drivers/media/rc/keymaps/rc-nebula.c +++ b/drivers/media/rc/keymaps/rc-nebula.c @@ -14,68 +14,68 @@ #include static struct rc_map_table nebula[] = { - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - { 0x0a, KEY_TV }, - { 0x0b, KEY_AUX }, - { 0x0c, KEY_DVD }, - { 0x0d, KEY_POWER }, - { 0x0e, KEY_CAMERA }, /* labelled 'Picture' */ - { 0x0f, KEY_AUDIO }, - { 0x10, KEY_INFO }, - { 0x11, KEY_F13 }, /* 16:9 */ - { 0x12, KEY_F14 }, /* 14:9 */ - { 0x13, KEY_EPG }, - { 0x14, KEY_EXIT }, - { 0x15, KEY_MENU }, - { 0x16, KEY_UP }, - { 0x17, KEY_DOWN }, - { 0x18, KEY_LEFT }, - { 0x19, KEY_RIGHT }, - { 0x1a, KEY_ENTER }, - { 0x1b, KEY_CHANNELUP }, - { 0x1c, KEY_CHANNELDOWN }, - { 0x1d, KEY_VOLUMEUP }, - { 0x1e, KEY_VOLUMEDOWN }, - { 0x1f, KEY_RED }, - { 0x20, KEY_GREEN }, - { 0x21, KEY_YELLOW }, - { 0x22, KEY_BLUE }, - { 0x23, KEY_SUBTITLE }, - { 0x24, KEY_F15 }, /* AD */ - { 0x25, KEY_TEXT }, - { 0x26, KEY_MUTE }, - { 0x27, KEY_REWIND }, - { 0x28, KEY_STOP }, - { 0x29, KEY_PLAY }, - { 0x2a, KEY_FASTFORWARD }, - { 0x2b, KEY_F16 }, /* chapter */ - { 0x2c, KEY_PAUSE }, - { 0x2d, KEY_PLAY }, - { 0x2e, KEY_RECORD }, - { 0x2f, KEY_F17 }, /* picture in picture */ - { 0x30, KEY_KPPLUS }, /* zoom in */ - { 0x31, KEY_KPMINUS }, /* zoom out */ - { 0x32, KEY_F18 }, /* capture */ - { 0x33, KEY_F19 }, /* web */ - { 0x34, KEY_EMAIL }, - { 0x35, KEY_PHONE }, - { 0x36, KEY_PC }, + { 0x0000, KEY_0 }, + { 0x0001, KEY_1 }, + { 0x0002, KEY_2 }, + { 0x0003, KEY_3 }, + { 0x0004, KEY_4 }, + { 0x0005, KEY_5 }, + { 0x0006, KEY_6 }, + { 0x0007, KEY_7 }, + { 0x0008, KEY_8 }, + { 0x0009, KEY_9 }, + { 0x000a, KEY_TV }, + { 0x000b, KEY_AUX }, + { 0x000c, KEY_DVD }, + { 0x000d, KEY_POWER }, + { 0x000e, KEY_CAMERA }, /* labelled 'Picture' */ + { 0x000f, KEY_AUDIO }, + { 0x0010, KEY_INFO }, + { 0x0011, KEY_F13 }, /* 16:9 */ + { 0x0012, KEY_F14 }, /* 14:9 */ + { 0x0013, KEY_EPG }, + { 0x0014, KEY_EXIT }, + { 0x0015, KEY_MENU }, + { 0x0016, KEY_UP }, + { 0x0017, KEY_DOWN }, + { 0x0018, KEY_LEFT }, + { 0x0019, KEY_RIGHT }, + { 0x001a, KEY_ENTER }, + { 0x001b, KEY_CHANNELUP }, + { 0x001c, KEY_CHANNELDOWN }, + { 0x001d, KEY_VOLUMEUP }, + { 0x001e, KEY_VOLUMEDOWN }, + { 0x001f, KEY_RED }, + { 0x0020, KEY_GREEN }, + { 0x0021, KEY_YELLOW }, + { 0x0022, KEY_BLUE }, + { 0x0023, KEY_SUBTITLE }, + { 0x0024, KEY_F15 }, /* AD */ + { 0x0025, KEY_TEXT }, + { 0x0026, KEY_MUTE }, + { 0x0027, KEY_REWIND }, + { 0x0028, KEY_STOP }, + { 0x0029, KEY_PLAY }, + { 0x002a, KEY_FASTFORWARD }, + { 0x002b, KEY_F16 }, /* chapter */ + { 0x002c, KEY_PAUSE }, + { 0x002d, KEY_PLAY }, + { 0x002e, KEY_RECORD }, + { 0x002f, KEY_F17 }, /* picture in picture */ + { 0x0030, KEY_KPPLUS }, /* zoom in */ + { 0x0031, KEY_KPMINUS }, /* zoom out */ + { 0x0032, KEY_F18 }, /* capture */ + { 0x0033, KEY_F19 }, /* web */ + { 0x0034, KEY_EMAIL }, + { 0x0035, KEY_PHONE }, + { 0x0036, KEY_PC }, }; static struct rc_map_list nebula_map = { .map = { .scan = nebula, .size = ARRAY_SIZE(nebula), - .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */ + .rc_type = RC_TYPE_RC5, .name = RC_MAP_NEBULA, } }; -- cgit v0.10.2 From 4dd9bb91bb5dc44e3f8c23c60a0ba432e50d7488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 3 Apr 2014 20:31:25 -0300 Subject: [media] rc-core: improve ir-kbd-i2c get_key functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The arguments used for ir-kbd-i2c's get_key() functions are not really suited for rc-core and the ir_raw/ir_key distinction is just confusing. Convert all of them to return a protocol/scancode/toggle triple instead. Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index c8fe135..143cb2b 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -62,8 +62,8 @@ module_param(debug, int, 0644); /* debug level (0,1,2) */ /* ----------------------------------------------------------------------- */ -static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, - int size, int offset) +static int get_key_haup_common(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *ptoggle, int size, int offset) { unsigned char buf[6]; int start, range, toggle, dev, code, ircode; @@ -86,19 +86,10 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, if (!start) /* no key pressed */ return 0; - /* - * Hauppauge remotes (black/silver) always use - * specific device ids. If we do not filter the - * device ids then messages destined for devices - * such as TVs (id=0) will get through causing - * mis-fired events. - * - * We also filter out invalid key presses which - * produce annoying debug log entries. - */ - ircode= (start << 12) | (toggle << 11) | (dev << 6) | code; - if ((ircode & 0x1fff)==0x1fff) - /* invalid key press */ + + /* filter out invalid key presses */ + ircode = (start << 12) | (toggle << 11) | (dev << 6) | code; + if ((ircode & 0x1fff) == 0x1fff) return 0; if (!range) @@ -107,18 +98,20 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, dprintk(1,"ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n", start, range, toggle, dev, code); - /* return key */ - *ir_key = (dev << 8) | code; - *ir_raw = ircode; + *protocol = RC_TYPE_RC5; + *scancode = RC_SCANCODE_RC5(dev, code); + *ptoggle = toggle; return 1; } -static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_haup(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { - return get_key_haup_common (ir, ir_key, ir_raw, 3, 0); + return get_key_haup_common (ir, protocol, scancode, toggle, 3, 0); } -static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_haup_xvr(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { int ret; unsigned char buf[1] = { 0 }; @@ -133,10 +126,11 @@ static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (ret != 1) return (ret < 0) ? ret : -EINVAL; - return get_key_haup_common (ir, ir_key, ir_raw, 6, 3); + return get_key_haup_common(ir, protocol, scancode, toggle, 6, 3); } -static int get_key_pixelview(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_pixelview(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; @@ -145,12 +139,15 @@ static int get_key_pixelview(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) dprintk(1,"read error\n"); return -EIO; } - *ir_key = b; - *ir_raw = b; + + *protocol = RC_TYPE_OTHER; + *scancode = b; + *toggle = 0; return 1; } -static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_fusionhdtv(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char buf[4]; @@ -168,13 +165,14 @@ static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if(buf[0] != 0x1 || buf[1] != 0xfe) return 0; - *ir_key = buf[2]; - *ir_raw = (buf[2] << 8) | buf[3]; - + *protocol = RC_TYPE_UNKNOWN; + *scancode = buf[2]; + *toggle = 0; return 1; } -static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_knc1(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; @@ -197,13 +195,14 @@ static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) /* keep old data */ return 1; - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } -static int get_key_avermedia_cardbus(struct IR_i2c *ir, - u32 *ir_key, u32 *ir_raw) +static int get_key_avermedia_cardbus(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char subaddr, key, keygroup; struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, @@ -237,12 +236,11 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, } key |= (keygroup & 1) << 6; - *ir_key = key; - *ir_raw = key; - if (!strcmp(ir->ir_codes, RC_MAP_AVERMEDIA_M733A_RM_K6)) { - *ir_key |= keygroup << 8; - *ir_raw |= keygroup << 8; - } + *protocol = RC_TYPE_UNKNOWN; + *scancode = key; + if (ir->c->addr == 0x41) /* AVerMedia EM78P153 */ + *scancode |= keygroup << 8; + *toggle = 0; return 1; } @@ -250,19 +248,21 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, static int ir_key_poll(struct IR_i2c *ir) { - static u32 ir_key, ir_raw; + enum rc_type protocol; + u32 scancode; + u8 toggle; int rc; dprintk(3, "%s\n", __func__); - rc = ir->get_key(ir, &ir_key, &ir_raw); + rc = ir->get_key(ir, &protocol, &scancode, &toggle); if (rc < 0) { dprintk(2,"error\n"); return rc; } if (rc) { - dprintk(1, "%s: keycode = 0x%04x\n", __func__, ir_key); - rc_keydown(ir->rc, ir_key, 0); + dprintk(1, "%s: scancode = 0x%08x\n", __func__, scancode); + rc_keydown(ir->rc, scancode, toggle); } return 0; } @@ -327,7 +327,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) case 0x6b: name = "FusionHDTV"; ir->get_key = get_key_fusionhdtv; - rc_type = RC_BIT_RC5; + rc_type = RC_BIT_UNKNOWN; ir_codes = RC_MAP_FUSIONHDTV_MCE; break; case 0x40: diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c index ffc0ee1..e745f5a 100644 --- a/drivers/media/pci/bt8xx/bttv-input.c +++ b/drivers/media/pci/bt8xx/bttv-input.c @@ -336,7 +336,8 @@ static void bttv_ir_stop(struct bttv *btv) * Get_key functions used by I2C remotes */ -static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_pv951(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; @@ -363,8 +364,9 @@ static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) * the device is bound to the vendor-provided RC. */ - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index f991696..779fc63 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -539,7 +539,8 @@ void cx88_ir_irq(struct cx88_core *core) ir_raw_event_handle(ir->dev); } -static int get_key_pvr2000(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_pvr2000(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { int flags, code; @@ -563,8 +564,9 @@ static int get_key_pvr2000(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", code & 0xff, flags & 0xff); - *ir_key = code & 0xff; - *ir_raw = code; + *protocol = RC_TYPE_UNKNOWN; + *scancode = code & 0xff; + *toggle = 0; return 1; } diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c index ceed2d8..1a41ba5 100644 --- a/drivers/media/pci/ivtv/ivtv-i2c.c +++ b/drivers/media/pci/ivtv/ivtv-i2c.c @@ -148,7 +148,8 @@ static const char * const hw_devicenames[] = { "ir_video", /* IVTV_HW_I2C_IR_RX_ADAPTEC */ }; -static int get_key_adaptec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_adaptec(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char keybuf[4]; @@ -167,9 +168,9 @@ static int get_key_adaptec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) keybuf[2] &= 0x7f; keybuf[3] |= 0x80; - *ir_key = keybuf[3] | keybuf[2] << 8 | keybuf[1] << 16 |keybuf[0] << 24; - *ir_raw = *ir_key; - + *protocol = RC_TYPE_UNKNOWN; + *scancode = keybuf[3] | keybuf[2] << 8 | keybuf[1] << 16 |keybuf[0] << 24; + *toggle = 0; return 1; } diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 6f43126..73670ed 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -108,7 +108,8 @@ static int build_key(struct saa7134_dev *dev) /* --------------------- Chip specific I2C key builders ----------------- */ -static int get_key_flydvb_trio(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_flydvb_trio(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { int gpio; int attempt = 0; @@ -158,13 +159,14 @@ static int get_key_flydvb_trio(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return -EIO; } - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } -static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; int gpio; @@ -205,14 +207,15 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key, /* Button pressed */ dprintk("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b); - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } /* copied and modified from get_key_msi_tvanywhere_plus() */ -static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int get_key_kworld_pc150u(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; unsigned int gpio; @@ -253,12 +256,14 @@ static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key, /* Button pressed */ dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b); - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } -static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_purpletv(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; @@ -276,12 +281,14 @@ static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (b & 0x80) return 1; - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } -static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_hvr1110(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char buf[5]; @@ -299,14 +306,20 @@ static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) * by preserving it into two separate readings * buf[4] bits 0 and 1, and buf[1] and buf[2] are always * zero. + * + * Note that the keymap which the hvr1110 uses is RC5. + * + * FIXME: start bits could maybe be used...? */ - *ir_key = 0x1fff & ((buf[3] << 8) | (buf[4] >> 2)); - *ir_raw = *ir_key; + *protocol = RC_TYPE_RC5; + *scancode = RC_SCANCODE_RC5(buf[3] & 0x1f, buf[4] >> 2); + *toggle = !!(buf[3] & 0x40); return 1; } -static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_beholdm6xx(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char data[12]; u32 gpio; @@ -332,17 +345,18 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (data[9] != (unsigned char)(~data[8])) return 0; - *ir_raw = ((data[10] << 16) | (data[11] << 8) | (data[9] << 0)); - *ir_key = *ir_raw; - + *protocol = RC_TYPE_NEC; + *scancode = RC_SCANCODE_NECX(((data[10] << 8) | data[11]), data[9]); + *toggle = 0; return 1; } /* Common (grey or coloured) pinnacle PCTV remote handling * */ -static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, - int parity_offset, int marker, int code_modulo) +static int get_key_pinnacle(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle, int parity_offset, + int marker, int code_modulo) { unsigned char b[4]; unsigned int start = 0,parity = 0,code = 0; @@ -377,11 +391,11 @@ static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, code %= code_modulo; - *ir_raw = code; - *ir_key = code; + *protocol = RC_TYPE_UNKNOWN; + *scancode = code; + *toggle = 0; i2cdprintk("Pinnacle PCTV key %02x\n", code); - return 1; } @@ -394,10 +408,11 @@ static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, * * Sylvain Pasche */ -static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_pinnacle_grey(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { - return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff); + return get_key_pinnacle(ir, protocol, scancode, toggle, 1, 0xfe, 0xff); } @@ -405,7 +420,8 @@ static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) * * Ricardo Cerqueira */ -static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_pinnacle_color(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { /* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE * @@ -413,7 +429,7 @@ static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) * codes < 128 */ - return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88); + return get_key_pinnacle(ir, protocol, scancode, toggle, 2, 0x80, 0x88); } void saa7134_input_irq(struct saa7134_dev *dev) diff --git a/drivers/media/usb/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c index 46d52fa..adcdd92 100644 --- a/drivers/media/usb/cx231xx/cx231xx-input.c +++ b/drivers/media/usb/cx231xx/cx231xx-input.c @@ -21,11 +21,12 @@ #include "cx231xx.h" #include #include +#include #define MODULE_NAME "cx231xx-input" -static int get_key_isdbt(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int get_key_isdbt(struct IR_i2c *ir, enum rc_type *protocol, + u32 *pscancode, u8 *toggle) { int rc; u8 cmd, scancode; @@ -46,21 +47,14 @@ static int get_key_isdbt(struct IR_i2c *ir, u32 *ir_key, if (cmd == 0xff) return 0; - scancode = - ((cmd & 0x01) ? 0x80 : 0) | - ((cmd & 0x02) ? 0x40 : 0) | - ((cmd & 0x04) ? 0x20 : 0) | - ((cmd & 0x08) ? 0x10 : 0) | - ((cmd & 0x10) ? 0x08 : 0) | - ((cmd & 0x20) ? 0x04 : 0) | - ((cmd & 0x40) ? 0x02 : 0) | - ((cmd & 0x80) ? 0x01 : 0); + scancode = bitrev8(cmd); dev_dbg(&ir->rc->input_dev->dev, "cmd %02x, scan = %02x\n", cmd, scancode); - *ir_key = scancode; - *ir_raw = scancode; + *protocol = RC_TYPE_OTHER; + *pscancode = scancode; + *toggle = 0; return 1; } diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h index e221bc7..d856435 100644 --- a/include/media/ir-kbd-i2c.h +++ b/include/media/ir-kbd-i2c.h @@ -20,7 +20,8 @@ struct IR_i2c { struct delayed_work work; char name[32]; char phys[32]; - int (*get_key)(struct IR_i2c*, u32*, u32*); + int (*get_key)(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle); }; enum ir_kbd_get_key_fn { @@ -44,7 +45,8 @@ struct IR_i2c_init_data { * Specify either a function pointer or a value indicating one of * ir_kbd_i2c's internal get_key functions */ - int (*get_key)(struct IR_i2c*, u32*, u32*); + int (*get_key)(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle); enum ir_kbd_get_key_fn internal_get_key_func; struct rc_dev *rc_dev; diff --git a/include/media/rc-map.h b/include/media/rc-map.h index e5aa240..894c7e4 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -62,6 +62,16 @@ enum rc_type { RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \ RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP) +#define RC_SCANCODE_UNKNOWN(x) (x) +#define RC_SCANCODE_OTHER(x) (x) +#define RC_SCANCODE_NEC(addr, cmd) (((addr) << 8) | (cmd)) +#define RC_SCANCODE_NECX(addr, cmd) (((addr) << 8) | (cmd)) +#define RC_SCANCODE_NEC32(data) ((data) & 0xffffffff) +#define RC_SCANCODE_RC5(sys, cmd) (((sys) << 8) | (cmd)) +#define RC_SCANCODE_RC5_SZ(sys, cmd) (((sys) << 8) | (cmd)) +#define RC_SCANCODE_RC6_0(sys, cmd) (((sys) << 8) | (cmd)) +#define RC_SCANCODE_RC6_6A(vendor, sys, cmd) (((vendor) << 16) | ((sys) << 8) | (cmd)) + struct rc_map_table { u32 scancode; u32 keycode; -- cgit v0.10.2 From af3a4a9bbeb00df3e42e77240b4cdac5479812f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 3 Apr 2014 20:31:51 -0300 Subject: [media] dib0700: NEC scancode cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the RC RX packet is defined as: struct dib0700_rc_response { ... u8 not_system; u8 system; ... u8 data; u8 not_data; The NEC protocol transmits in the order: system not_system data not_data Note that the code defines the NEC extended scancode as: scancode = be16_to_cpu(poll_reply->system16) << 8 | poll_reply->data; i.e. scancode = poll_reply->not_system << 16 | poll_reply->system << 8 | poll_reply->data; Which, if the order *is* reversed, would mean that the scancode that gets defined is in reality: scancode = poll_reply->system << 16 | poll_reply->not_system << 8 | poll_reply->data; Which is the same as the order used in drivers/media/rc/ir-nec-decoder.c. This patch changes the code to match my assumption (the generated scancode should, however, not change). [m.chehab@samsung.com: rebased and fixed the decoding error message] Signed-off-by: David Härdeman CC: Patrick Boettcher Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index c14285f..38b151f 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -658,13 +658,8 @@ out: struct dib0700_rc_response { u8 report_id; u8 data_state; - union { - u16 system16; - struct { - u8 not_system; - u8 system; - }; - }; + u8 system; + u8 not_system; u8 data; u8 not_data; }; @@ -710,20 +705,29 @@ static void dib0700_rc_urb_completion(struct urb *purb) toggle = 0; /* NEC protocol sends repeat code as 0 0 0 FF */ - if ((poll_reply->system == 0x00) && (poll_reply->data == 0x00) - && (poll_reply->not_data == 0xff)) { + if (poll_reply->system == 0x00 && + poll_reply->not_system == 0x00 && + poll_reply->data == 0x00 && + poll_reply->not_data == 0xff) { poll_reply->data_state = 2; break; } - if ((poll_reply->system ^ poll_reply->not_system) != 0xff) { + if ((poll_reply->data ^ poll_reply->not_data) != 0xff) { + deb_data("NEC32 protocol\n"); + keycode = RC_SCANCODE_NEC32(poll_reply->system << 24 | + poll_reply->not_system << 16 | + poll_reply->data << 8 | + poll_reply->not_data); + } else if ((poll_reply->system ^ poll_reply->not_system) != 0xff) { deb_data("NEC extended protocol\n"); - /* NEC extended code - 24 bits */ - keycode = be16_to_cpu(poll_reply->system16) << 8 | poll_reply->data; + keycode = RC_SCANCODE_NECX(poll_reply->system << 8 | + poll_reply->not_system, + poll_reply->data); } else { deb_data("NEC normal protocol\n"); - /* normal NEC code - 16 bits */ - keycode = poll_reply->system << 8 | poll_reply->data; + keycode = RC_SCANCODE_NEC(poll_reply->system, + poll_reply->data); } break; @@ -738,8 +742,8 @@ static void dib0700_rc_urb_completion(struct urb *purb) if ((poll_reply->data + poll_reply->not_data) != 0xff) { /* Key failed integrity check */ - err("key failed integrity check: %04x %02x %02x", - poll_reply->system, + err("key failed integrity check: %02x %02x %02x %02x", + poll_reply->system, poll_reply->not_system, poll_reply->data, poll_reply->not_data); goto resubmit; } -- cgit v0.10.2 From 120703f9eb32033f0e39bdc552c0273c8ab45f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 3 Apr 2014 20:31:30 -0300 Subject: [media] rc-core: document the protocol type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now the protocol information is not preserved, rc-core gets handed a scancode but has no idea which protocol it corresponds to. This patch (which required reading through the source/keymap for all drivers, not fun) makes the protocol information explicit which is important documentation and makes it easier to e.g. support multiple protocols with one decoder (think rc5 and rc-streamzap). The information isn't used yet so there should be no functional changes. [m.chehab@samsung.com: rebased, added cxusb and removed bad whitespacing] Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 143cb2b..f9c4233 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -261,8 +261,9 @@ static int ir_key_poll(struct IR_i2c *ir) } if (rc) { - dprintk(1, "%s: scancode = 0x%08x\n", __func__, scancode); - rc_keydown(ir->rc, scancode, toggle); + dprintk(1, "%s: proto = 0x%04x, scancode = 0x%08x\n", + __func__, protocol, scancode); + rc_keydown(ir->rc, protocol, scancode, toggle); } return 0; } diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c index e745f5a..67c8d6b 100644 --- a/drivers/media/pci/bt8xx/bttv-input.c +++ b/drivers/media/pci/bt8xx/bttv-input.c @@ -73,12 +73,12 @@ static void ir_handle_key(struct bttv *btv) if ((ir->mask_keydown && (gpio & ir->mask_keydown)) || (ir->mask_keyup && !(gpio & ir->mask_keyup))) { - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); } else { /* HACK: Probably, ir->mask_keydown is missing for this board */ if (btv->c.type == BTTV_BOARD_WINFAST2000) - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); rc_keyup(ir->dev); } @@ -103,7 +103,7 @@ static void ir_enltv_handle_key(struct bttv *btv) gpio, data, (gpio & ir->mask_keyup) ? " up" : "up/down"); - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); if (keyup) rc_keyup(ir->dev); } else { @@ -117,7 +117,7 @@ static void ir_enltv_handle_key(struct bttv *btv) if (keyup) rc_keyup(ir->dev); else - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); } ir->last_gpio = data | keyup; @@ -241,8 +241,8 @@ static void bttv_rc5_timer_end(unsigned long data) return; } - scancode = system << 8 | command; - rc_keydown(ir->dev, scancode, toggle); + scancode = RC_SCANCODE_RC5(system, command); + rc_keydown(ir->dev, RC_TYPE_RC5, scancode, toggle); dprintk("scancode %x, toggle %x\n", scancode, toggle); } diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index 779fc63..9bf48ca 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -130,25 +130,41 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) data = (data << 4) | ((gpio_key & 0xf0) >> 4); - rc_keydown(ir->dev, data, 0); + rc_keydown(ir->dev, RC_TYPE_UNKNOWN, data, 0); + + } else if (ir->core->boardnr == CX88_BOARD_PROLINK_PLAYTVPVR || + ir->core->boardnr == CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO) { + /* bit cleared on keydown, NEC scancode, 0xAAAACC, A = 0x866b */ + u16 addr; + u8 cmd; + u32 scancode; + + addr = (data >> 8) & 0xffff; + cmd = (data >> 0) & 0x00ff; + scancode = RC_SCANCODE_NECX(addr, cmd); + + if (0 == (gpio & ir->mask_keyup)) + rc_keydown_notimeout(ir->dev, RC_TYPE_NEC, scancode, 0); + else + rc_keyup(ir->dev); } else if (ir->mask_keydown) { /* bit set on keydown */ if (gpio & ir->mask_keydown) - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); else rc_keyup(ir->dev); } else if (ir->mask_keyup) { /* bit cleared on keydown */ if (0 == (gpio & ir->mask_keyup)) - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); else rc_keyup(ir->dev); } else { /* can't distinguish keydown/up :-/ */ - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); rc_keyup(ir->dev); } } @@ -329,6 +345,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) * 002-T mini RC, provided with newer PV hardware */ ir_codes = RC_MAP_PIXELVIEW_MK12; + rc_type = RC_BIT_NEC; ir->gpio_addr = MO_GP1_IO; ir->mask_keyup = 0x80; ir->polling = 10; /* ms */ @@ -416,7 +433,6 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) break; case CX88_BOARD_TWINHAN_VP1027_DVBS: ir_codes = RC_MAP_TWINHAN_VP1027_DVBS; - rc_type = RC_BIT_NEC; ir->sampling = 0xff00; /* address */ break; } diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index e60ac35..e8826c5 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -678,7 +678,8 @@ static void dm1105_emit_key(struct work_struct *work) data = (ircom >> 8) & 0x7f; - rc_keydown(ir->dev, data, 0); + /* FIXME: UNKNOWN because we don't generate a full NEC scancode (yet?) */ + rc_keydown(ir->dev, RC_TYPE_UNKNOWN, data, 0); } /* work handler */ diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 73670ed..43dd8bd 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -83,14 +83,14 @@ static int build_key(struct saa7134_dev *dev) if (data == ir->mask_keycode) rc_keyup(ir->dev); else - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); return 0; } if (ir->polling) { if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); } else { rc_keyup(ir->dev); } @@ -98,7 +98,7 @@ static int build_key(struct saa7134_dev *dev) else { /* IRQ driven mode - handle key press and release in one go */ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); rc_keyup(ir->dev); } } diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c index 0acf920..41ce7de 100644 --- a/drivers/media/pci/ttpci/budget-ci.c +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -161,14 +161,14 @@ static void msp430_ir_interrupt(unsigned long data) return; if (budget_ci->ir.full_rc5) { - rc_keydown(dev, - budget_ci->ir.rc5_device <<8 | budget_ci->ir.ir_key, - (command & 0x20) ? 1 : 0); + rc_keydown(dev, RC_TYPE_RC5, + RC_SCANCODE_RC5(budget_ci->ir.rc5_device, budget_ci->ir.ir_key), + !!(command & 0x20)); return; } /* FIXME: We should generate complete scancodes for all devices */ - rc_keydown(dev, budget_ci->ir.ir_key, (command & 0x20) ? 1 : 0); + rc_keydown(dev, RC_TYPE_UNKNOWN, budget_ci->ir.ir_key, !!(command & 0x20)); } static int msp430_ir_init(struct budget_ci *budget_ci) diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 2df7c55..8730b32 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -622,8 +622,8 @@ static void ati_remote_input_report(struct urb *urb) * it would cause ghost repeats which would be a * regression for this driver. */ - rc_keydown_notimeout(ati_remote->rdev, scancode, - data[2]); + rc_keydown_notimeout(ati_remote->rdev, RC_TYPE_OTHER, + scancode, data[2]); rc_keyup(ati_remote->rdev); } return; diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 153c084..b0ec55f 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -512,7 +512,7 @@ unlock: static int img_ir_set_normal_filter(struct rc_dev *dev, struct rc_scancode_filter *sc_filter) { - return img_ir_set_filter(dev, RC_FILTER_NORMAL, sc_filter); + return img_ir_set_filter(dev, RC_FILTER_NORMAL, sc_filter); } static int img_ir_set_wakeup_filter(struct rc_dev *dev, @@ -795,9 +795,11 @@ static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) struct img_ir_priv_hw *hw = &priv->hw; const struct img_ir_decoder *dec = hw->decoder; int ret = IMG_IR_SCANCODE; - int scancode; + u32 scancode; + enum rc_type protocol = RC_TYPE_UNKNOWN; + if (dec->scancode) - ret = dec->scancode(len, raw, &scancode, hw->enabled_protocols); + ret = dec->scancode(len, raw, &protocol, &scancode, hw->enabled_protocols); else if (len >= 32) scancode = (u32)raw; else if (len < 32) @@ -806,7 +808,7 @@ static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) len, (unsigned long long)raw); if (ret == IMG_IR_SCANCODE) { dev_dbg(priv->dev, "decoded scan code %#x\n", scancode); - rc_keydown(hw->rdev, scancode, 0); + rc_keydown(hw->rdev, protocol, scancode, 0); img_ir_end_repeat(priv); } else if (ret == IMG_IR_REPEATCODE) { if (hw->mode == IMG_IR_M_REPEATING) { diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h index 450f17d..3e40ce8 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.h +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -162,7 +162,8 @@ struct img_ir_decoder { struct img_ir_control control; /* scancode logic */ - int (*scancode)(int len, u64 raw, int *scancode, u64 protocols); + int (*scancode)(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols); int (*filter)(const struct rc_scancode_filter *in, struct img_ir_filter *out, u64 protocols); }; diff --git a/drivers/media/rc/img-ir/img-ir-jvc.c b/drivers/media/rc/img-ir/img-ir-jvc.c index 85ee90f..a60dda8 100644 --- a/drivers/media/rc/img-ir/img-ir-jvc.c +++ b/drivers/media/rc/img-ir/img-ir-jvc.c @@ -12,7 +12,8 @@ #include "img-ir-hw.h" /* Convert JVC data to a scancode */ -static int img_ir_jvc_scancode(int len, u64 raw, int *scancode, u64 protocols) +static int img_ir_jvc_scancode(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols) { unsigned int cust, data; @@ -22,6 +23,7 @@ static int img_ir_jvc_scancode(int len, u64 raw, int *scancode, u64 protocols) cust = (raw >> 0) & 0xff; data = (raw >> 8) & 0xff; + *protocol = RC_TYPE_JVC; *scancode = cust << 8 | data; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c index f7520e2..7398975 100644 --- a/drivers/media/rc/img-ir/img-ir-nec.c +++ b/drivers/media/rc/img-ir/img-ir-nec.c @@ -13,7 +13,8 @@ #include /* Convert NEC data to a scancode */ -static int img_ir_nec_scancode(int len, u64 raw, int *scancode, u64 protocols) +static int img_ir_nec_scancode(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols) { unsigned int addr, addr_inv, data, data_inv; /* a repeat code has no data */ @@ -45,6 +46,7 @@ static int img_ir_nec_scancode(int len, u64 raw, int *scancode, u64 protocols) *scancode = addr << 8 | data; } + *protocol = RC_TYPE_NEC; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-sanyo.c b/drivers/media/rc/img-ir/img-ir-sanyo.c index 6755c94..6b0653e 100644 --- a/drivers/media/rc/img-ir/img-ir-sanyo.c +++ b/drivers/media/rc/img-ir/img-ir-sanyo.c @@ -23,7 +23,8 @@ #include "img-ir-hw.h" /* Convert Sanyo data to a scancode */ -static int img_ir_sanyo_scancode(int len, u64 raw, int *scancode, u64 protocols) +static int img_ir_sanyo_scancode(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols) { unsigned int addr, addr_inv, data, data_inv; /* a repeat code has no data */ @@ -43,6 +44,7 @@ static int img_ir_sanyo_scancode(int len, u64 raw, int *scancode, u64 protocols) return -EINVAL; /* Normal Sanyo */ + *protocol = RC_TYPE_SANYO; *scancode = addr << 8 | data; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-sharp.c b/drivers/media/rc/img-ir/img-ir-sharp.c index 5867be0..3300a38 100644 --- a/drivers/media/rc/img-ir/img-ir-sharp.c +++ b/drivers/media/rc/img-ir/img-ir-sharp.c @@ -12,7 +12,8 @@ #include "img-ir-hw.h" /* Convert Sharp data to a scancode */ -static int img_ir_sharp_scancode(int len, u64 raw, int *scancode, u64 protocols) +static int img_ir_sharp_scancode(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols) { unsigned int addr, cmd, exp, chk; @@ -31,6 +32,7 @@ static int img_ir_sharp_scancode(int len, u64 raw, int *scancode, u64 protocols) /* probably the second half of the message */ return -EINVAL; + *protocol = RC_TYPE_SHARP; *scancode = addr << 8 | cmd; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-sony.c b/drivers/media/rc/img-ir/img-ir-sony.c index b9029ae..3a0f17b 100644 --- a/drivers/media/rc/img-ir/img-ir-sony.c +++ b/drivers/media/rc/img-ir/img-ir-sony.c @@ -12,35 +12,39 @@ #include "img-ir-hw.h" /* Convert Sony data to a scancode */ -static int img_ir_sony_scancode(int len, u64 raw, int *scancode, u64 protocols) +static int img_ir_sony_scancode(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols) { unsigned int dev, subdev, func; switch (len) { case 12: - if (!(protocols & RC_BIT_SONY12)) + if (!(enabled_protocols & RC_BIT_SONY12)) return -EINVAL; func = raw & 0x7f; /* first 7 bits */ raw >>= 7; dev = raw & 0x1f; /* next 5 bits */ subdev = 0; + *protocol = RC_TYPE_SONY12; break; case 15: - if (!(protocols & RC_BIT_SONY15)) + if (!(enabled_protocols & RC_BIT_SONY15)) return -EINVAL; func = raw & 0x7f; /* first 7 bits */ raw >>= 7; dev = raw & 0xff; /* next 8 bits */ subdev = 0; + *protocol = RC_TYPE_SONY15; break; case 20: - if (!(protocols & RC_BIT_SONY20)) + if (!(enabled_protocols & RC_BIT_SONY20)) return -EINVAL; func = raw & 0x7f; /* first 7 bits */ raw >>= 7; dev = raw & 0x1f; /* next 5 bits */ raw >>= 5; subdev = raw & 0xff; /* next 8 bits */ + *protocol = RC_TYPE_SONY20; break; default: return -EINVAL; diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 6f24e77..eb37f26 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1579,7 +1579,10 @@ static void imon_incoming_packet(struct imon_context *ictx, if (press_type == 0) rc_keyup(ictx->rdev); else { - rc_keydown(ictx->rdev, ictx->rc_scancode, ictx->rc_toggle); + if (ictx->rc_type == RC_BIT_RC6_MCE) + rc_keydown(ictx->rdev, + ictx->rc_type == RC_BIT_RC6_MCE ? RC_TYPE_RC6_MCE : RC_TYPE_OTHER, + ictx->rc_scancode, ictx->rc_toggle); spin_lock_irqsave(&ictx->kc_lock, flags); ictx->last_keycode = ictx->kc; spin_unlock_irqrestore(&ictx->kc_lock, flags); diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c index 4ea62a1..7b79eca 100644 --- a/drivers/media/rc/ir-jvc-decoder.c +++ b/drivers/media/rc/ir-jvc-decoder.c @@ -140,7 +140,7 @@ again: scancode = (bitrev8((data->bits >> 8) & 0xff) << 8) | (bitrev8((data->bits >> 0) & 0xff) << 0); IR_dprintk(1, "JVC scancode 0x%04x\n", scancode); - rc_keydown(dev, scancode, data->toggle); + rc_keydown(dev, RC_TYPE_JVC, scancode, data->toggle); data->first = false; data->old_bits = data->bits; } else if (data->bits == data->old_bits) { diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index 35c42e5..c4333d5 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -189,7 +189,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) if (data->is_nec_x) data->necx_repeat = true; - rc_keydown(dev, scancode, 0); + rc_keydown(dev, RC_TYPE_NEC, scancode, 0); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 4295d9b..3d38cbc 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -51,6 +51,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev) struct rc5_dec *data = &dev->raw->rc5; u8 toggle; u32 scancode; + enum rc_type protocol; if (!rc_protocols_enabled(dev, RC_BIT_RC5 | RC_BIT_RC5X)) return 0; @@ -138,6 +139,7 @@ again: toggle = (data->bits & 0x20000) ? 1 : 0; command += (data->bits & 0x01000) ? 0 : 0x40; scancode = system << 16 | command << 8 | xdata; + protocol = RC_TYPE_RC5X; IR_dprintk(1, "RC5X scancode 0x%06x (toggle: %u)\n", scancode, toggle); @@ -154,12 +156,13 @@ again: toggle = (data->bits & 0x00800) ? 1 : 0; command += (data->bits & 0x01000) ? 0 : 0x40; scancode = system << 8 | command; + protocol = RC_TYPE_RC5; IR_dprintk(1, "RC5 scancode 0x%04x (toggle: %u)\n", scancode, toggle); } - rc_keydown(dev, scancode, toggle); + rc_keydown(dev, protocol, scancode, toggle); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rc5-sz-decoder.c b/drivers/media/rc/ir-rc5-sz-decoder.c index dc18b74..85c7711 100644 --- a/drivers/media/rc/ir-rc5-sz-decoder.c +++ b/drivers/media/rc/ir-rc5-sz-decoder.c @@ -115,7 +115,7 @@ again: IR_dprintk(1, "RC5-sz scancode 0x%04x (toggle: %u)\n", scancode, toggle); - rc_keydown(dev, scancode, toggle); + rc_keydown(dev, RC_TYPE_RC5_SZ, scancode, toggle); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index cfbd64e..1dc97a7 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -88,6 +88,7 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev) struct rc6_dec *data = &dev->raw->rc6; u32 scancode; u8 toggle; + enum rc_type protocol; if (!rc_protocols_enabled(dev, RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | @@ -233,9 +234,11 @@ again: case RC6_MODE_0: scancode = data->body; toggle = data->toggle; + protocol = RC_TYPE_RC6_0; IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n", scancode, toggle); break; + case RC6_MODE_6A: if (data->count > CHAR_BIT * sizeof data->body) { IR_dprintk(1, "RC6 too many (%u) data bits\n", @@ -244,23 +247,39 @@ again: } scancode = data->body; - if (data->count == RC6_6A_32_NBITS && - (scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { - /* MCE RC */ - toggle = (scancode & RC6_6A_MCE_TOGGLE_MASK) ? 1 : 0; - scancode &= ~RC6_6A_MCE_TOGGLE_MASK; - } else { + switch (data->count) { + case 20: + protocol = RC_TYPE_RC6_6A_20; + toggle = 0; + break; + case 24: + protocol = RC_BIT_RC6_6A_24; toggle = 0; + break; + case 32: + if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { + protocol = RC_TYPE_RC6_MCE; + scancode &= ~RC6_6A_MCE_TOGGLE_MASK; + toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); + } else { + protocol = RC_BIT_RC6_6A_32; + toggle = 0; + } + break; + default: + IR_dprintk(1, "RC6(6A) unsupported length\n"); + goto out; } - IR_dprintk(1, "RC6(6A) scancode 0x%08x (toggle: %u)\n", - scancode, toggle); + + IR_dprintk(1, "RC6(6A) proto 0x%04x, scancode 0x%08x (toggle: %u)\n", + protocol, scancode, toggle); break; default: IR_dprintk(1, "RC6 unknown mode\n"); goto out; } - rc_keydown(dev, scancode, toggle); + rc_keydown(dev, protocol, scancode, toggle); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c index eb715f0..5f77022 100644 --- a/drivers/media/rc/ir-sanyo-decoder.c +++ b/drivers/media/rc/ir-sanyo-decoder.c @@ -167,7 +167,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) scancode = address << 8 | command; IR_dprintk(1, "SANYO scancode: 0x%06x\n", scancode); - rc_keydown(dev, scancode, 0); + rc_keydown(dev, RC_TYPE_SANYO, scancode, 0); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c index 66d2039..c8f2519 100644 --- a/drivers/media/rc/ir-sharp-decoder.c +++ b/drivers/media/rc/ir-sharp-decoder.c @@ -162,7 +162,7 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev) scancode = address << 8 | command; IR_dprintk(1, "Sharp scancode 0x%04x\n", scancode); - rc_keydown(dev, scancode, 0); + rc_keydown(dev, RC_TYPE_SHARP, scancode, 0); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c index 599c19a..f485f9fe 100644 --- a/drivers/media/rc/ir-sony-decoder.c +++ b/drivers/media/rc/ir-sony-decoder.c @@ -42,6 +42,7 @@ enum sony_state { static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) { struct sony_dec *data = &dev->raw->sony; + enum rc_type protocol; u32 scancode; u8 device, subdevice, function; @@ -131,6 +132,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) device = bitrev8((data->bits << 3) & 0xF8); subdevice = 0; function = bitrev8((data->bits >> 4) & 0xFE); + protocol = RC_TYPE_SONY12; break; case 15: if (!rc_protocols_enabled(dev, RC_BIT_SONY15)) { @@ -140,6 +142,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) device = bitrev8((data->bits >> 0) & 0xFF); subdevice = 0; function = bitrev8((data->bits >> 7) & 0xFE); + protocol = RC_TYPE_SONY15; break; case 20: if (!rc_protocols_enabled(dev, RC_BIT_SONY20)) { @@ -149,6 +152,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) device = bitrev8((data->bits >> 5) & 0xF8); subdevice = bitrev8((data->bits >> 0) & 0xFF); function = bitrev8((data->bits >> 12) & 0xFE); + protocol = RC_TYPE_SONY20; break; default: IR_dprintk(1, "Sony invalid bitcount %u\n", data->count); @@ -157,7 +161,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) scancode = device << 16 | subdevice << 8 | function; IR_dprintk(1, "Sony(%u) scancode 0x%05x\n", data->count, scancode); - rc_keydown(dev, scancode, 0); + rc_keydown(dev, protocol, scancode, 0); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 970b93d..9abcf77 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -623,6 +623,7 @@ EXPORT_SYMBOL_GPL(rc_repeat); /** * ir_do_keydown() - internal function to process a keypress * @dev: the struct rc_dev descriptor of the device + * @protocol: the protocol of the keypress * @scancode: the scancode of the keypress * @keycode: the keycode of the keypress * @toggle: the toggle value of the keypress @@ -630,12 +631,13 @@ EXPORT_SYMBOL_GPL(rc_repeat); * This function is used internally to register a keypress, it must be * called with keylock held. */ -static void ir_do_keydown(struct rc_dev *dev, int scancode, - u32 keycode, u8 toggle) +static void ir_do_keydown(struct rc_dev *dev, enum rc_type protocol, + u32 scancode, u32 keycode, u8 toggle) { bool new_event = (!dev->keypressed || + dev->last_protocol != protocol || dev->last_scancode != scancode || - dev->last_toggle != toggle); + dev->last_toggle != toggle); if (new_event && dev->keypressed) ir_do_keyup(dev, false); @@ -645,13 +647,14 @@ static void ir_do_keydown(struct rc_dev *dev, int scancode, if (new_event && keycode != KEY_RESERVED) { /* Register a keypress */ dev->keypressed = true; + dev->last_protocol = protocol; dev->last_scancode = scancode; dev->last_toggle = toggle; dev->last_keycode = keycode; IR_dprintk(1, "%s: key down event, " - "key 0x%04x, scancode 0x%04x\n", - dev->input_name, keycode, scancode); + "key 0x%04x, protocol 0x%04x, scancode 0x%08x\n", + dev->input_name, keycode, protocol, scancode); input_report_key(dev->input_dev, keycode, 1); led_trigger_event(led_feedback, LED_FULL); @@ -663,20 +666,21 @@ static void ir_do_keydown(struct rc_dev *dev, int scancode, /** * rc_keydown() - generates input event for a key press * @dev: the struct rc_dev descriptor of the device - * @scancode: the scancode that we're seeking + * @protocol: the protocol for the keypress + * @scancode: the scancode for the keypress * @toggle: the toggle value (protocol dependent, if the protocol doesn't * support toggle values, this should be set to zero) * * This routine is used to signal that a key has been pressed on the * remote control. */ -void rc_keydown(struct rc_dev *dev, int scancode, u8 toggle) +void rc_keydown(struct rc_dev *dev, enum rc_type protocol, u32 scancode, u8 toggle) { unsigned long flags; u32 keycode = rc_g_keycode_from_table(dev, scancode); spin_lock_irqsave(&dev->keylock, flags); - ir_do_keydown(dev, scancode, keycode, toggle); + ir_do_keydown(dev, protocol, scancode, keycode, toggle); if (dev->keypressed) { dev->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT); @@ -690,20 +694,22 @@ EXPORT_SYMBOL_GPL(rc_keydown); * rc_keydown_notimeout() - generates input event for a key press without * an automatic keyup event at a later time * @dev: the struct rc_dev descriptor of the device - * @scancode: the scancode that we're seeking + * @protocol: the protocol for the keypress + * @scancode: the scancode for the keypress * @toggle: the toggle value (protocol dependent, if the protocol doesn't * support toggle values, this should be set to zero) * * This routine is used to signal that a key has been pressed on the * remote control. The driver must manually call rc_keyup() at a later stage. */ -void rc_keydown_notimeout(struct rc_dev *dev, int scancode, u8 toggle) +void rc_keydown_notimeout(struct rc_dev *dev, enum rc_type protocol, + u32 scancode, u8 toggle) { unsigned long flags; u32 keycode = rc_g_keycode_from_table(dev, scancode); spin_lock_irqsave(&dev->keylock, flags); - ir_do_keydown(dev, scancode, keycode, toggle); + ir_do_keydown(dev, protocol, scancode, keycode, toggle); spin_unlock_irqrestore(&dev->keylock, flags); } EXPORT_SYMBOL_GPL(rc_keydown_notimeout); @@ -1315,7 +1321,7 @@ int rc_register_device(struct rc_dev *dev) dev->dev.groups = dev->sysfs_groups; dev->sysfs_groups[attr++] = &rc_dev_protocol_attr_grp; if (dev->s_filter) - dev->sysfs_groups[attr++] = &rc_dev_filter_attr_grp; + dev->sysfs_groups[attr++] = &rc_dev_filter_attr_grp; if (dev->s_wakeup_filter) dev->sysfs_groups[attr++] = &rc_dev_wakeup_filter_attr_grp; if (dev->change_wakeup_protocol) diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index da47d23..5ca738a 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -1213,7 +1213,7 @@ static int af9015_rc_query(struct dvb_usb_device *d) if ((state->rc_repeat != buf[6] || buf[0]) && !memcmp(&buf[12], state->rc_last, 4)) { dev_dbg(&d->udev->dev, "%s: key repeated\n", __func__); - rc_keydown(d->rc_dev, state->rc_keycode, 0); + rc_repeat(d->rc_dev); state->rc_repeat = buf[6]; return ret; } @@ -1233,18 +1233,22 @@ static int af9015_rc_query(struct dvb_usb_device *d) if (buf[14] == (u8) ~buf[15]) { if (buf[12] == (u8) ~buf[13]) { /* NEC */ - state->rc_keycode = buf[12] << 8 | buf[14]; + state->rc_keycode = RC_SCANCODE_NEC(buf[12], + buf[14]); } else { /* NEC extended*/ - state->rc_keycode = buf[12] << 16 | - buf[13] << 8 | buf[14]; + state->rc_keycode = RC_SCANCODE_NECX(buf[12] << 8 | + buf[13], + buf[14]); } } else { /* 32 bit NEC */ - state->rc_keycode = buf[12] << 24 | buf[13] << 16 | - buf[14] << 8 | buf[15]; + state->rc_keycode = RC_SCANCODE_NEC32(buf[12] << 24 | + buf[13] << 16 | + buf[14] << 8 | + buf[15]); } - rc_keydown(d->rc_dev, state->rc_keycode, 0); + rc_keydown(d->rc_dev, RC_TYPE_NEC, state->rc_keycode, 0); } else { dev_dbg(&d->udev->dev, "%s: no key press\n", __func__); /* Invalidate last keypress */ diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 7b9b75f..221af2b 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1313,19 +1313,20 @@ static int af9035_rc_query(struct dvb_usb_device *d) if ((buf[2] + buf[3]) == 0xff) { if ((buf[0] + buf[1]) == 0xff) { /* NEC standard 16bit */ - key = buf[0] << 8 | buf[2]; + key = RC_SCANCODE_NEC(buf[0], buf[2]); } else { /* NEC extended 24bit */ - key = buf[0] << 16 | buf[1] << 8 | buf[2]; + key = RC_SCANCODE_NECX(buf[0] << 8 | buf[1], buf[2]); } } else { /* NEC full code 32bit */ - key = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + key = RC_SCANCODE_NEC32(buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3]); } dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 4, buf); - rc_keydown(d->rc_dev, key, 0); + rc_keydown(d->rc_dev, RC_TYPE_NEC, key, 0); return 0; diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index eeab79b..e4a2382 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -1038,7 +1038,8 @@ static int anysee_rc_query(struct dvb_usb_device *d) if (ircode[0]) { dev_dbg(&d->udev->dev, "%s: key pressed %02x\n", __func__, ircode[1]); - rc_keydown(d->rc_dev, 0x08 << 8 | ircode[1], 0); + rc_keydown(d->rc_dev, RC_TYPE_NEC, + RC_SCANCODE_NEC(0x08, ircode[1]), 0); } return 0; diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index c3c4b98..935dbaa 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -207,24 +207,27 @@ static int az6007_streaming_ctrl(struct dvb_frontend *fe, int onoff) static int az6007_rc_query(struct dvb_usb_device *d) { struct az6007_device_state *st = d_to_priv(d); - unsigned code = 0; + unsigned code; az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10); if (st->data[1] == 0x44) return 0; - if ((st->data[1] ^ st->data[2]) == 0xff) - code = st->data[1]; - else - code = st->data[1] << 8 | st->data[2]; - - if ((st->data[3] ^ st->data[4]) == 0xff) - code = code << 8 | st->data[3]; - else - code = code << 16 | st->data[3] << 8 | st->data[4]; + if ((st->data[3] ^ st->data[4]) == 0xff) { + if ((st->data[1] ^ st->data[2]) == 0xff) + code = RC_SCANCODE_NEC(st->data[1], st->data[3]); + else + code = RC_SCANCODE_NECX(st->data[1] << 8 | st->data[2], + st->data[3]); + } else { + code = RC_SCANCODE_NEC32(st->data[1] << 24 | + st->data[2] << 16 | + st->data[3] << 8 | + st->data[4]); + } - rc_keydown(d->rc_dev, code, st->data[5]); + rc_keydown(d->rc_dev, RC_TYPE_NEC, code, st->data[5]); return 0; } diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 7d685bc..e332af7 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -286,14 +286,13 @@ static void lme2510_int_response(struct urb *lme_urb) case 0xaa: debug_data_snipet(1, "INT Remote data snipet", ibuf); if ((ibuf[4] + ibuf[5]) == 0xff) { - key = ibuf[5]; - key += (ibuf[3] > 0) - ? (ibuf[3] ^ 0xff) << 8 : 0; - key += (ibuf[2] ^ 0xff) << 16; + key = RC_SCANCODE_NECX((ibuf[2] ^ 0xff) << 8 | + (ibuf[3] > 0) ? (ibuf[3] ^ 0xff) : 0, + ibuf[5]); deb_info(1, "INT Key =%08x", key); if (adap_to_d(adap)->rc_dev != NULL) rc_keydown(adap_to_d(adap)->rc_dev, - key, 0); + RC_TYPE_NEC, key, 0); } break; case 0xbb: diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index a676e44..57ac62d 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1287,19 +1287,19 @@ static int rtl2831u_rc_query(struct dvb_usb_device *d) if (buf[2] == (u8) ~buf[3]) { if (buf[0] == (u8) ~buf[1]) { /* NEC standard (16 bit) */ - rc_code = buf[0] << 8 | buf[2]; + rc_code = RC_SCANCODE_NEC(buf[0], buf[2]); } else { /* NEC extended (24 bit) */ - rc_code = buf[0] << 16 | - buf[1] << 8 | buf[2]; + rc_code = RC_SCANCODE_NECX(buf[0] << 8 | buf[1], + buf[2]); } } else { /* NEC full (32 bit) */ - rc_code = buf[0] << 24 | buf[1] << 16 | - buf[2] << 8 | buf[3]; + rc_code = RC_SCANCODE_NEC32(buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3]); } - rc_keydown(d->rc_dev, rc_code, 0); + rc_keydown(d->rc_dev, RC_TYPE_NEC, rc_code, 0); ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1); if (ret) diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index f379f7e..16bc579 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -541,7 +541,7 @@ static int cxusb_tt_ct2_4400_rc_query(struct dvb_usb_device *d) rc5_addr = (cmd & 0x07C0) >> 6; /* bits 7-11 for address */ rc5_toggle = (cmd & 0x0800) >> 11; /* bit 12 for toggle */ keycode = (rc5_addr << 8) | rc5_cmd; - rc_keydown(d->rc_dev, keycode, rc5_toggle); + rc_keydown(d->rc_dev, RC_BIT_RC5, keycode, rc5_toggle); } return 0; diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index 38b151f..50856db 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -669,6 +669,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) { struct dvb_usb_device *d = purb->context; struct dib0700_rc_response *poll_reply; + enum rc_type protocol; u32 uninitialized_var(keycode); u8 toggle; @@ -702,6 +703,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) switch (d->props.rc.core.protocol) { case RC_BIT_NEC: + protocol = RC_TYPE_NEC; toggle = 0; /* NEC protocol sends repeat code as 0 0 0 FF */ @@ -724,6 +726,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) keycode = RC_SCANCODE_NECX(poll_reply->system << 8 | poll_reply->not_system, poll_reply->data); + } else { deb_data("NEC normal protocol\n"); keycode = RC_SCANCODE_NEC(poll_reply->system, @@ -733,9 +736,9 @@ static void dib0700_rc_urb_completion(struct urb *purb) break; default: deb_data("RC5 protocol\n"); - /* RC5 Protocol */ + protocol = RC_TYPE_RC5; toggle = poll_reply->report_id; - keycode = poll_reply->system << 8 | poll_reply->data; + keycode = RC_SCANCODE_RC5(poll_reply->system, poll_reply->data); break; } @@ -748,7 +751,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) goto resubmit; } - rc_keydown(d->rc_dev, keycode, toggle); + rc_keydown(d->rc_dev, protocol, keycode, toggle); resubmit: /* Clean the buffer before we requeue */ diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 501947e..91e5ebb0 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -512,7 +512,8 @@ static u8 rc_request[] = { REQUEST_POLL_RC, 0 }; static int dib0700_rc_query_old_firmware(struct dvb_usb_device *d) { u8 key[4]; - u32 keycode; + enum rc_type protocol; + u32 scancode; u8 toggle; int i; struct dib0700_state *st = d->priv; @@ -539,28 +540,29 @@ static int dib0700_rc_query_old_firmware(struct dvb_usb_device *d) dib0700_rc_setup(d, NULL); /* reset ir sensor data to prevent false events */ - d->last_event = 0; switch (d->props.rc.core.protocol) { case RC_BIT_NEC: /* NEC protocol sends repeat code as 0 0 0 FF */ if ((key[3-2] == 0x00) && (key[3-3] == 0x00) && - (key[3] == 0xff)) - keycode = d->last_event; - else { - keycode = key[3-2] << 8 | key[3-3]; - d->last_event = keycode; + (key[3] == 0xff)) { + rc_repeat(d->rc_dev); + return 0; } - rc_keydown(d->rc_dev, keycode, 0); + protocol = RC_TYPE_NEC; + scancode = RC_SCANCODE_NEC(key[3-2], key[3-3]); + toggle = 0; break; + default: /* RC-5 protocol changes toggle bit on new keypress */ - keycode = key[3-2] << 8 | key[3-3]; + protocol = RC_TYPE_RC5; + scancode = RC_SCANCODE_RC5(key[3-2], key[3-3]); toggle = key[3-1]; - rc_keydown(d->rc_dev, keycode, toggle); - break; } + + rc_keydown(d->rc_dev, protocol, scancode, toggle); return 0; } diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 7135a3e..2add8c5 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -1490,7 +1490,7 @@ static int dw2102_rc_query(struct dvb_usb_device *d) if (msg.buf[0] != 0xff) { deb_rc("%s: rc code: %x, %x\n", __func__, key[0], key[1]); - rc_keydown(d->rc_dev, key[0], 1); + rc_keydown(d->rc_dev, RC_TYPE_UNKNOWN, key[0], 0); } } @@ -1511,7 +1511,7 @@ static int prof_rc_query(struct dvb_usb_device *d) if (msg.buf[0] != 0xff) { deb_rc("%s: rc code: %x, %x\n", __func__, key[0], key[1]); - rc_keydown(d->rc_dev, key[0]^0xff, 1); + rc_keydown(d->rc_dev, RC_TYPE_UNKNOWN, key[0]^0xff, 0); } } @@ -1532,7 +1532,8 @@ static int su3000_rc_query(struct dvb_usb_device *d) if (msg.buf[0] != 0xff) { deb_rc("%s: rc code: %x, %x\n", __func__, key[0], key[1]); - rc_keydown(d->rc_dev, key[1] << 8 | key[0], 1); + rc_keydown(d->rc_dev, RC_TYPE_RC5, + RC_SCANCODE_RC5(key[1], key[0]), 0); } } diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 0306cb7..abf8ab2 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -245,7 +245,7 @@ static int m920x_rc_core_query(struct dvb_usb_device *d) else if (state == REMOTE_KEY_REPEAT) rc_repeat(d->rc_dev); else - rc_keydown(d->rc_dev, rc_state[1], 0); + rc_keydown(d->rc_dev, RC_TYPE_UNKNOWN, rc_state[1], 0); out: kfree(rc_state); diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c index 449a996..bdfe896 100644 --- a/drivers/media/usb/dvb-usb/pctv452e.c +++ b/drivers/media/usb/dvb-usb/pctv452e.c @@ -565,12 +565,12 @@ static int pctv452e_rc_query(struct dvb_usb_device *d) if ((rx[3] == 9) && (rx[12] & 0x01)) { /* got a "press" event */ - state->last_rc_key = (rx[7] << 8) | rx[6]; + state->last_rc_key = RC_SCANCODE_RC5(rx[7], rx[6]); if (debug > 2) info("%s: cmd=0x%02x sys=0x%02x\n", __func__, rx[6], rx[7]); - rc_keydown(d->rc_dev, state->last_rc_key, 0); + rc_keydown(d->rc_dev, RC_TYPE_RC5, state->last_rc_key, 0); } else if (state->last_rc_key) { rc_keyup(d->rc_dev); state->last_rc_key = 0; @@ -927,7 +927,7 @@ static struct dvb_usb_device_properties pctv452e_properties = { .rc.core = { .rc_codes = RC_MAP_DIB0700_RC5_TABLE, - .allowed_protos = RC_BIT_UNKNOWN, + .allowed_protos = RC_BIT_RC5, .rc_query = pctv452e_rc_query, .rc_interval = 100, }, @@ -980,7 +980,7 @@ static struct dvb_usb_device_properties tt_connect_s2_3600_properties = { .rc.core = { .rc_codes = RC_MAP_TT_1500, - .allowed_protos = RC_BIT_UNKNOWN, + .allowed_protos = RC_BIT_RC5, .rc_query = pctv452e_rc_query, .rc_interval = 100, }, diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c index 2ce3d19..f107173 100644 --- a/drivers/media/usb/dvb-usb/ttusb2.c +++ b/drivers/media/usb/dvb-usb/ttusb2.c @@ -438,9 +438,9 @@ static int tt3650_rc_query(struct dvb_usb_device *d) if (rx[8] & 0x01) { /* got a "press" event */ - st->last_rc_key = (rx[3] << 8) | rx[2]; + st->last_rc_key = RC_SCANCODE_RC5(rx[3], rx[2]); deb_info("%s: cmd=0x%02x sys=0x%02x\n", __func__, rx[2], rx[3]); - rc_keydown(d->rc_dev, st->last_rc_key, rx[1]); + rc_keydown(d->rc_dev, RC_TYPE_RC5, st->last_rc_key, rx[1]); } else if (st->last_rc_key) { rc_keyup(d->rc_dev); st->last_rc_key = 0; @@ -747,7 +747,7 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = { .rc_interval = 150, /* Less than IR_KEYPRESS_TIMEOUT */ .rc_codes = RC_MAP_TT_1500, .rc_query = tt3650_rc_query, - .allowed_protos = RC_BIT_UNKNOWN, + .allowed_protos = RC_BIT_RC5, }, .num_adapters = 1, diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 56ef49d..7f06ae5 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "em28xx.h" @@ -53,6 +54,7 @@ struct em28xx_ir_poll_result { unsigned int toggle_bit:1; unsigned int read_count:7; + enum rc_type protocol; u32 scancode; }; @@ -72,7 +74,7 @@ struct em28xx_IR { /* i2c slave address of external device (if used) */ u16 i2c_dev_addr; - int (*get_key_i2c)(struct i2c_client *, u32 *); + int (*get_key_i2c)(struct i2c_client *ir, enum rc_type *protocol, u32 *scancode); int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; @@ -80,7 +82,8 @@ struct em28xx_IR { I2C IR based get keycodes - should be used with ir-kbd-i2c **********************************************************/ -static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, u32 *ir_key) +static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, + enum rc_type *protocol, u32 *scancode) { unsigned char b; @@ -98,14 +101,15 @@ static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, u32 *ir_key) /* keep old data */ return 1; - *ir_key = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; return 1; } -static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, u32 *ir_key) +static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, + enum rc_type *protocol, u32 *scancode) { unsigned char buf[2]; - u16 code; int size; /* poll IR chip */ @@ -127,26 +131,13 @@ static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, u32 *ir_key) * So, the code translation is not complete. Yet, it is enough to * work with the provided RC5 IR. */ - code = - ((buf[0] & 0x01) ? 0x0020 : 0) | /* 0010 0000 */ - ((buf[0] & 0x02) ? 0x0010 : 0) | /* 0001 0000 */ - ((buf[0] & 0x04) ? 0x0008 : 0) | /* 0000 1000 */ - ((buf[0] & 0x08) ? 0x0004 : 0) | /* 0000 0100 */ - ((buf[0] & 0x10) ? 0x0002 : 0) | /* 0000 0010 */ - ((buf[0] & 0x20) ? 0x0001 : 0) | /* 0000 0001 */ - ((buf[1] & 0x08) ? 0x1000 : 0) | /* 0001 0000 */ - ((buf[1] & 0x10) ? 0x0800 : 0) | /* 0000 1000 */ - ((buf[1] & 0x20) ? 0x0400 : 0) | /* 0000 0100 */ - ((buf[1] & 0x40) ? 0x0200 : 0) | /* 0000 0010 */ - ((buf[1] & 0x80) ? 0x0100 : 0); /* 0000 0001 */ - - /* return key */ - *ir_key = code; + *protocol = RC_TYPE_RC5; + *scancode = (bitrev8(buf[1]) & 0x1f) << 8 | bitrev8(buf[0]) >> 2; return 1; } static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, - u32 *ir_key) + enum rc_type *protocol, u32 *scancode) { unsigned char buf[3]; @@ -158,13 +149,13 @@ static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, if (buf[0] != 0x00) return 0; - *ir_key = buf[2]&0x3f; - + *protocol = RC_TYPE_UNKNOWN; + *scancode = buf[2] & 0x3f; return 1; } static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, - u32 *ir_key) + enum rc_type *protocol, u32 *scancode) { unsigned char subaddr, keydetect, key; @@ -184,7 +175,8 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, if (key == 0x00) return 0; - *ir_key = key; + *protocol = RC_TYPE_UNKNOWN; + *scancode = key; return 1; } @@ -215,7 +207,22 @@ static int default_polling_getkey(struct em28xx_IR *ir, poll_result->read_count = (msg[0] & 0x7f); /* Remote Control Address/Data (Regs 0x46/0x47) */ - poll_result->scancode = msg[1] << 8 | msg[2]; + switch (ir->rc_type) { + case RC_BIT_RC5: + poll_result->protocol = RC_TYPE_RC5; + poll_result->scancode = RC_SCANCODE_RC5(msg[1], msg[2]); + break; + + case RC_BIT_NEC: + poll_result->protocol = RC_TYPE_NEC; + poll_result->scancode = RC_SCANCODE_NEC(msg[1], msg[2]); + break; + + default: + poll_result->protocol = RC_TYPE_UNKNOWN; + poll_result->scancode = msg[1] << 8 | msg[2]; + break; + } return 0; } @@ -247,25 +254,32 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, */ switch (ir->rc_type) { case RC_BIT_RC5: - poll_result->scancode = msg[1] << 8 | msg[2]; + poll_result->protocol = RC_TYPE_RC5; + poll_result->scancode = RC_SCANCODE_RC5(msg[1], msg[2]); break; + case RC_BIT_NEC: + poll_result->protocol = RC_TYPE_RC5; + poll_result->scancode = msg[1] << 8 | msg[2]; if ((msg[3] ^ msg[4]) != 0xff) /* 32 bits NEC */ - poll_result->scancode = (msg[1] << 24) | - (msg[2] << 16) | - (msg[3] << 8) | - msg[4]; + poll_result->scancode = RC_SCANCODE_NEC32((msg[1] << 24) | + (msg[2] << 16) | + (msg[3] << 8) | + (msg[4])); else if ((msg[1] ^ msg[2]) != 0xff) /* 24 bits NEC */ - poll_result->scancode = (msg[1] << 16) | - (msg[2] << 8) | - msg[3]; + poll_result->scancode = RC_SCANCODE_NECX(msg[1] << 8 | + msg[2], msg[3]); else /* Normal NEC */ - poll_result->scancode = msg[1] << 8 | msg[3]; + poll_result->scancode = RC_SCANCODE_NEC(msg[1], msg[3]); break; + case RC_BIT_RC6_0: - poll_result->scancode = msg[1] << 8 | msg[2]; + poll_result->protocol = RC_TYPE_RC6_0; + poll_result->scancode = RC_SCANCODE_RC6_0(msg[1], msg[2]); break; + default: + poll_result->protocol = RC_TYPE_UNKNOWN; poll_result->scancode = (msg[1] << 24) | (msg[2] << 16) | (msg[3] << 8) | msg[4]; break; @@ -281,22 +295,24 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir) { struct em28xx *dev = ir->dev; - static u32 ir_key; + static u32 scancode; + enum rc_type protocol; int rc; struct i2c_client client; client.adapter = &ir->dev->i2c_adap[dev->def_i2c_bus]; client.addr = ir->i2c_dev_addr; - rc = ir->get_key_i2c(&client, &ir_key); + rc = ir->get_key_i2c(&client, &protocol, &scancode); if (rc < 0) { dprintk("ir->get_key_i2c() failed: %d\n", rc); return rc; } if (rc) { - dprintk("%s: keycode = 0x%04x\n", __func__, ir_key); - rc_keydown(ir->rc, ir_key, 0); + dprintk("%s: proto = 0x%04x, scancode = 0x%04x\n", + __func__, protocol, scancode); + rc_keydown(ir->rc, protocol, scancode, 0); } return 0; } @@ -319,10 +335,12 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) poll_result.scancode); if (ir->full_code) rc_keydown(ir->rc, + poll_result.protocol, poll_result.scancode, poll_result.toggle_bit); else rc_keydown(ir->rc, + RC_TYPE_UNKNOWN, poll_result.scancode & 0xff, poll_result.toggle_bit); diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c index d1af543..676c0232 100644 --- a/drivers/media/usb/tm6000/tm6000-input.c +++ b/drivers/media/usb/tm6000/tm6000-input.c @@ -162,11 +162,42 @@ static int tm6000_ir_config(struct tm6000_IR *ir) return 0; } +static void tm6000_ir_keydown(struct tm6000_IR *ir, + const char *buf, unsigned int len) +{ + u8 device, command; + u32 scancode; + enum rc_type protocol; + + if (len < 1) + return; + + command = buf[0]; + device = (len > 1 ? buf[1] : 0x0); + switch (ir->rc_type) { + case RC_BIT_RC5: + protocol = RC_TYPE_RC5; + scancode = RC_SCANCODE_RC5(device, command); + break; + case RC_BIT_NEC: + protocol = RC_TYPE_NEC; + scancode = RC_SCANCODE_NEC(device, command); + break; + default: + protocol = RC_TYPE_OTHER; + scancode = RC_SCANCODE_OTHER(device << 8 | command); + break; + } + + dprintk(1, "%s, protocol: 0x%04x, scancode: 0x%08x\n", + __func__, protocol, scancode); + rc_keydown(ir->rc, protocol, scancode, 0); +} + static void tm6000_ir_urb_received(struct urb *urb) { struct tm6000_core *dev = urb->context; struct tm6000_IR *ir = dev->ir; - struct tm6000_ir_poll_result poll_result; char *buf; dprintk(2, "%s\n",__func__); @@ -184,12 +215,7 @@ static void tm6000_ir_urb_received(struct urb *urb) DUMP_PREFIX_OFFSET,16, 1, buf, urb->actual_length, false); - poll_result.rc_data = buf[0]; - if (urb->actual_length > 1) - poll_result.rc_data |= buf[1] << 8; - - dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data); - rc_keydown(ir->rc, poll_result.rc_data, 0); + tm6000_ir_keydown(ir, urb->transfer_buffer, urb->actual_length); usb_submit_urb(urb, GFP_ATOMIC); /* @@ -204,7 +230,6 @@ static void tm6000_ir_handle_key(struct work_struct *work) { struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); struct tm6000_core *dev = ir->dev; - struct tm6000_ir_poll_result poll_result; int rc; u8 buf[2]; @@ -219,13 +244,8 @@ static void tm6000_ir_handle_key(struct work_struct *work) if (rc < 0) return; - if (rc > 1) - poll_result.rc_data = buf[0] | buf[1] << 8; - else - poll_result.rc_data = buf[0]; - /* Check if something was read */ - if ((poll_result.rc_data & 0xff) == 0xff) { + if ((buf[0] & 0xff) == 0xff) { if (!ir->pwled) { tm6000_flash_led(dev, 1); ir->pwled = 1; @@ -233,8 +253,7 @@ static void tm6000_ir_handle_key(struct work_struct *work) return; } - dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data); - rc_keydown(ir->rc, poll_result.rc_data, 0); + tm6000_ir_keydown(ir, buf, rc); tm6000_flash_led(dev, 0); ir->pwled = 0; diff --git a/include/media/rc-core.h b/include/media/rc-core.h index fde142e..00811c9e2 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -89,6 +89,7 @@ enum rc_filter_type { * @keyup_jiffies: time (in jiffies) when the current keypress should be released * @timer_keyup: timer for releasing a keypress * @last_keycode: keycode of last keypress + * @last_protocol: protocol of last keypress * @last_scancode: scancode of last keypress * @last_toggle: toggle value of last command * @timeout: optional time after which device stops sending data @@ -113,7 +114,7 @@ enum rc_filter_type { * device doesn't interrupt host until it sees IR pulses * @s_learning_mode: enable wide band receiver used for learning * @s_carrier_report: enable carrier reports - * @s_filter: set the scancode filter + * @s_filter: set the scancode filter * @s_wakeup_filter: set the wakeup scancode filter */ struct rc_dev { @@ -141,6 +142,7 @@ struct rc_dev { unsigned long keyup_jiffies; struct timer_list timer_keyup; u32 last_keycode; + enum rc_type last_protocol; u32 last_scancode; u8 last_toggle; u32 timeout; @@ -221,8 +223,8 @@ int rc_open(struct rc_dev *rdev); void rc_close(struct rc_dev *rdev); void rc_repeat(struct rc_dev *dev); -void rc_keydown(struct rc_dev *dev, int scancode, u8 toggle); -void rc_keydown_notimeout(struct rc_dev *dev, int scancode, u8 toggle); +void rc_keydown(struct rc_dev *dev, enum rc_type protocol, u32 scancode, u8 toggle); +void rc_keydown_notimeout(struct rc_dev *dev, enum rc_type protocol, u32 scancode, u8 toggle); void rc_keyup(struct rc_dev *dev); u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode); -- cgit v0.10.2 From 0bc56cbef9fb496c79e34c46329f016064d3b0f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 3 Apr 2014 20:32:01 -0300 Subject: [media] saa7134: NEC scancode fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver codes the two address bytes in reverse order when compared to the other drivers, so make it consistent (and update the keymap, note that the result is a prefix change from 0x6b86 -> 0x866b, and the latter is pretty common among the NECX keymaps. While not conclusive, it's still a strong hint that the change is correct). Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 43dd8bd..887429b 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -346,7 +346,7 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, enum rc_type *protocol, return 0; *protocol = RC_TYPE_NEC; - *scancode = RC_SCANCODE_NECX(((data[10] << 8) | data[11]), data[9]); + *scancode = RC_SCANCODE_NECX(data[11] << 8 | data[10], data[9]); *toggle = 0; return 1; } diff --git a/drivers/media/rc/keymaps/rc-behold.c b/drivers/media/rc/keymaps/rc-behold.c index d6519f8..520a96f 100644 --- a/drivers/media/rc/keymaps/rc-behold.c +++ b/drivers/media/rc/keymaps/rc-behold.c @@ -30,8 +30,8 @@ static struct rc_map_table behold[] = { /* 0x1c 0x12 * * TV/FM POWER * * */ - { 0x6b861c, KEY_TUNER }, /* XXX KEY_TV / KEY_RADIO */ - { 0x6b8612, KEY_POWER }, + { 0x866b1c, KEY_TUNER }, /* XXX KEY_TV / KEY_RADIO */ + { 0x866b12, KEY_POWER }, /* 0x01 0x02 0x03 * * 1 2 3 * @@ -42,28 +42,28 @@ static struct rc_map_table behold[] = { * 0x07 0x08 0x09 * * 7 8 9 * * */ - { 0x6b8601, KEY_1 }, - { 0x6b8602, KEY_2 }, - { 0x6b8603, KEY_3 }, - { 0x6b8604, KEY_4 }, - { 0x6b8605, KEY_5 }, - { 0x6b8606, KEY_6 }, - { 0x6b8607, KEY_7 }, - { 0x6b8608, KEY_8 }, - { 0x6b8609, KEY_9 }, + { 0x866b01, KEY_1 }, + { 0x866b02, KEY_2 }, + { 0x866b03, KEY_3 }, + { 0x866b04, KEY_4 }, + { 0x866b05, KEY_5 }, + { 0x866b06, KEY_6 }, + { 0x866b07, KEY_7 }, + { 0x866b08, KEY_8 }, + { 0x866b09, KEY_9 }, /* 0x0a 0x00 0x17 * * RECALL 0 MODE * * */ - { 0x6b860a, KEY_AGAIN }, - { 0x6b8600, KEY_0 }, - { 0x6b8617, KEY_MODE }, + { 0x866b0a, KEY_AGAIN }, + { 0x866b00, KEY_0 }, + { 0x866b17, KEY_MODE }, /* 0x14 0x10 * * ASPECT FULLSCREEN * * */ - { 0x6b8614, KEY_SCREEN }, - { 0x6b8610, KEY_ZOOM }, + { 0x866b14, KEY_SCREEN }, + { 0x866b10, KEY_ZOOM }, /* 0x0b * * Up * @@ -74,17 +74,17 @@ static struct rc_map_table behold[] = { * 0x015 * * Down * * */ - { 0x6b860b, KEY_CHANNELUP }, - { 0x6b8618, KEY_VOLUMEDOWN }, - { 0x6b8616, KEY_OK }, /* XXX KEY_ENTER */ - { 0x6b860c, KEY_VOLUMEUP }, - { 0x6b8615, KEY_CHANNELDOWN }, + { 0x866b0b, KEY_CHANNELUP }, + { 0x866b18, KEY_VOLUMEDOWN }, + { 0x866b16, KEY_OK }, /* XXX KEY_ENTER */ + { 0x866b0c, KEY_VOLUMEUP }, + { 0x866b15, KEY_CHANNELDOWN }, /* 0x11 0x0d * * MUTE INFO * * */ - { 0x6b8611, KEY_MUTE }, - { 0x6b860d, KEY_INFO }, + { 0x866b11, KEY_MUTE }, + { 0x866b0d, KEY_INFO }, /* 0x0f 0x1b 0x1a * * RECORD PLAY/PAUSE STOP * @@ -93,26 +93,26 @@ static struct rc_map_table behold[] = { *TELETEXT AUDIO SOURCE * * RED YELLOW * * */ - { 0x6b860f, KEY_RECORD }, - { 0x6b861b, KEY_PLAYPAUSE }, - { 0x6b861a, KEY_STOP }, - { 0x6b860e, KEY_TEXT }, - { 0x6b861f, KEY_RED }, /*XXX KEY_AUDIO */ - { 0x6b861e, KEY_VIDEO }, + { 0x866b0f, KEY_RECORD }, + { 0x866b1b, KEY_PLAYPAUSE }, + { 0x866b1a, KEY_STOP }, + { 0x866b0e, KEY_TEXT }, + { 0x866b1f, KEY_RED }, /*XXX KEY_AUDIO */ + { 0x866b1e, KEY_VIDEO }, /* 0x1d 0x13 0x19 * * SLEEP PREVIEW DVB * * GREEN BLUE * * */ - { 0x6b861d, KEY_SLEEP }, - { 0x6b8613, KEY_GREEN }, - { 0x6b8619, KEY_BLUE }, /* XXX KEY_SAT */ + { 0x866b1d, KEY_SLEEP }, + { 0x866b13, KEY_GREEN }, + { 0x866b19, KEY_BLUE }, /* XXX KEY_SAT */ /* 0x58 0x5c * * FREEZE SNAPSHOT * * */ - { 0x6b8658, KEY_SLOW }, - { 0x6b865c, KEY_CAMERA }, + { 0x866b58, KEY_SLOW }, + { 0x866b5c, KEY_CAMERA }, }; -- cgit v0.10.2 From da6e162d6a4607362f8478c715c797d84d449f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 3 Apr 2014 20:32:16 -0300 Subject: [media] rc-core: simplify sysfs code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify and cleanup the sysfs code a bit. [m.chehab@samsung.com: rebased and fixed a CodingStyle issue] Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index 763c9d1..c08718e 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -240,6 +240,12 @@ ir_raw_get_allowed_protocols(void) return protocols; } +static int change_protocol(struct rc_dev *dev, u64 *rc_type) +{ + /* the caller will update dev->enabled_protocols */ + return 0; +} + /* * Used to (un)register raw event clients */ @@ -257,6 +263,7 @@ int ir_raw_event_register(struct rc_dev *dev) dev->raw->dev = dev; rc_set_enabled_protocols(dev, ~0); + dev->change_protocol = change_protocol; rc = kfifo_alloc(&dev->raw->kfifo, sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE, GFP_KERNEL); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 9abcf77..d5bfe64 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -830,7 +830,7 @@ struct rc_filter_attribute { /** * show_protocols() - shows the current/wakeup IR protocol(s) * @device: the device descriptor - * @mattr: the device attribute struct (unused) + * @mattr: the device attribute struct * @buf: a pointer to the output buffer * * This routine is a callback routine for input read the IR protocol type(s). @@ -856,20 +856,21 @@ static ssize_t show_protocols(struct device *device, mutex_lock(&dev->lock); - enabled = dev->enabled_protocols[fattr->type]; - if (dev->driver_type == RC_DRIVER_SCANCODE || - fattr->type == RC_FILTER_WAKEUP) - allowed = dev->allowed_protocols[fattr->type]; - else if (dev->raw) - allowed = ir_raw_get_allowed_protocols(); - else { - mutex_unlock(&dev->lock); - return -ENODEV; + if (fattr->type == RC_FILTER_NORMAL) { + enabled = dev->enabled_protocols[RC_FILTER_NORMAL]; + if (dev->raw) + allowed = ir_raw_get_allowed_protocols(); + else + allowed = dev->allowed_protocols[RC_FILTER_NORMAL]; + } else { + enabled = dev->enabled_protocols[RC_FILTER_WAKEUP]; + allowed = dev->allowed_protocols[RC_FILTER_WAKEUP]; } - IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n", - (long long)allowed, - (long long)enabled); + mutex_unlock(&dev->lock); + + IR_dprintk(1, "%s: allowed - 0x%llx, enabled - 0x%llx\n", + __func__, (long long)allowed, (long long)enabled); for (i = 0; i < ARRAY_SIZE(proto_names); i++) { if (allowed & enabled & proto_names[i].type) @@ -885,62 +886,29 @@ static ssize_t show_protocols(struct device *device, tmp--; *tmp = '\n'; - mutex_unlock(&dev->lock); - return tmp + 1 - buf; } /** - * store_protocols() - changes the current/wakeup IR protocol(s) - * @device: the device descriptor - * @mattr: the device attribute struct (unused) - * @buf: a pointer to the input buffer - * @len: length of the input buffer + * parse_protocol_change() - parses a protocol change request + * @protocols: pointer to the bitmask of current protocols + * @buf: pointer to the buffer with a list of changes * - * This routine is for changing the IR protocol type. - * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]protocols. - * Writing "+proto" will add a protocol to the list of enabled protocols. - * Writing "-proto" will remove a protocol from the list of enabled protocols. + * Writing "+proto" will add a protocol to the protocol mask. + * Writing "-proto" will remove a protocol from protocol mask. * Writing "proto" will enable only "proto". * Writing "none" will disable all protocols. - * Returns -EINVAL if an invalid protocol combination or unknown protocol name - * is used, otherwise @len. - * - * dev->lock is taken to guard against races between device - * registration, store_protocols and show_protocols. + * Returns the number of changes performed or a negative error code. */ -static ssize_t store_protocols(struct device *device, - struct device_attribute *mattr, - const char *data, - size_t len) +static int parse_protocol_change(u64 *protocols, const char *buf) { - struct rc_dev *dev = to_rc_dev(device); - struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr); - bool enable, disable; const char *tmp; - u64 old_type, type; + unsigned count = 0; + bool enable, disable; u64 mask; - int rc, i, count = 0; - ssize_t ret; - int (*change_protocol)(struct rc_dev *dev, u64 *rc_type); - int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter); - struct rc_scancode_filter local_filter, *filter; - - /* Device is being removed */ - if (!dev) - return -EINVAL; - - mutex_lock(&dev->lock); - - if (dev->driver_type != RC_DRIVER_SCANCODE && !dev->raw) { - IR_dprintk(1, "Protocol switching not supported\n"); - ret = -EINVAL; - goto out; - } - old_type = dev->enabled_protocols[fattr->type]; - type = old_type; + int i; - while ((tmp = strsep((char **) &data, " \n")) != NULL) { + while ((tmp = strsep((char **)&buf, " \n")) != NULL) { if (!*tmp) break; @@ -966,76 +934,124 @@ static ssize_t store_protocols(struct device *device, if (i == ARRAY_SIZE(proto_names)) { IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); - ret = -EINVAL; - goto out; + return -EINVAL; } count++; if (enable) - type |= mask; + *protocols |= mask; else if (disable) - type &= ~mask; + *protocols &= ~mask; else - type = mask; + *protocols = mask; } if (!count) { IR_dprintk(1, "Protocol not specified\n"); - ret = -EINVAL; + return -EINVAL; + } + + return count; +} + +/** + * store_protocols() - changes the current/wakeup IR protocol(s) + * @device: the device descriptor + * @mattr: the device attribute struct + * @buf: a pointer to the input buffer + * @len: length of the input buffer + * + * This routine is for changing the IR protocol type. + * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]protocols. + * See parse_protocol_change() for the valid commands. + * Returns @len on success or a negative error code. + * + * dev->lock is taken to guard against races between device + * registration, store_protocols and show_protocols. + */ +static ssize_t store_protocols(struct device *device, + struct device_attribute *mattr, + const char *buf, size_t len) +{ + struct rc_dev *dev = to_rc_dev(device); + struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr); + u64 *current_protocols; + int (*change_protocol)(struct rc_dev *dev, u64 *rc_type); + struct rc_scancode_filter *filter; + int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter); + u64 old_protocols, new_protocols; + ssize_t rc; + + /* Device is being removed */ + if (!dev) + return -EINVAL; + + if (fattr->type == RC_FILTER_NORMAL) { + IR_dprintk(1, "Normal protocol change requested\n"); + current_protocols = &dev->enabled_protocols[RC_FILTER_NORMAL]; + change_protocol = dev->change_protocol; + filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + set_filter = dev->s_filter; + } else { + IR_dprintk(1, "Wakeup protocol change requested\n"); + current_protocols = &dev->enabled_protocols[RC_FILTER_WAKEUP]; + change_protocol = dev->change_wakeup_protocol; + filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + set_filter = dev->s_wakeup_filter; + } + + if (!change_protocol) { + IR_dprintk(1, "Protocol switching not supported\n"); + return -EINVAL; + } + + mutex_lock(&dev->lock); + + old_protocols = *current_protocols; + new_protocols = old_protocols; + rc = parse_protocol_change(&new_protocols, buf); + if (rc < 0) + goto out; + + rc = change_protocol(dev, &new_protocols); + if (rc < 0) { + IR_dprintk(1, "Error setting protocols to 0x%llx\n", + (long long)new_protocols); goto out; } - change_protocol = (fattr->type == RC_FILTER_NORMAL) - ? dev->change_protocol : dev->change_wakeup_protocol; - if (change_protocol) { - rc = change_protocol(dev, &type); - if (rc < 0) { - IR_dprintk(1, "Error setting protocols to 0x%llx\n", - (long long)type); - ret = -EINVAL; - goto out; - } + if (new_protocols == old_protocols) { + rc = len; + goto out; } - dev->enabled_protocols[fattr->type] = type; - IR_dprintk(1, "Current protocol(s): 0x%llx\n", - (long long)type); + *current_protocols = new_protocols; + IR_dprintk(1, "Protocols changed to 0x%llx\n", (long long)new_protocols); /* * If the protocol is changed the filter needs updating. * Try setting the same filter with the new protocol (if any). * Fall back to clearing the filter. */ - filter = &dev->scancode_filters[fattr->type]; - set_filter = (fattr->type == RC_FILTER_NORMAL) - ? dev->s_filter : dev->s_wakeup_filter; - - if (set_filter && old_type != type && filter->mask) { - local_filter = *filter; - if (!type) { - /* no protocol => clear filter */ - ret = -1; - } else { - /* hardware filtering => try setting, otherwise clear */ - ret = set_filter(dev, &local_filter); - } - if (ret < 0) { - /* clear the filter */ - local_filter.data = 0; - local_filter.mask = 0; - set_filter(dev, &local_filter); - } + if (set_filter && filter->mask) { + if (new_protocols) + rc = set_filter(dev, filter); + else + rc = -1; - /* commit the new filter */ - *filter = local_filter; + if (rc < 0) { + filter->data = 0; + filter->mask = 0; + set_filter(dev, filter); + } } - ret = len; + rc = len; out: mutex_unlock(&dev->lock); - return ret; + return rc; } /** @@ -1061,20 +1077,23 @@ static ssize_t show_filter(struct device *device, { struct rc_dev *dev = to_rc_dev(device); struct rc_filter_attribute *fattr = to_rc_filter_attr(attr); + struct rc_scancode_filter *filter; u32 val; /* Device is being removed */ if (!dev) return -EINVAL; + if (fattr->type == RC_FILTER_NORMAL) + filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + else + filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + mutex_lock(&dev->lock); - if ((fattr->type == RC_FILTER_NORMAL && !dev->s_filter) || - (fattr->type == RC_FILTER_WAKEUP && !dev->s_wakeup_filter)) - val = 0; - else if (fattr->mask) - val = dev->scancode_filters[fattr->type].mask; + if (fattr->mask) + val = filter->mask; else - val = dev->scancode_filters[fattr->type].data; + val = filter->data; mutex_unlock(&dev->lock); return sprintf(buf, "%#x\n", val); @@ -1101,15 +1120,15 @@ static ssize_t show_filter(struct device *device, */ static ssize_t store_filter(struct device *device, struct device_attribute *attr, - const char *buf, - size_t count) + const char *buf, size_t len) { struct rc_dev *dev = to_rc_dev(device); struct rc_filter_attribute *fattr = to_rc_filter_attr(attr); - struct rc_scancode_filter local_filter, *filter; + struct rc_scancode_filter new_filter, *filter; int ret; unsigned long val; int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter); + u64 *enabled_protocols; /* Device is being removed */ if (!dev) @@ -1119,38 +1138,42 @@ static ssize_t store_filter(struct device *device, if (ret < 0) return ret; - /* Can the scancode filter be set? */ - set_filter = (fattr->type == RC_FILTER_NORMAL) ? dev->s_filter : - dev->s_wakeup_filter; + if (fattr->type == RC_FILTER_NORMAL) { + set_filter = dev->s_filter; + enabled_protocols = &dev->enabled_protocols[RC_FILTER_NORMAL]; + filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + } else { + set_filter = dev->s_wakeup_filter; + enabled_protocols = &dev->enabled_protocols[RC_FILTER_WAKEUP]; + filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + } + if (!set_filter) return -EINVAL; mutex_lock(&dev->lock); - /* Tell the driver about the new filter */ - filter = &dev->scancode_filters[fattr->type]; - local_filter = *filter; + new_filter = *filter; if (fattr->mask) - local_filter.mask = val; + new_filter.mask = val; else - local_filter.data = val; + new_filter.data = val; - if (!dev->enabled_protocols[fattr->type] && local_filter.mask) { + if (!*enabled_protocols && val) { /* refuse to set a filter unless a protocol is enabled */ ret = -EINVAL; goto unlock; } - ret = set_filter(dev, &local_filter); + ret = set_filter(dev, &new_filter); if (ret < 0) goto unlock; - /* Success, commit the new filter */ - *filter = local_filter; + *filter = new_filter; unlock: mutex_unlock(&dev->lock); - return (ret < 0) ? ret : count; + return (ret < 0) ? ret : len; } static void rc_dev_release(struct device *device) -- cgit v0.10.2 From c5540fbb9de39ceec108a889133664a887c2f55a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 3 Apr 2014 20:32:21 -0300 Subject: [media] rc-core: remove protocol arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The basic API of rc-core used to be: dev = rc_allocate_device(); dev->x = a; dev->y = b; dev->z = c; rc_register_device(); which is a pretty common pattern in the kernel, after the introduction of protocol arrays the API looks something like: dev = rc_allocate_device(); dev->x = a; rc_set_allowed_protocols(dev, RC_BIT_X); dev->z = c; rc_register_device(); There's no real need for the protocols to be an array, so change it back to be consistent (and in preparation for the following patches). [m.chehab@samsung.com: added missing changes at some files] Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c index cf1a9f1..045f8eb 100644 --- a/drivers/hid/hid-picolcd_cir.c +++ b/drivers/hid/hid-picolcd_cir.c @@ -114,7 +114,7 @@ int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report) rdev->priv = data; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->open = picolcd_cir_open; rdev->close = picolcd_cir_close; rdev->input_name = data->hdev->name; diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c index 6d7c0c8..273043e 100644 --- a/drivers/media/common/siano/smsir.c +++ b/drivers/media/common/siano/smsir.c @@ -88,7 +88,7 @@ int sms_ir_init(struct smscore_device_t *coredev) dev->priv = coredev; dev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(dev, RC_BIT_ALL); + dev->allowed_protocols = RC_BIT_ALL; dev->map_name = sms_get_board(board_id)->rc_codes; dev->driver_name = MODULE_NAME; diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index f9c4233..8311f1a 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -432,8 +432,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) * Initialize the other fields of rc_dev */ rc->map_name = ir->ir_codes; - rc_set_allowed_protocols(rc, rc_type); - rc_set_enabled_protocols(rc, rc_type); + rc->allowed_protocols = rc_type; + rc->enabled_protocols = rc_type; if (!rc->driver_name) rc->driver_name = MODULE_NAME; diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index 097d0a0..1940c18 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -346,7 +346,7 @@ int cx23885_input_init(struct cx23885_dev *dev) } rc->dev.parent = &dev->pci->dev; rc->driver_type = driver_type; - rc_set_allowed_protocols(rc, allowed_protos); + rc->allowed_protocols = allowed_protos; rc->priv = kernel_ir; rc->open = cx23885_input_ir_open; rc->close = cx23885_input_ir_close; diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index 9bf48ca..93ff6a7 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -485,7 +485,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) dev->timeout = 10 * 1000 * 1000; /* 10 ms */ } else { dev->driver_type = RC_DRIVER_SCANCODE; - rc_set_allowed_protocols(dev, rc_type); + dev->allowed_protocols = rc_type; } ir->core = core; diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 8730b32..2e13353 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -784,7 +784,7 @@ static void ati_remote_rc_init(struct ati_remote *ati_remote) rdev->priv = ati_remote; rdev->driver_type = RC_DRIVER_SCANCODE; - rc_set_allowed_protocols(rdev, RC_BIT_OTHER); + rdev->allowed_protocols = RC_BIT_OTHER; rdev->driver_name = "ati_remote"; rdev->open = ati_remote_rc_open; diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index fc9d23f..d16d9b4 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1059,7 +1059,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) learning_mode_force = false; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->priv = dev; rdev->open = ene_open; rdev->close = ene_close; diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 46b66e5..f000faf 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -541,7 +541,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* Set up the rc device */ rdev->priv = fintek; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->open = fintek_open; rdev->close = fintek_close; rdev->input_name = FINTEK_DESCRIPTION; diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index 29b5f89..5985308 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -145,9 +145,9 @@ static int gpio_ir_recv_probe(struct platform_device *pdev) rcdev->dev.parent = &pdev->dev; rcdev->driver_name = GPIO_IR_DRIVER_NAME; if (pdata->allowed_protos) - rc_set_allowed_protocols(rcdev, pdata->allowed_protos); + rcdev->allowed_protocols = pdata->allowed_protos; else - rc_set_allowed_protocols(rcdev, RC_BIT_ALL); + rcdev->allowed_protocols = RC_BIT_ALL; rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY; gpio_dev->rcdev = rcdev; diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index 627ddfd..ee60e17 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -495,7 +495,7 @@ static int iguanair_probe(struct usb_interface *intf, usb_to_input_id(ir->udev, &rc->input_id); rc->dev.parent = &intf->dev; rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rc, RC_BIT_ALL); + rc->allowed_protocols = RC_BIT_ALL; rc->priv = ir; rc->open = iguanair_open; rc->close = iguanair_close; diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index b0ec55f..bfb282a 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -556,8 +556,8 @@ static void img_ir_set_decoder(struct img_ir_priv *priv, hw->mode = IMG_IR_M_NORMAL; /* clear the wakeup scancode filter */ - rdev->scancode_filters[RC_FILTER_WAKEUP].data = 0; - rdev->scancode_filters[RC_FILTER_WAKEUP].mask = 0; + rdev->scancode_wakeup_filter.data = 0; + rdev->scancode_wakeup_filter.mask = 0; /* clear raw filters */ _img_ir_set_filter(priv, NULL); @@ -661,8 +661,8 @@ success: wakeup_protocols = *ir_type; if (!hw->decoder || !hw->decoder->filter) wakeup_protocols = 0; - rc_set_allowed_wakeup_protocols(rdev, wakeup_protocols); - rc_set_enabled_wakeup_protocols(rdev, wakeup_protocols); + rdev->allowed_wakeup_protocols = wakeup_protocols; + rdev->enabled_wakeup_protocols = wakeup_protocols; return 0; } @@ -676,9 +676,9 @@ static void img_ir_set_protocol(struct img_ir_priv *priv, u64 proto) spin_unlock_irq(&rdev->rc_map.lock); mutex_lock(&rdev->lock); - rc_set_enabled_protocols(rdev, proto); - rc_set_allowed_wakeup_protocols(rdev, proto); - rc_set_enabled_wakeup_protocols(rdev, proto); + rdev->enabled_protocols = proto; + rdev->allowed_wakeup_protocols = proto; + rdev->enabled_wakeup_protocols = proto; mutex_unlock(&rdev->lock); } @@ -1003,7 +1003,7 @@ int img_ir_probe_hw(struct img_ir_priv *priv) } rdev->priv = priv; rdev->map_name = RC_MAP_EMPTY; - rc_set_allowed_protocols(rdev, img_ir_allowed_protos(priv)); + rdev->allowed_protocols = img_ir_allowed_protos(priv); rdev->input_name = "IMG Infrared Decoder"; rdev->s_filter = img_ir_set_normal_filter; rdev->s_wakeup_filter = img_ir_set_wakeup_filter; diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index eb37f26..ac05c36 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1017,7 +1017,7 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 *rc_type) unsigned char ir_proto_packet[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 }; - if (*rc_type && !rc_protocols_allowed(rc, *rc_type)) + if (*rc_type && !(*rc_type & rc->allowed_protocols)) dev_warn(dev, "Looks like you're trying to use an IR protocol " "this device does not support\n"); @@ -1870,8 +1870,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx) rdev->priv = ictx; rdev->driver_type = RC_DRIVER_SCANCODE; - /* iMON PAD or MCE */ - rc_set_allowed_protocols(rdev, RC_BIT_OTHER | RC_BIT_RC6_MCE); + rdev->allowed_protocols = RC_BIT_OTHER | RC_BIT_RC6_MCE; /* iMON PAD or MCE */ rdev->change_protocol = imon_ir_change_protocol; rdev->driver_name = MOD_NAME; @@ -1884,7 +1883,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx) if (ictx->product == 0xffdc) { imon_get_ffdc_type(ictx); - rc_set_allowed_protocols(rdev, ictx->rc_type); + rdev->allowed_protocols = ictx->rc_type; } imon_set_display_type(ictx); diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c index 7b79eca..30bcf18 100644 --- a/drivers/media/rc/ir-jvc-decoder.c +++ b/drivers/media/rc/ir-jvc-decoder.c @@ -47,7 +47,7 @@ static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev) { struct jvc_dec *data = &dev->raw->jvc; - if (!rc_protocols_enabled(dev, RC_BIT_JVC)) + if (!(dev->enabled_protocols & RC_BIT_JVC)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index d731da6..ed2c8a1 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -35,7 +35,7 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) struct lirc_codec *lirc = &dev->raw->lirc; int sample; - if (!rc_protocols_enabled(dev, RC_BIT_LIRC)) + if (!(dev->enabled_protocols & RC_BIT_LIRC)) return 0; if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf) diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c index 0c55f79..9f3c9b5 100644 --- a/drivers/media/rc/ir-mce_kbd-decoder.c +++ b/drivers/media/rc/ir-mce_kbd-decoder.c @@ -216,7 +216,7 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; unsigned long delay; - if (!rc_protocols_enabled(dev, RC_BIT_MCE_KBD)) + if (!(dev->enabled_protocols & RC_BIT_MCE_KBD)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index c4333d5..7b81fec 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -52,7 +52,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 address, not_address, command, not_command; bool send_32bits = false; - if (!rc_protocols_enabled(dev, RC_BIT_NEC)) + if (!(dev->enabled_protocols & RC_BIT_NEC)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index c08718e..c72f57c 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -262,7 +262,7 @@ int ir_raw_event_register(struct rc_dev *dev) return -ENOMEM; dev->raw->dev = dev; - rc_set_enabled_protocols(dev, ~0); + dev->enabled_protocols = ~0; dev->change_protocol = change_protocol; rc = kfifo_alloc(&dev->raw->kfifo, sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE, diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 3d38cbc..04ce42f 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -53,7 +53,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; enum rc_type protocol; - if (!rc_protocols_enabled(dev, RC_BIT_RC5 | RC_BIT_RC5X)) + if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X))) return 0; if (!is_timing_event(ev)) { @@ -129,7 +129,7 @@ again: if (data->wanted_bits == RC5X_NBITS) { /* RC5X */ u8 xdata, command, system; - if (!rc_protocols_enabled(dev, RC_BIT_RC5X)) { + if (!(dev->enabled_protocols & RC_BIT_RC5X)) { data->state = STATE_INACTIVE; return 0; } @@ -147,7 +147,7 @@ again: } else { /* RC5 */ u8 command, system; - if (!rc_protocols_enabled(dev, RC_BIT_RC5)) { + if (!(dev->enabled_protocols & RC_BIT_RC5)) { data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rc5-sz-decoder.c b/drivers/media/rc/ir-rc5-sz-decoder.c index 85c7711..771e9fc 100644 --- a/drivers/media/rc/ir-rc5-sz-decoder.c +++ b/drivers/media/rc/ir-rc5-sz-decoder.c @@ -48,7 +48,7 @@ static int ir_rc5_sz_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 toggle, command, system; u32 scancode; - if (!rc_protocols_enabled(dev, RC_BIT_RC5_SZ)) + if (!(dev->enabled_protocols & RC_BIT_RC5_SZ)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index 1dc97a7..f1f098e 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -90,9 +90,9 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 toggle; enum rc_type protocol; - if (!rc_protocols_enabled(dev, RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | - RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | - RC_BIT_RC6_MCE)) + if (!(dev->enabled_protocols & + (RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | + RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE))) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c index 5f77022..ad1dc6a 100644 --- a/drivers/media/rc/ir-sanyo-decoder.c +++ b/drivers/media/rc/ir-sanyo-decoder.c @@ -58,7 +58,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; u8 address, command, not_command; - if (!rc_protocols_enabled(dev, RC_BIT_SANYO)) + if (!(dev->enabled_protocols & RC_BIT_SANYO)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c index c8f2519..b7acdba 100644 --- a/drivers/media/rc/ir-sharp-decoder.c +++ b/drivers/media/rc/ir-sharp-decoder.c @@ -48,7 +48,7 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev) struct sharp_dec *data = &dev->raw->sharp; u32 msg, echo, address, command, scancode; - if (!rc_protocols_enabled(dev, RC_BIT_SHARP)) + if (!(dev->enabled_protocols & RC_BIT_SHARP)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c index f485f9fe..d12dc3d 100644 --- a/drivers/media/rc/ir-sony-decoder.c +++ b/drivers/media/rc/ir-sony-decoder.c @@ -46,8 +46,8 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; u8 device, subdevice, function; - if (!rc_protocols_enabled(dev, RC_BIT_SONY12 | RC_BIT_SONY15 | - RC_BIT_SONY20)) + if (!(dev->enabled_protocols & + (RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20))) return 0; if (!is_timing_event(ev)) { @@ -125,7 +125,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) switch (data->count) { case 12: - if (!rc_protocols_enabled(dev, RC_BIT_SONY12)) { + if (!(dev->enabled_protocols & RC_BIT_SONY12)) { data->state = STATE_INACTIVE; return 0; } @@ -135,7 +135,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) protocol = RC_TYPE_SONY12; break; case 15: - if (!rc_protocols_enabled(dev, RC_BIT_SONY15)) { + if (!(dev->enabled_protocols & RC_BIT_SONY15)) { data->state = STATE_INACTIVE; return 0; } @@ -145,7 +145,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) protocol = RC_TYPE_SONY15; break; case 20: - if (!rc_protocols_enabled(dev, RC_BIT_SONY20)) { + if (!(dev->enabled_protocols & RC_BIT_SONY20)) { data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index ab24cc6..32fd5f4 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1563,7 +1563,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id /* set up ir-core props */ rdev->priv = itdev; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->open = ite_open; rdev->close = ite_close; rdev->s_idle = ite_s_idle; diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index d5c1df3..b1be81f 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1219,7 +1219,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) rc->dev.parent = dev; rc->priv = ir; rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rc, RC_BIT_ALL); + rc->allowed_protocols = RC_BIT_ALL; rc->timeout = MS_TO_NS(100); if (!ir->flags.no_tx) { rc->s_tx_mask = mceusb_set_tx_mask; diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index d244e1a..8c6008f 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1044,7 +1044,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* Set up the rc device */ rdev->priv = nvt; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->open = nvt_open; rdev->close = nvt_close; rdev->tx_ir = nvt_tx_ir; diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index 0a88e0c..63dace8 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -195,7 +195,7 @@ static int __init loop_init(void) rc->map_name = RC_MAP_EMPTY; rc->priv = &loopdev; rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rc, RC_BIT_ALL); + rc->allowed_protocols = RC_BIT_ALL; rc->timeout = 100 * 1000 * 1000; /* 100 ms */ rc->min_timeout = 1; rc->max_timeout = UINT_MAX; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index d5bfe64..c3fb8f8 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -857,14 +857,14 @@ static ssize_t show_protocols(struct device *device, mutex_lock(&dev->lock); if (fattr->type == RC_FILTER_NORMAL) { - enabled = dev->enabled_protocols[RC_FILTER_NORMAL]; + enabled = dev->enabled_protocols; if (dev->raw) allowed = ir_raw_get_allowed_protocols(); else - allowed = dev->allowed_protocols[RC_FILTER_NORMAL]; + allowed = dev->allowed_protocols; } else { - enabled = dev->enabled_protocols[RC_FILTER_WAKEUP]; - allowed = dev->allowed_protocols[RC_FILTER_WAKEUP]; + enabled = dev->enabled_wakeup_protocols; + allowed = dev->allowed_wakeup_protocols; } mutex_unlock(&dev->lock); @@ -989,15 +989,15 @@ static ssize_t store_protocols(struct device *device, if (fattr->type == RC_FILTER_NORMAL) { IR_dprintk(1, "Normal protocol change requested\n"); - current_protocols = &dev->enabled_protocols[RC_FILTER_NORMAL]; + current_protocols = &dev->enabled_protocols; change_protocol = dev->change_protocol; - filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + filter = &dev->scancode_filter; set_filter = dev->s_filter; } else { IR_dprintk(1, "Wakeup protocol change requested\n"); - current_protocols = &dev->enabled_protocols[RC_FILTER_WAKEUP]; + current_protocols = &dev->enabled_wakeup_protocols; change_protocol = dev->change_wakeup_protocol; - filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + filter = &dev->scancode_wakeup_filter; set_filter = dev->s_wakeup_filter; } @@ -1085,9 +1085,9 @@ static ssize_t show_filter(struct device *device, return -EINVAL; if (fattr->type == RC_FILTER_NORMAL) - filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + filter = &dev->scancode_filter; else - filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + filter = &dev->scancode_wakeup_filter; mutex_lock(&dev->lock); if (fattr->mask) @@ -1140,12 +1140,12 @@ static ssize_t store_filter(struct device *device, if (fattr->type == RC_FILTER_NORMAL) { set_filter = dev->s_filter; - enabled_protocols = &dev->enabled_protocols[RC_FILTER_NORMAL]; - filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + enabled_protocols = &dev->enabled_protocols; + filter = &dev->scancode_filter; } else { set_filter = dev->s_wakeup_filter; - enabled_protocols = &dev->enabled_protocols[RC_FILTER_WAKEUP]; - filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + enabled_protocols = &dev->enabled_wakeup_protocols; + filter = &dev->scancode_wakeup_filter; } if (!set_filter) @@ -1424,7 +1424,7 @@ int rc_register_device(struct rc_dev *dev) rc = dev->change_protocol(dev, &rc_type); if (rc < 0) goto out_raw; - dev->enabled_protocols[RC_FILTER_NORMAL] = rc_type; + dev->enabled_protocols = rc_type; } mutex_unlock(&dev->lock); diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 79abbc8..795b394 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -878,7 +878,7 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3) rc->dev.parent = dev; rc->priv = rr3; rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rc, RC_BIT_ALL); + rc->allowed_protocols = RC_BIT_ALL; rc->timeout = US_TO_NS(2750); rc->tx_ir = redrat3_transmit_ir; rc->s_tx_carrier = redrat3_set_tx_carrier; diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c index 22e4c1f..5c15135 100644 --- a/drivers/media/rc/st_rc.c +++ b/drivers/media/rc/st_rc.c @@ -287,7 +287,7 @@ static int st_rc_probe(struct platform_device *pdev) st_rc_hardware_init(rc_dev); rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; /* rx sampling rate is 10Mhz */ rdev->rx_resolution = 100; rdev->timeout = US_TO_NS(MAX_SYMB_TIME); diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index bd5e4ff..828bbb8 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -316,7 +316,7 @@ static struct rc_dev *streamzap_init_rc_dev(struct streamzap_ir *sz) rdev->dev.parent = dev; rdev->priv = sz; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->driver_name = DRIVER_NAME; rdev->map_name = RC_MAP_STREAMZAP; diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 13dcd80..bcee8e1 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -210,7 +210,7 @@ static int sunxi_ir_probe(struct platform_device *pdev) ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY; ir->rc->dev.parent = dev; ir->rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(ir->rc, RC_BIT_ALL); + ir->rc->allowed_protocols = RC_BIT_ALL; ir->rc->rx_resolution = SUNXI_IR_SAMPLE; ir->rc->timeout = MS_TO_NS(SUNXI_IR_TIMEOUT); ir->rc->driver_name = SUNXI_IR_DEV; diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index c5be38e..bc214e2 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -318,7 +318,7 @@ static int ttusbir_probe(struct usb_interface *intf, usb_to_input_id(tt->udev, &rc->input_id); rc->dev.parent = &intf->dev; rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rc, RC_BIT_ALL); + rc->allowed_protocols = RC_BIT_ALL; rc->priv = tt; rc->driver_name = DRIVER_NAME; rc->map_name = RC_MAP_TT_1500; diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index a8b981f..d839f73 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1082,7 +1082,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) data->dev->dev.parent = &device->dev; data->dev->timeout = MS_TO_NS(100); data->dev->rx_resolution = US_TO_NS(2); - rc_set_allowed_protocols(data->dev, RC_BIT_ALL); + data->dev->allowed_protocols = RC_BIT_ALL; err = rc_register_device(data->dev); if (err) diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index e355806..45f5ee9 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -164,7 +164,7 @@ static int dvb_usbv2_remote_init(struct dvb_usb_device *d) dev->driver_name = (char *) d->props->driver_name; dev->map_name = d->rc.map_name; dev->driver_type = d->rc.driver_type; - rc_set_allowed_protocols(dev, d->rc.allowed_protos); + dev->allowed_protocols = d->rc.allowed_protos; dev->change_protocol = d->rc.change_protocol; dev->priv = d; diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c index 4058aea..7b5dae3 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c @@ -272,7 +272,7 @@ static int rc_core_dvb_usb_remote_init(struct dvb_usb_device *d) dev->driver_name = d->props.rc.core.module_name; dev->map_name = d->props.rc.core.rc_codes; dev->change_protocol = d->props.rc.core.change_protocol; - rc_set_allowed_protocols(dev, d->props.rc.core.allowed_protos); + dev->allowed_protocols = d->props.rc.core.allowed_protos; dev->driver_type = d->props.rc.core.driver_type; usb_to_input_id(d->udev, &dev->input_id); dev->input_name = "IR-receiver inside an USB DVB receiver"; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 7f06ae5..ed843bd 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -745,7 +745,7 @@ static int em28xx_ir_init(struct em28xx *dev) case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: rc->map_name = RC_MAP_HAUPPAUGE; ir->get_key_i2c = em28xx_get_key_em_haup; - rc_set_allowed_protocols(rc, RC_BIT_RC5); + rc->allowed_protocols = RC_BIT_RC5; break; case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: rc->map_name = RC_MAP_WINFAST_USBII_DELUXE; @@ -761,7 +761,7 @@ static int em28xx_ir_init(struct em28xx *dev) switch (dev->chip_id) { case CHIP_ID_EM2860: case CHIP_ID_EM2883: - rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC); + rc->allowed_protocols = RC_BIT_RC5 | RC_BIT_NEC; ir->get_key = default_polling_getkey; break; case CHIP_ID_EM2884: @@ -769,8 +769,8 @@ static int em28xx_ir_init(struct em28xx *dev) case CHIP_ID_EM28174: case CHIP_ID_EM28178: ir->get_key = em2874_polling_getkey; - rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC | - RC_BIT_RC6_0); + rc->allowed_protocols = RC_BIT_RC5 | RC_BIT_NEC | + RC_BIT_RC6_0; break; default: err = -ENODEV; diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c index 676c0232..8a519f5 100644 --- a/drivers/media/usb/tm6000/tm6000-input.c +++ b/drivers/media/usb/tm6000/tm6000-input.c @@ -441,7 +441,7 @@ int tm6000_ir_init(struct tm6000_core *dev) ir->rc = rc; /* input setup */ - rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC); + rc->allowed_protocols = RC_BIT_RC5 | RC_BIT_NEC; /* Neded, in order to support NEC remotes with 24 or 32 bits */ rc->scanmask = 0xffff; rc->priv = ir; diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 00811c9e2..4f9e187 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -74,10 +74,12 @@ enum rc_filter_type { * @input_dev: the input child device used to communicate events to userspace * @driver_type: specifies if protocol decoding is done in hardware or software * @idle: used to keep track of RX state - * @allowed_protocols: bitmask with the supported RC_BIT_* protocols for each - * filter type - * @enabled_protocols: bitmask with the enabled RC_BIT_* protocols for each - * filter type + * @allowed_protocols: bitmask with the supported RC_BIT_* protocols + * @enabled_protocols: bitmask with the enabled RC_BIT_* protocols + * @allowed_wakeup_protocols: bitmask with the supported RC_BIT_* wakeup protocols + * @enabled_wakeup_protocols: bitmask with the enabled RC_BIT_* wakeup protocols + * @scancode_filter: scancode filter + * @scancode_wakeup_filter: scancode wakeup filters * @scanmask: some hardware decoders are not capable of providing the full * scancode to the application. As this is a hardware limit, we can't do * anything with it. Yet, as the same keycode table can be used with other @@ -97,7 +99,6 @@ enum rc_filter_type { * @max_timeout: maximum timeout supported by device * @rx_resolution : resolution (in ns) of input sampler * @tx_resolution: resolution (in ns) of output sampler - * @scancode_filters: scancode filters (indexed by enum rc_filter_type) * @change_protocol: allow changing the protocol used on hardware decoders * @change_wakeup_protocol: allow changing the protocol used for wakeup * filtering @@ -132,8 +133,12 @@ struct rc_dev { struct input_dev *input_dev; enum rc_driver_type driver_type; bool idle; - u64 allowed_protocols[RC_FILTER_MAX]; - u64 enabled_protocols[RC_FILTER_MAX]; + u64 allowed_protocols; + u64 enabled_protocols; + u64 allowed_wakeup_protocols; + u64 enabled_wakeup_protocols; + struct rc_scancode_filter scancode_filter; + struct rc_scancode_filter scancode_wakeup_filter; u32 users; u32 scanmask; void *priv; @@ -150,7 +155,6 @@ struct rc_dev { u32 max_timeout; u32 rx_resolution; u32 tx_resolution; - struct rc_scancode_filter scancode_filters[RC_FILTER_MAX]; int (*change_protocol)(struct rc_dev *dev, u64 *rc_type); int (*change_wakeup_protocol)(struct rc_dev *dev, u64 *rc_type); int (*open)(struct rc_dev *dev); @@ -171,42 +175,6 @@ struct rc_dev { #define to_rc_dev(d) container_of(d, struct rc_dev, dev) -static inline bool rc_protocols_allowed(struct rc_dev *rdev, u64 protos) -{ - return rdev->allowed_protocols[RC_FILTER_NORMAL] & protos; -} - -/* should be called prior to registration or with mutex held */ -static inline void rc_set_allowed_protocols(struct rc_dev *rdev, u64 protos) -{ - rdev->allowed_protocols[RC_FILTER_NORMAL] = protos; -} - -static inline bool rc_protocols_enabled(struct rc_dev *rdev, u64 protos) -{ - return rdev->enabled_protocols[RC_FILTER_NORMAL] & protos; -} - -/* should be called prior to registration or with mutex held */ -static inline void rc_set_enabled_protocols(struct rc_dev *rdev, u64 protos) -{ - rdev->enabled_protocols[RC_FILTER_NORMAL] = protos; -} - -/* should be called prior to registration or with mutex held */ -static inline void rc_set_allowed_wakeup_protocols(struct rc_dev *rdev, - u64 protos) -{ - rdev->allowed_protocols[RC_FILTER_WAKEUP] = protos; -} - -/* should be called prior to registration or with mutex held */ -static inline void rc_set_enabled_wakeup_protocols(struct rc_dev *rdev, - u64 protos) -{ - rdev->enabled_protocols[RC_FILTER_WAKEUP] = protos; -} - /* * From rc-main.c * Those functions can be used on any type of Remote Controller. They -- cgit v0.10.2 From 9d2f1d3cdedd4d3efff8d14f1f49cf73e2f5cc36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 3 Apr 2014 20:32:26 -0300 Subject: [media] rc-core: rename dev->scanmask to dev->scancode_mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have dev->scancode_filter and dev->scancode_wakeup_filter so rename dev->scanmask to dev->scancode_mask for consistency. Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index 93ff6a7..3f1342c 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -478,7 +478,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) dev->priv = core; dev->open = cx88_ir_open; dev->close = cx88_ir_close; - dev->scanmask = hardware_mask; + dev->scancode_mask = hardware_mask; if (ir->sampling) { dev->driver_type = RC_DRIVER_IR_RAW; diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c index 41ce7de..1feeeff 100644 --- a/drivers/media/pci/ttpci/budget-ci.c +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -234,7 +234,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) break; } if (!budget_ci->ir.full_rc5) - dev->scanmask = 0xff; + dev->scancode_mask = 0xff; error = rc_register_device(dev); if (error) { diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index c3fb8f8..39e4b5f 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -285,8 +285,8 @@ static unsigned int ir_establish_scancode(struct rc_dev *dev, * IR tables from other remotes. So, we support specifying a mask to * indicate the valid bits of the scancodes. */ - if (dev->scanmask) - scancode &= dev->scanmask; + if (dev->scancode_mask) + scancode &= dev->scancode_mask; /* First check if we already have a mapping for this ir command */ for (i = 0; i < rc_map->len; i++) { diff --git a/drivers/media/usb/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c index adcdd92..05f0434 100644 --- a/drivers/media/usb/cx231xx/cx231xx-input.c +++ b/drivers/media/usb/cx231xx/cx231xx-input.c @@ -91,7 +91,7 @@ int cx231xx_ir_init(struct cx231xx *dev) dev->init_data.get_key = get_key_isdbt; dev->init_data.ir_codes = cx231xx_boards[dev->model].rc_map_name; /* The i2c micro-controller only outputs the cmd part of NEC protocol */ - dev->init_data.rc_dev->scanmask = 0xff; + dev->init_data.rc_dev->scancode_mask = 0xff; dev->init_data.rc_dev->driver_name = "cx231xx"; dev->init_data.type = RC_BIT_NEC; info.addr = 0x30; diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c index 8a519f5..26b2ebb 100644 --- a/drivers/media/usb/tm6000/tm6000-input.c +++ b/drivers/media/usb/tm6000/tm6000-input.c @@ -443,7 +443,7 @@ int tm6000_ir_init(struct tm6000_core *dev) /* input setup */ rc->allowed_protocols = RC_BIT_RC5 | RC_BIT_NEC; /* Neded, in order to support NEC remotes with 24 or 32 bits */ - rc->scanmask = 0xffff; + rc->scancode_mask = 0xffff; rc->priv = ir; rc->change_protocol = tm6000_ir_change_protocol; if (dev->int_in.endp) { diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 4f9e187..3047837 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -80,11 +80,12 @@ enum rc_filter_type { * @enabled_wakeup_protocols: bitmask with the enabled RC_BIT_* wakeup protocols * @scancode_filter: scancode filter * @scancode_wakeup_filter: scancode wakeup filters - * @scanmask: some hardware decoders are not capable of providing the full + * @scancode_mask: some hardware decoders are not capable of providing the full * scancode to the application. As this is a hardware limit, we can't do * anything with it. Yet, as the same keycode table can be used with other * devices, a mask is provided to allow its usage. Drivers should generally * leave this field in blank + * @users: number of current users of the device * @priv: driver-specific data * @keylock: protects the remaining members of the struct * @keypressed: whether a key is currently pressed @@ -139,8 +140,8 @@ struct rc_dev { u64 enabled_wakeup_protocols; struct rc_scancode_filter scancode_filter; struct rc_scancode_filter scancode_wakeup_filter; + u32 scancode_mask; u32 users; - u32 scanmask; void *priv; spinlock_t keylock; bool keypressed; -- cgit v0.10.2 From e87b540be2dd02552fb9244d50ae8b4e4619a34b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 3 Apr 2014 20:32:31 -0300 Subject: [media] rc-core: merge rc5 and streamzap decoders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the protocol is part of the scancode, it is pretty easy to merge the rc5 and streamzap decoders. An additional advantage is that the decoder is now stricter as it waits for the trailing silence before determining that a command is a valid rc5/streamzap command (which avoids collisions that I've seen with e.g. Sony protocols). Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 9427fad..9d15a57 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -84,18 +84,6 @@ config IR_SONY_DECODER Enable this option if you have an infrared remote control which uses the Sony protocol, and you need software decoding support. -config IR_RC5_SZ_DECODER - tristate "Enable IR raw decoder for the RC-5 (streamzap) protocol" - depends on RC_CORE - select BITREVERSE - default y - - ---help--- - Enable this option if you have IR with RC-5 (streamzap) protocol, - and if the IR is decoded in software. (The Streamzap PC Remote - uses an IR protocol that is almost standard RC-5, but not quite, - as it uses an additional bit). - config IR_SANYO_DECODER tristate "Enable IR raw decoder for the Sanyo protocol" depends on RC_CORE diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 9ee9ee7..d07576f 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o -obj-$(CONFIG_IR_RC5_SZ_DECODER) += ir-rc5-sz-decoder.o obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 04ce42f..93168da 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -1,6 +1,7 @@ -/* ir-rc5-decoder.c - handle RC5(x) IR Pulse/Space protocol +/* ir-rc5-decoder.c - decoder for RC5(x) and StreamZap protocols * * Copyright (C) 2010 by Mauro Carvalho Chehab + * Copyright (C) 2010 by Jarod Wilson * * 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 @@ -13,23 +14,22 @@ */ /* - * This code handles 14 bits RC5 protocols and 20 bits RC5x protocols. - * There are other variants that use a different number of bits. - * This is currently unsupported. - * It considers a carrier of 36 kHz, with a total of 14/20 bits, where - * the first two bits are start bits, and a third one is a filing bit + * This decoder handles the 14 bit RC5 protocol, 15 bit "StreamZap" protocol + * and 20 bit RC5x protocol. */ #include "rc-core-priv.h" #include #define RC5_NBITS 14 +#define RC5_SZ_NBITS 15 #define RC5X_NBITS 20 #define CHECK_RC5X_NBITS 8 #define RC5_UNIT 888888 /* ns */ #define RC5_BIT_START (1 * RC5_UNIT) #define RC5_BIT_END (1 * RC5_UNIT) #define RC5X_SPACE (4 * RC5_UNIT) +#define RC5_TRAILER (10 * RC5_UNIT) /* In reality, approx 100 */ enum rc5_state { STATE_INACTIVE, @@ -66,7 +66,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev) goto out; again: - IR_dprintk(2, "RC5(x) decode started at state %i (%uus %s)\n", + IR_dprintk(2, "RC5(x/sz) decode started at state %i (%uus %s)\n", data->state, TO_US(ev.duration), TO_STR(ev.pulse)); if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2)) @@ -80,12 +80,15 @@ again: data->state = STATE_BIT_START; data->count = 1; - /* We just need enough bits to get to STATE_CHECK_RC5X */ - data->wanted_bits = RC5X_NBITS; decrease_duration(&ev, RC5_BIT_START); goto again; case STATE_BIT_START: + if (!ev.pulse && geq_margin(ev.duration, RC5_TRAILER, RC5_UNIT / 2)) { + data->state = STATE_FINISHED; + goto again; + } + if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2)) break; @@ -100,9 +103,7 @@ again: if (!is_transition(&ev, &dev->raw->prev_ev)) break; - if (data->count == data->wanted_bits) - data->state = STATE_FINISHED; - else if (data->count == CHECK_RC5X_NBITS) + if (data->count == CHECK_RC5X_NBITS) data->state = STATE_CHECK_RC5X; else data->state = STATE_BIT_START; @@ -112,13 +113,10 @@ again: case STATE_CHECK_RC5X: if (!ev.pulse && geq_margin(ev.duration, RC5X_SPACE, RC5_UNIT / 2)) { - /* RC5X */ - data->wanted_bits = RC5X_NBITS; + data->is_rc5x = true; decrease_duration(&ev, RC5X_SPACE); - } else { - /* RC5 */ - data->wanted_bits = RC5_NBITS; - } + } else + data->is_rc5x = false; data->state = STATE_BIT_START; goto again; @@ -126,7 +124,7 @@ again: if (ev.pulse) break; - if (data->wanted_bits == RC5X_NBITS) { + if (data->is_rc5x && data->count == RC5X_NBITS) { /* RC5X */ u8 xdata, command, system; if (!(dev->enabled_protocols & RC_BIT_RC5X)) { @@ -141,10 +139,7 @@ again: scancode = system << 16 | command << 8 | xdata; protocol = RC_TYPE_RC5X; - IR_dprintk(1, "RC5X scancode 0x%06x (toggle: %u)\n", - scancode, toggle); - - } else { + } else if (!data->is_rc5x && data->count == RC5_NBITS) { /* RC5 */ u8 command, system; if (!(dev->enabled_protocols & RC_BIT_RC5)) { @@ -158,9 +153,24 @@ again: scancode = system << 8 | command; protocol = RC_TYPE_RC5; - IR_dprintk(1, "RC5 scancode 0x%04x (toggle: %u)\n", - scancode, toggle); - } + } else if (!data->is_rc5x && data->count == RC5_SZ_NBITS) { + /* RC5 StreamZap */ + u8 command, system; + if (!(dev->enabled_protocols & RC_BIT_RC5_SZ)) { + data->state = STATE_INACTIVE; + return 0; + } + command = (data->bits & 0x0003F) >> 0; + system = (data->bits & 0x02FC0) >> 6; + toggle = (data->bits & 0x01000) ? 1 : 0; + scancode = system << 6 | command; + protocol = RC_TYPE_RC5_SZ; + + } else + break; + + IR_dprintk(1, "RC5(x/sz) scancode 0x%06x (p: %u, t: %u)\n", + scancode, protocol, toggle); rc_keydown(dev, protocol, scancode, toggle); data->state = STATE_INACTIVE; @@ -168,14 +178,14 @@ again: } out: - IR_dprintk(1, "RC5(x) decode failed at state %i (%uus %s)\n", + IR_dprintk(1, "RC5(x/sz) decode failed at state %i (%uus %s)\n", data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } static struct ir_raw_handler rc5_handler = { - .protocols = RC_BIT_RC5 | RC_BIT_RC5X, + .protocols = RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ, .decode = ir_rc5_decode, }; @@ -183,7 +193,7 @@ static int __init ir_rc5_decode_init(void) { ir_raw_handler_register(&rc5_handler); - printk(KERN_INFO "IR RC5(x) protocol handler initialized\n"); + printk(KERN_INFO "IR RC5(x/sz) protocol handler initialized\n"); return 0; } @@ -196,6 +206,6 @@ module_init(ir_rc5_decode_init); module_exit(ir_rc5_decode_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_AUTHOR("Mauro Carvalho Chehab and Jarod Wilson"); MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); -MODULE_DESCRIPTION("RC5(x) IR protocol decoder"); +MODULE_DESCRIPTION("RC5(x/sz) IR protocol decoder"); diff --git a/drivers/media/rc/ir-rc5-sz-decoder.c b/drivers/media/rc/ir-rc5-sz-decoder.c deleted file mode 100644 index 771e9fc..0000000 --- a/drivers/media/rc/ir-rc5-sz-decoder.c +++ /dev/null @@ -1,154 +0,0 @@ -/* ir-rc5-sz-decoder.c - handle RC5 Streamzap IR Pulse/Space protocol - * - * Copyright (C) 2010 by Mauro Carvalho Chehab - * Copyright (C) 2010 by Jarod Wilson - * - * 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 version 2 of the License. - * - * 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. - */ - -/* - * This code handles the 15 bit RC5-ish protocol used by the Streamzap - * PC Remote. - * It considers a carrier of 36 kHz, with a total of 15 bits, where - * the first two bits are start bits, and a third one is a filing bit - */ - -#include "rc-core-priv.h" -#include - -#define RC5_SZ_NBITS 15 -#define RC5_UNIT 888888 /* ns */ -#define RC5_BIT_START (1 * RC5_UNIT) -#define RC5_BIT_END (1 * RC5_UNIT) - -enum rc5_sz_state { - STATE_INACTIVE, - STATE_BIT_START, - STATE_BIT_END, - STATE_FINISHED, -}; - -/** - * ir_rc5_sz_decode() - Decode one RC-5 Streamzap pulse or space - * @dev: the struct rc_dev descriptor of the device - * @ev: the struct ir_raw_event descriptor of the pulse/space - * - * This function returns -EINVAL if the pulse violates the state machine - */ -static int ir_rc5_sz_decode(struct rc_dev *dev, struct ir_raw_event ev) -{ - struct rc5_sz_dec *data = &dev->raw->rc5_sz; - u8 toggle, command, system; - u32 scancode; - - if (!(dev->enabled_protocols & RC_BIT_RC5_SZ)) - return 0; - - if (!is_timing_event(ev)) { - if (ev.reset) - data->state = STATE_INACTIVE; - return 0; - } - - if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2)) - goto out; - -again: - IR_dprintk(2, "RC5-sz decode started at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); - - if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2)) - return 0; - - switch (data->state) { - - case STATE_INACTIVE: - if (!ev.pulse) - break; - - data->state = STATE_BIT_START; - data->count = 1; - data->wanted_bits = RC5_SZ_NBITS; - decrease_duration(&ev, RC5_BIT_START); - goto again; - - case STATE_BIT_START: - if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2)) - break; - - data->bits <<= 1; - if (!ev.pulse) - data->bits |= 1; - data->count++; - data->state = STATE_BIT_END; - return 0; - - case STATE_BIT_END: - if (!is_transition(&ev, &dev->raw->prev_ev)) - break; - - if (data->count == data->wanted_bits) - data->state = STATE_FINISHED; - else - data->state = STATE_BIT_START; - - decrease_duration(&ev, RC5_BIT_END); - goto again; - - case STATE_FINISHED: - if (ev.pulse) - break; - - /* RC5-sz */ - command = (data->bits & 0x0003F) >> 0; - system = (data->bits & 0x02FC0) >> 6; - toggle = (data->bits & 0x01000) ? 1 : 0; - scancode = system << 6 | command; - - IR_dprintk(1, "RC5-sz scancode 0x%04x (toggle: %u)\n", - scancode, toggle); - - rc_keydown(dev, RC_TYPE_RC5_SZ, scancode, toggle); - data->state = STATE_INACTIVE; - return 0; - } - -out: - IR_dprintk(1, "RC5-sz decode failed at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); - data->state = STATE_INACTIVE; - return -EINVAL; -} - -static struct ir_raw_handler rc5_sz_handler = { - .protocols = RC_BIT_RC5_SZ, - .decode = ir_rc5_sz_decode, -}; - -static int __init ir_rc5_sz_decode_init(void) -{ - ir_raw_handler_register(&rc5_sz_handler); - - printk(KERN_INFO "IR RC5 (streamzap) protocol handler initialized\n"); - return 0; -} - -static void __exit ir_rc5_sz_decode_exit(void) -{ - ir_raw_handler_unregister(&rc5_sz_handler); -} - -module_init(ir_rc5_sz_decode_init); -module_exit(ir_rc5_sz_decode_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jarod Wilson "); -MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); -MODULE_DESCRIPTION("RC5 (streamzap) IR protocol decoder"); diff --git a/drivers/media/rc/keymaps/rc-streamzap.c b/drivers/media/rc/keymaps/rc-streamzap.c index f9a0757..23c0611 100644 --- a/drivers/media/rc/keymaps/rc-streamzap.c +++ b/drivers/media/rc/keymaps/rc-streamzap.c @@ -15,9 +15,7 @@ static struct rc_map_table streamzap[] = { /* * The Streamzap remote is almost, but not quite, RC-5, as it has an extra - * bit in it, which throws the in-kernel RC-5 decoder for a loop. Currently, - * an additional RC-5-sz decoder is being deployed to support it, but it - * may be possible to merge it back with the standard RC-5 decoder. + * bit in it. */ { 0x28c0, KEY_NUMERIC_0 }, { 0x28c1, KEY_NUMERIC_1 }, diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index da536c9..dea7aff 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -54,7 +54,7 @@ struct ir_raw_event_ctrl { int state; u32 bits; unsigned count; - unsigned wanted_bits; + bool is_rc5x; } rc5; struct rc6_dec { int state; @@ -77,12 +77,6 @@ struct ir_raw_event_ctrl { bool first; bool toggle; } jvc; - struct rc5_sz_dec { - int state; - u32 bits; - unsigned count; - unsigned wanted_bits; - } rc5_sz; struct sanyo_dec { int state; unsigned count; diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index 828bbb8..80c4fee 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -63,13 +63,6 @@ MODULE_DEVICE_TABLE(usb, streamzap_table); /* number of samples buffered */ #define SZ_BUF_LEN 128 -/* from ir-rc5-sz-decoder.c */ -#ifdef CONFIG_IR_RC5_SZ_DECODER_MODULE -#define load_rc5_sz_decode() request_module("ir-rc5-sz-decoder") -#else -#define load_rc5_sz_decode() {} -#endif - enum StreamzapDecoderState { PulseSpace, FullPulse, @@ -452,9 +445,6 @@ static int streamzap_probe(struct usb_interface *intf, dev_info(sz->dev, "Registered %s on usb%d:%d\n", name, usbdev->bus->busnum, usbdev->devnum); - /* Load the streamzap not-quite-rc5 decoder too */ - load_rc5_sz_decode(); - return 0; rc_dev_fail: -- cgit v0.10.2 From 592d1349f719db7c071ba9f4ade272be114b4196 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:16:47 -0300 Subject: [media] Docbook/media: improve data_offset/bytesused documentation This patch explicitly documents the relationship between bytesused and data_offset. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index 8c4ee74..e5e8325 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -870,7 +870,8 @@ should set this to 0. If the application sets this to 0 for an output stream, then bytesused will be set to the size of the plane (see the length field of this struct) - by the driver. + by the driver. Note that the actual image data starts at + data_offset which may not be 0. __u32 @@ -919,6 +920,10 @@ should set this to 0. Offset in bytes to video data in the plane. Drivers must set this field when type refers to an input stream, applications when it refers to an output stream. + Note that data_offset is included in bytesused. + So the size of the image in the plane is + bytesused-data_offset at + offset data_offset from the start of the plane. -- cgit v0.10.2 From 5d0360a4f027576e5419d4a7c711c9ca0f1be8ca Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:42 -0300 Subject: [media] v4l2-ctrls: add support for setting string controls Rather than always having to use a v4l2_ext_control struct to set a control value from within a driver, switch to just setting the new value. This is faster and it makes it possible to set more complex types such as a string control as is added by this patch. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 004e7e8..5db0385 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3121,26 +3121,22 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, struct v4l2_ctrl *master = ctrl->cluster[0]; int i; - /* Compound controls are not supported. The user_to_new() and - * cur_to_user() calls below would need to be modified not to access - * userspace memory when called from set_ctrl(). - */ - if (ctrl->is_ptr) - return -EINVAL; - /* Reset the 'is_new' flags of the cluster */ for (i = 0; i < master->ncontrols; i++) if (master->cluster[i]) master->cluster[i]->is_new = 0; + if (c) + user_to_new(c, ctrl); + /* For autoclusters with volatiles that are switched from auto to manual mode we have to update the current volatile values since those will become the initial manual values after such a switch. */ if (master->is_auto && master->has_volatiles && ctrl == master && - !is_cur_manual(master) && c->value == master->manual_mode_value) + !is_cur_manual(master) && ctrl->val == master->manual_mode_value) update_from_auto_cluster(master); - user_to_new(c, ctrl); + ctrl->is_new = 1; return try_or_set_cluster(fh, master, true, ch_flags); } @@ -3188,40 +3184,37 @@ EXPORT_SYMBOL(v4l2_subdev_s_ctrl); int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) { - struct v4l2_ext_control c; - int rval; - lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(!ctrl->is_int); - c.value = val; - rval = set_ctrl(NULL, ctrl, &c, 0); - if (!rval) - cur_to_user(&c, ctrl); - - return rval; + ctrl->val = val; + return set_ctrl(NULL, ctrl, NULL, 0); } EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl); int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) { - struct v4l2_ext_control c; - int rval; - lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); - c.value64 = val; - rval = set_ctrl(NULL, ctrl, &c, 0); - if (!rval) - cur_to_user(&c, ctrl); - - return rval; + *ctrl->p_new.p_s64 = val; + return set_ctrl(NULL, ctrl, NULL, 0); } EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_int64); +int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) +{ + lockdep_assert_held(ctrl->handler->lock); + + /* It's a driver bug if this happens. */ + WARN_ON(ctrl->type != V4L2_CTRL_TYPE_STRING); + strlcpy(ctrl->p_new.p_char, s, ctrl->maximum + 1); + return set_ctrl(NULL, ctrl, NULL, 0); +} +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string); + void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv) { if (ctrl == NULL) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 8c4edd6..d6540d2 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -737,6 +737,30 @@ static inline int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) return rval; } +/** __v4l2_ctrl_s_ctrl_string() - Unlocked variant of v4l2_ctrl_s_ctrl_string(). */ +int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s); + +/** v4l2_ctrl_s_ctrl_string() - Helper function to set a control's string value from within a driver. + * @ctrl: The control. + * @s: The new string. + * + * This set the control's new string safely by going through the control + * framework. This function will lock the control's handler, so it cannot be + * used from within the &v4l2_ctrl_ops functions. + * + * This function is for string type controls only. + */ +static inline int v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) +{ + int rval; + + v4l2_ctrl_lock(ctrl); + rval = __v4l2_ctrl_s_ctrl_string(ctrl, s); + v4l2_ctrl_unlock(ctrl); + + return rval; +} + /* Internal helper functions that deal with control events. */ extern const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops; void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new); -- cgit v0.10.2 From ccf58cb4eac050ebe777cfcb209bc53d58f5fd79 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 23 Jul 2014 03:17:06 -0300 Subject: [media] vb2: fix videobuf2-core.h comments A lot of work was done in vb2 to regulate how drivers and the vb2 core handle buffer ownership, but inexplicably the videobuf2-core.h comments were never updated. Do so now. The same was true for the replacement of the -ENOBUFS mechanism by the min_buffers_needed field. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 1a262ae..fc910a6 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -294,15 +294,19 @@ struct vb2_buffer { * of already queued buffers in count parameter; driver * can return an error if hardware fails, in that case all * buffers that have been already given by the @buf_queue - * callback are invalidated. - * If there were not enough queued buffers to start - * streaming, then this callback returns -ENOBUFS, and the - * vb2 core will retry calling @start_streaming when a new - * buffer is queued. + * callback are to be returned by the driver by calling + * @vb2_buffer_done(VB2_BUF_STATE_DEQUEUED). + * If you need a minimum number of buffers before you can + * start streaming, then set @min_buffers_needed in the + * vb2_queue structure. If that is non-zero then + * start_streaming won't be called until at least that + * many buffers have been queued up by userspace. * @stop_streaming: called when 'streaming' state must be disabled; driver * should stop any DMA transactions or wait until they * finish and give back all buffers it got from buf_queue() - * callback; may use vb2_wait_for_all_buffers() function + * callback by calling @vb2_buffer_done() with either + * VB2_BUF_STATE_DONE or VB2_BUF_STATE_ERROR; may use + * vb2_wait_for_all_buffers() function * @buf_queue: passes buffer vb to the driver; driver may start * hardware operation on this buffer; driver should give * the buffer back by calling vb2_buffer_done() function; -- cgit v0.10.2 From 1612f20ea025f55741b2d6e8b9c91047a7324a76 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 24 Jul 2014 09:19:37 -0300 Subject: [media] vb2: fix vb2_poll for output streams vb2_poll should always return POLLOUT | POLLWRNORM as long as there are fewer buffers queued than there are buffers available. Poll for an output stream should only wait if all buffers are queued and nobody is dequeuing them. Signed-off-by: Hans Verkuil Acked-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index f33508f..c359006 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -2596,6 +2596,13 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) if ((list_empty(&q->queued_list) && !vb2_is_streaming(q)) || q->error) return res | POLLERR; + /* + * For output streams you can write as long as there are fewer buffers + * queued than there are buffers available. + */ + if (V4L2_TYPE_IS_OUTPUT(q->type) && q->queued_count < q->num_buffers) + return res | POLLOUT | POLLWRNORM; + if (list_empty(&q->done_list)) poll_wait(file, &q->done_wq, wait); -- cgit v0.10.2 From 811c508104d092683ea5fe86cdcfd23dded22934 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:37 -0300 Subject: [media] v4l2-ctrls: add new RDS TX controls The si4713 supports several RDS features not yet implemented in the driver. This patch adds the missing RDS functionality to the list of RDS controls. The ALT_FREQS control is a compound control containing an array of up to 25 (the maximum according to the RDS standard) frequencies. To support that the V4L2_CTRL_TYPE_U32 was added. Signed-off-by: Hans Verkuil Cc: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5db0385..4863cf0 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -805,6 +805,15 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_RDS_TX_PTY: return "RDS Program Type"; case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name"; case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text"; + case V4L2_CID_RDS_TX_MONO_STEREO: return "RDS Stereo"; + case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD: return "RDS Artificial Head"; + case V4L2_CID_RDS_TX_COMPRESSED: return "RDS Compressed"; + case V4L2_CID_RDS_TX_DYNAMIC_PTY: return "RDS Dynamic PTY"; + case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: return "RDS Traffic Announcement"; + case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: return "RDS Traffic Program"; + case V4L2_CID_RDS_TX_MUSIC_SPEECH: return "RDS Music"; + case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: return "RDS Enable Alt Frequencies"; + case V4L2_CID_RDS_TX_ALT_FREQS: return "RDS Alternate Frequencies"; case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled"; case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time"; case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation"; @@ -946,6 +955,14 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RF_TUNER_IF_GAIN_AUTO: case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: case V4L2_CID_RF_TUNER_PLL_LOCK: + case V4L2_CID_RDS_TX_MONO_STEREO: + case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD: + case V4L2_CID_RDS_TX_COMPRESSED: + case V4L2_CID_RDS_TX_DYNAMIC_PTY: + case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: + case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: + case V4L2_CID_RDS_TX_MUSIC_SPEECH: + case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; @@ -1089,6 +1106,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DETECT_MD_THRESHOLD_GRID: *type = V4L2_CTRL_TYPE_U16; break; + case V4L2_CID_RDS_TX_ALT_FREQS: + *type = V4L2_CTRL_TYPE_U32; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; @@ -1209,6 +1229,8 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, return ptr1.p_u8[idx] == ptr2.p_u8[idx]; case V4L2_CTRL_TYPE_U16: return ptr1.p_u16[idx] == ptr2.p_u16[idx]; + case V4L2_CTRL_TYPE_U32: + return ptr1.p_u32[idx] == ptr2.p_u32[idx]; default: if (ctrl->is_int) return ptr1.p_s32[idx] == ptr2.p_s32[idx]; @@ -1242,6 +1264,9 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_U16: ptr.p_u16[idx] = ctrl->default_value; break; + case V4L2_CTRL_TYPE_U32: + ptr.p_u32[idx] = ctrl->default_value; + break; default: idx *= ctrl->elem_size; memset(ptr.p + idx, 0, ctrl->elem_size); @@ -1289,6 +1314,9 @@ static void std_log(const struct v4l2_ctrl *ctrl) case V4L2_CTRL_TYPE_U16: pr_cont("%u", (unsigned)*ptr.p_u16); break; + case V4L2_CTRL_TYPE_U32: + pr_cont("%u", (unsigned)*ptr.p_u32); + break; default: pr_cont("unknown type %d", ctrl->type); break; @@ -1346,6 +1374,8 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl); case V4L2_CTRL_TYPE_U16: return ROUND_TO_RANGE(ptr.p_u16[idx], u16, ctrl); + case V4L2_CTRL_TYPE_U32: + return ROUND_TO_RANGE(ptr.p_u32[idx], u32, ctrl); case V4L2_CTRL_TYPE_BOOLEAN: ptr.p_s32[idx] = !!ptr.p_s32[idx]; @@ -1578,6 +1608,7 @@ static int check_range(enum v4l2_ctrl_type type, /* fall through */ case V4L2_CTRL_TYPE_U8: case V4L2_CTRL_TYPE_U16: + case V4L2_CTRL_TYPE_U32: case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER64: if (step == 0 || min > max || def < min || def > max) @@ -1893,6 +1924,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, case V4L2_CTRL_TYPE_U16: elem_size = sizeof(u16); break; + case V4L2_CTRL_TYPE_U32: + elem_size = sizeof(u32); + break; default: if (type < V4L2_CTRL_COMPOUND_TYPES) elem_size = sizeof(s32); @@ -3248,6 +3282,7 @@ int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_U8: case V4L2_CTRL_TYPE_U16: + case V4L2_CTRL_TYPE_U32: if (ctrl->is_array) return -EINVAL; ret = check_range(ctrl->type, min, max, step, def); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index d6540d2..b7cd7a6 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -41,6 +41,7 @@ struct poll_table_struct; * @p_s64: Pointer to a 64-bit signed value. * @p_u8: Pointer to a 8-bit unsigned value. * @p_u16: Pointer to a 16-bit unsigned value. + * @p_u32: Pointer to a 32-bit unsigned value. * @p_char: Pointer to a string. * @p: Pointer to a compound value. */ @@ -49,6 +50,7 @@ union v4l2_ctrl_ptr { s64 *p_s64; u8 *p_u8; u16 *p_u16; + u32 *p_u32; char *p_char; void *p; }; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index db526d1..a13e6fe 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -757,6 +757,15 @@ enum v4l2_auto_focus_range { #define V4L2_CID_RDS_TX_PTY (V4L2_CID_FM_TX_CLASS_BASE + 3) #define V4L2_CID_RDS_TX_PS_NAME (V4L2_CID_FM_TX_CLASS_BASE + 5) #define V4L2_CID_RDS_TX_RADIO_TEXT (V4L2_CID_FM_TX_CLASS_BASE + 6) +#define V4L2_CID_RDS_TX_MONO_STEREO (V4L2_CID_FM_TX_CLASS_BASE + 7) +#define V4L2_CID_RDS_TX_ARTIFICIAL_HEAD (V4L2_CID_FM_TX_CLASS_BASE + 8) +#define V4L2_CID_RDS_TX_COMPRESSED (V4L2_CID_FM_TX_CLASS_BASE + 9) +#define V4L2_CID_RDS_TX_DYNAMIC_PTY (V4L2_CID_FM_TX_CLASS_BASE + 10) +#define V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT (V4L2_CID_FM_TX_CLASS_BASE + 11) +#define V4L2_CID_RDS_TX_TRAFFIC_PROGRAM (V4L2_CID_FM_TX_CLASS_BASE + 12) +#define V4L2_CID_RDS_TX_MUSIC_SPEECH (V4L2_CID_FM_TX_CLASS_BASE + 13) +#define V4L2_CID_RDS_TX_ALT_FREQS_ENABLE (V4L2_CID_FM_TX_CLASS_BASE + 14) +#define V4L2_CID_RDS_TX_ALT_FREQS (V4L2_CID_FM_TX_CLASS_BASE + 15) #define V4L2_CID_AUDIO_LIMITER_ENABLED (V4L2_CID_FM_TX_CLASS_BASE + 64) #define V4L2_CID_AUDIO_LIMITER_RELEASE_TIME (V4L2_CID_FM_TX_CLASS_BASE + 65) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 5fd4202..778a329 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1288,6 +1288,7 @@ struct v4l2_ext_control { char *string; __u8 *p_u8; __u16 *p_u16; + __u32 *p_u32; void *ptr; }; } __attribute__ ((packed)); @@ -1320,6 +1321,7 @@ enum v4l2_ctrl_type { V4L2_CTRL_COMPOUND_TYPES = 0x0100, V4L2_CTRL_TYPE_U8 = 0x0100, V4L2_CTRL_TYPE_U16 = 0x0101, + V4L2_CTRL_TYPE_U32 = 0x0102, }; /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ -- cgit v0.10.2 From 36cd37f2a56b8f3d71828f2eee950bfe147781e3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:38 -0300 Subject: [media] DocBook/media: document the new RDS TX controls Document the new RDS features that will be supported by the si4713 driver. Signed-off-by: Hans Verkuil Cc: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 74f5755..3ec85f6 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -4051,6 +4051,68 @@ to find receivers which can scroll strings sized as 32 x N or 64 x N characters. with steps of 32 or 64 characters. The result is it must always contain a string with size multiple of 32 or 64. + V4L2_CID_RDS_TX_MONO_STEREO  + boolean + + Sets the Mono/Stereo bit of the Decoder Identification code. If set, +then the audio was recorded as stereo. + + + V4L2_CID_RDS_TX_ARTIFICIAL_HEAD  + boolean + + Sets the +Artificial Head bit of the Decoder +Identification code. If set, then the audio was recorded using an artificial head. + + + V4L2_CID_RDS_TX_COMPRESSED  + boolean + + Sets the Compressed bit of the Decoder Identification code. If set, +then the audio is compressed. + + + V4L2_CID_RDS_TX_DYNAMIC_PTY  + boolean + + Sets the Dynamic PTY bit of the Decoder Identification code. If set, +then the PTY code is dynamically switched. + + + V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT  + boolean + + If set, then a traffic announcement is in progress. + + + V4L2_CID_RDS_TX_TRAFFIC_PROGRAM  + boolean + + If set, then the tuned programme carries traffic announcements. + + + V4L2_CID_RDS_TX_MUSIC_SPEECH  + boolean + + If set, then this channel broadcasts music. If cleared, then it +broadcasts speech. If the transmitter doesn't make this distinction, then it should be set. + + + V4L2_CID_RDS_TX_ALT_FREQS_ENABLE  + boolean + + If set, then transmit alternate frequencies. + + + V4L2_CID_RDS_TX_ALT_FREQS  + __u32 array + + The alternate frequencies in kHz units. The RDS standard allows +for up to 25 frequencies to be defined. Drivers may support fewer frequencies so check +the array size. + + V4L2_CID_AUDIO_LIMITER_ENABLED  boolean -- cgit v0.10.2 From 1abba28cf171f8216363652b263c259a1547d70d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:39 -0300 Subject: [media] si4713: add the missing RDS functionality Not all the RDS features of the si4713 were supported. Add the missing bits to fully support the hardware capabilities. Signed-off-by: Hans Verkuil Cc: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index dbe4726..b576555 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -957,6 +957,41 @@ static int si4713_choose_econtrol_action(struct si4713_device *sdev, u32 id, *bit = 5; *mask = 0x1F << 5; break; + case V4L2_CID_RDS_TX_DYNAMIC_PTY: + *property = SI4713_TX_RDS_PS_MISC; + *bit = 15; + *mask = 1 << 15; + break; + case V4L2_CID_RDS_TX_COMPRESSED: + *property = SI4713_TX_RDS_PS_MISC; + *bit = 14; + *mask = 1 << 14; + break; + case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD: + *property = SI4713_TX_RDS_PS_MISC; + *bit = 13; + *mask = 1 << 13; + break; + case V4L2_CID_RDS_TX_MONO_STEREO: + *property = SI4713_TX_RDS_PS_MISC; + *bit = 12; + *mask = 1 << 12; + break; + case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: + *property = SI4713_TX_RDS_PS_MISC; + *bit = 10; + *mask = 1 << 10; + break; + case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: + *property = SI4713_TX_RDS_PS_MISC; + *bit = 4; + *mask = 1 << 4; + break; + case V4L2_CID_RDS_TX_MUSIC_SPEECH: + *property = SI4713_TX_RDS_PS_MISC; + *bit = 3; + *mask = 1 << 3; + break; case V4L2_CID_AUDIO_LIMITER_ENABLED: *property = SI4713_TX_ACOMP_ENABLE; *bit = 1; @@ -1122,6 +1157,17 @@ static int si4713_s_ctrl(struct v4l2_ctrl *ctrl) } break; + case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: + case V4L2_CID_RDS_TX_ALT_FREQS: + if (sdev->rds_alt_freqs_enable->val) { + val = sdev->rds_alt_freqs->p_new.p_u32[0]; + val = val / 100 - 876 + 0xe101; + } else { + val = 0xe0e0; + } + ret = si4713_write_property(sdev, SI4713_TX_RDS_PS_AF, val); + break; + default: ret = si4713_choose_econtrol_action(sdev, ctrl->id, &bit, &mask, &property, &mul, &table, &size); @@ -1355,6 +1401,17 @@ static const struct v4l2_subdev_ops si4713_subdev_ops = { .tuner = &si4713_subdev_tuner_ops, }; +static const struct v4l2_ctrl_config si4713_alt_freqs_ctrl = { + .id = V4L2_CID_RDS_TX_ALT_FREQS, + .type = V4L2_CTRL_TYPE_U32, + .min = 87600, + .max = 107900, + .step = 100, + .def = 87600, + .dims = { 1 }, + .elem_size = sizeof(u32), +}; + /* * I2C driver interface */ @@ -1410,6 +1467,23 @@ static int si4713_probe(struct i2c_client *client, V4L2_CID_RDS_TX_PI, 0, 0xffff, 1, DEFAULT_RDS_PI); sdev->rds_pty = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, V4L2_CID_RDS_TX_PTY, 0, 31, 1, DEFAULT_RDS_PTY); + sdev->rds_compressed = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_COMPRESSED, 0, 1, 1, 0); + sdev->rds_art_head = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_ARTIFICIAL_HEAD, 0, 1, 1, 0); + sdev->rds_stereo = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_MONO_STEREO, 0, 1, 1, 1); + sdev->rds_tp = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_TRAFFIC_PROGRAM, 0, 1, 1, 0); + sdev->rds_ta = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT, 0, 1, 1, 0); + sdev->rds_ms = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_MUSIC_SPEECH, 0, 1, 1, 1); + sdev->rds_dyn_pty = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_DYNAMIC_PTY, 0, 1, 1, 0); + sdev->rds_alt_freqs_enable = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, + V4L2_CID_RDS_TX_ALT_FREQS_ENABLE, 0, 1, 1, 0); + sdev->rds_alt_freqs = v4l2_ctrl_new_custom(hdl, &si4713_alt_freqs_ctrl, NULL); sdev->rds_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops, V4L2_CID_RDS_TX_DEVIATION, 0, MAX_RDS_DEVIATION, 10, DEFAULT_RDS_DEVIATION); @@ -1476,7 +1550,7 @@ static int si4713_probe(struct i2c_client *client, rval = hdl->error; goto free_ctrls; } - v4l2_ctrl_cluster(20, &sdev->mute); + v4l2_ctrl_cluster(29, &sdev->mute); sdev->sd.ctrl_handler = hdl; if (client->irq) { diff --git a/drivers/media/radio/si4713/si4713.h b/drivers/media/radio/si4713/si4713.h index 4837cf6..ed700e3 100644 --- a/drivers/media/radio/si4713/si4713.h +++ b/drivers/media/radio/si4713/si4713.h @@ -211,6 +211,15 @@ struct si4713_device { struct v4l2_ctrl *rds_pi; struct v4l2_ctrl *rds_deviation; struct v4l2_ctrl *rds_pty; + struct v4l2_ctrl *rds_compressed; + struct v4l2_ctrl *rds_art_head; + struct v4l2_ctrl *rds_stereo; + struct v4l2_ctrl *rds_ta; + struct v4l2_ctrl *rds_tp; + struct v4l2_ctrl *rds_ms; + struct v4l2_ctrl *rds_dyn_pty; + struct v4l2_ctrl *rds_alt_freqs_enable; + struct v4l2_ctrl *rds_alt_freqs; struct v4l2_ctrl *compression_enabled; struct v4l2_ctrl *compression_threshold; struct v4l2_ctrl *compression_gain; -- cgit v0.10.2 From 9570a14847a9d334a0baf5a5921da2dbc6b9f6d2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:40 -0300 Subject: [media] v4l2-ctrls: add RX RDS controls The radio-miropcm20 driver has firmware that decodes the RDS signals. So in that case the RDS data becomes available in the form of controls. Add support for these controls to the control framework, allowing the miro driver to use them. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 4863cf0..2d8ced8 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -881,7 +881,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_FM_RX_CLASS: return "FM Radio Receiver Controls"; case V4L2_CID_TUNE_DEEMPHASIS: return "De-Emphasis"; case V4L2_CID_RDS_RECEPTION: return "RDS Reception"; - case V4L2_CID_RF_TUNER_CLASS: return "RF Tuner Controls"; case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: return "LNA Gain, Auto"; case V4L2_CID_RF_TUNER_LNA_GAIN: return "LNA Gain"; @@ -892,6 +891,12 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: return "Bandwidth, Auto"; case V4L2_CID_RF_TUNER_BANDWIDTH: return "Bandwidth"; case V4L2_CID_RF_TUNER_PLL_LOCK: return "PLL Lock"; + case V4L2_CID_RDS_RX_PTY: return "RDS Program Type"; + case V4L2_CID_RDS_RX_PS_NAME: return "RDS PS Name"; + case V4L2_CID_RDS_RX_RADIO_TEXT: return "RDS Radio Text"; + case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: return "RDS Traffic Announcement"; + case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: return "RDS Traffic Program"; + case V4L2_CID_RDS_RX_MUSIC_SPEECH: return "RDS Music"; /* Detection controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ @@ -900,7 +905,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: return "MD Global Threshold"; case V4L2_CID_DETECT_MD_THRESHOLD_GRID: return "MD Threshold Grid"; case V4L2_CID_DETECT_MD_REGION_GRID: return "MD Region Grid"; - default: return NULL; } @@ -963,6 +967,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: case V4L2_CID_RDS_TX_MUSIC_SPEECH: case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: + case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: + case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: + case V4L2_CID_RDS_RX_MUSIC_SPEECH: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; @@ -1035,6 +1042,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_RDS_TX_PS_NAME: case V4L2_CID_RDS_TX_RADIO_TEXT: + case V4L2_CID_RDS_RX_PS_NAME: + case V4L2_CID_RDS_RX_RADIO_TEXT: *type = V4L2_CTRL_TYPE_STRING; break; case V4L2_CID_ISO_SENSITIVITY: @@ -1166,6 +1175,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DV_TX_RXSENSE: case V4L2_CID_DV_TX_EDID_PRESENT: case V4L2_CID_DV_RX_POWER_PRESENT: + case V4L2_CID_RDS_RX_PTY: + case V4L2_CID_RDS_RX_PS_NAME: + case V4L2_CID_RDS_RX_RADIO_TEXT: + case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: + case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: + case V4L2_CID_RDS_RX_MUSIC_SPEECH: *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; case V4L2_CID_RF_TUNER_PLL_LOCK: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index a13e6fe..e946e43 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -910,6 +910,12 @@ enum v4l2_deemphasis { }; #define V4L2_CID_RDS_RECEPTION (V4L2_CID_FM_RX_CLASS_BASE + 2) +#define V4L2_CID_RDS_RX_PTY (V4L2_CID_FM_RX_CLASS_BASE + 3) +#define V4L2_CID_RDS_RX_PS_NAME (V4L2_CID_FM_RX_CLASS_BASE + 4) +#define V4L2_CID_RDS_RX_RADIO_TEXT (V4L2_CID_FM_RX_CLASS_BASE + 5) +#define V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT (V4L2_CID_FM_RX_CLASS_BASE + 6) +#define V4L2_CID_RDS_RX_TRAFFIC_PROGRAM (V4L2_CID_FM_RX_CLASS_BASE + 7) +#define V4L2_CID_RDS_RX_MUSIC_SPEECH (V4L2_CID_FM_RX_CLASS_BASE + 8) #define V4L2_CID_RF_TUNER_CLASS_BASE (V4L2_CTRL_CLASS_RF_TUNER | 0x900) #define V4L2_CID_RF_TUNER_CLASS (V4L2_CTRL_CLASS_RF_TUNER | 1) -- cgit v0.10.2 From 1a586e1a5b0d27d8ec3cba67a767115ea1cb596b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:41 -0300 Subject: [media] DocBook/media: document the new RDS RX controls Document the new RDS receiver controls. This will be used by the radio-miropcm20 driver. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 3ec85f6..9f5ffd8 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -5089,6 +5089,57 @@ description of this control class. Enables/disables RDS reception by the radio tuner + + V4L2_CID_RDS_RX_PTY  + integer + + Gets RDS Programme Type field. +This encodes up to 31 pre-defined programme types. + + + V4L2_CID_RDS_RX_PS_NAME  + string + + Gets the Programme Service name (PS_NAME). +It is intended for static display on a receiver. It is the primary aid to listeners in programme service +identification and selection. In Annex E of , the RDS specification, +there is a full description of the correct character encoding for Programme Service name strings. +Also from RDS specification, PS is usually a single eight character text. However, it is also possible +to find receivers which can scroll strings sized as 8 x N characters. So, this control must be configured +with steps of 8 characters. The result is it must always contain a string with size multiple of 8. + + + V4L2_CID_RDS_RX_RADIO_TEXT  + string + + Gets the Radio Text info. It is a textual description of +what is being broadcasted. RDS Radio Text can be applied when broadcaster wishes to transmit longer PS names, +programme-related information or any other text. In these cases, RadioText can be used in addition to +V4L2_CID_RDS_RX_PS_NAME. The encoding for Radio Text strings is also fully described +in Annex E of . The length of Radio Text strings depends on which RDS Block is being +used to transmit it, either 32 (2A block) or 64 (2B block). However, it is also possible +to find receivers which can scroll strings sized as 32 x N or 64 x N characters. So, this control must be configured +with steps of 32 or 64 characters. The result is it must always contain a string with size multiple of 32 or 64. + + + V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT  + boolean + + If set, then a traffic announcement is in progress. + + + V4L2_CID_RDS_RX_TRAFFIC_PROGRAM  + boolean + + If set, then the tuned programme carries traffic announcements. + + + V4L2_CID_RDS_RX_MUSIC_SPEECH  + boolean + + If set, then this channel broadcasts music. If cleared, then it +broadcasts speech. If the transmitter doesn't make this distinction, then it will be set. + V4L2_CID_TUNE_DEEMPHASIS  enum v4l2_deemphasis -- cgit v0.10.2 From fdcfd4e704cd6de6a3d96b9d680c67cef9b339f4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:43 -0300 Subject: [media] radio-miropcm20: add RDS support Once upon a time the radio-miropcm20 driver had RDS support. However, after some internal kernel changes that support was removed. Now that we have a nice RDS API I have been working on adding back this support. It has been tested with the si4713 RDS transmitter and it is working quite nicely. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 3d12edf..72df00e 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -1,20 +1,35 @@ -/* Miro PCM20 radio driver for Linux radio support +/* + * Miro PCM20 radio driver for Linux radio support * (c) 1998 Ruurd Reitsma * Thanks to Norberto Pellici for the ACI device interface specification * The API part is based on the radiotrack driver by M. Kirkwood * This driver relies on the aci mixer provided by the snd-miro * ALSA driver. * Look there for further info... - */ - -/* What ever you think about the ACI, version 0x07 is not very well! - * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono - * conditions... Robert + * + * From the original miro RDS sources: + * + * (c) 2001 Robert Siemer + * + * Many thanks to Fred Seidel , the + * designer of the RDS decoder hardware. With his help + * I was able to code this driver. + * Thanks also to Norberto Pellicci, Dominic Mounteney + * and www.teleauskunft.de + * for good hints on finding Fred. It was somewhat hard + * to locate him here in Germany... [: + * + * This code has been reintroduced and converted to use + * the new V4L2 RDS API by: + * + * Hans Verkuil */ #include #include +#include #include +#include #include #include #include @@ -22,6 +37,22 @@ #include #include +#define RDS_DATASHIFT 2 /* Bit 2 */ +#define RDS_DATAMASK (1 << RDS_DATASHIFT) +#define RDS_BUSYMASK 0x10 /* Bit 4 */ +#define RDS_CLOCKMASK 0x08 /* Bit 3 */ +#define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1) + +#define RDS_STATUS 0x01 +#define RDS_STATIONNAME 0x02 +#define RDS_TEXT 0x03 +#define RDS_ALTFREQ 0x04 +#define RDS_TIMEDATE 0x05 +#define RDS_PI_CODE 0x06 +#define RDS_PTYTATP 0x07 +#define RDS_RESET 0x08 +#define RDS_RXVALUE 0x09 + static int radio_nr = -1; module_param(radio_nr, int, 0); MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); @@ -30,6 +61,14 @@ struct pcm20 { struct v4l2_device v4l2_dev; struct video_device vdev; struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *rds_pty; + struct v4l2_ctrl *rds_ps_name; + struct v4l2_ctrl *rds_radio_test; + struct v4l2_ctrl *rds_ta; + struct v4l2_ctrl *rds_tp; + struct v4l2_ctrl *rds_ms; + /* thread for periodic RDS status checking */ + struct task_struct *kthread; unsigned long freq; u32 audmode; struct snd_miro_aci *aci; @@ -41,6 +80,103 @@ static struct pcm20 pcm20_card = { .audmode = V4L2_TUNER_MODE_STEREO, }; + +static int rds_waitread(struct snd_miro_aci *aci) +{ + u8 byte; + int i = 2000; + + do { + byte = inb(aci->aci_port + ACI_REG_RDS); + i--; + } while ((byte & RDS_BUSYMASK) && i); + + /* + * It's magic, but without this the data that you read later on + * is unreliable and full of bit errors. With this 1 usec delay + * everything is fine. + */ + udelay(1); + return i ? byte : -1; +} + +static int rds_rawwrite(struct snd_miro_aci *aci, u8 byte) +{ + if (rds_waitread(aci) >= 0) { + outb(byte, aci->aci_port + ACI_REG_RDS); + return 0; + } + return -1; +} + +static int rds_write(struct snd_miro_aci *aci, u8 byte) +{ + u8 sendbuffer[8]; + int i; + + for (i = 7; i >= 0; i--) + sendbuffer[7 - i] = (byte & (1 << i)) ? RDS_DATAMASK : 0; + sendbuffer[0] |= RDS_CLOCKMASK; + + for (i = 0; i < 8; i++) + rds_rawwrite(aci, sendbuffer[i]); + return 0; +} + +static int rds_readcycle_nowait(struct snd_miro_aci *aci) +{ + outb(0, aci->aci_port + ACI_REG_RDS); + return rds_waitread(aci); +} + +static int rds_readcycle(struct snd_miro_aci *aci) +{ + if (rds_rawwrite(aci, 0) < 0) + return -1; + return rds_waitread(aci); +} + +static int rds_ack(struct snd_miro_aci *aci) +{ + int i = rds_readcycle(aci); + + if (i < 0) + return -1; + if (i & RDS_DATAMASK) + return 0; /* ACK */ + return 1; /* NACK */ +} + +static int rds_cmd(struct snd_miro_aci *aci, u8 cmd, u8 databuffer[], u8 datasize) +{ + int i, j; + + rds_write(aci, cmd); + + /* RDS_RESET doesn't need further processing */ + if (cmd == RDS_RESET) + return 0; + if (rds_ack(aci)) + return -EIO; + if (datasize == 0) + return 0; + + /* to be able to use rds_readcycle_nowait() + I have to waitread() here */ + if (rds_waitread(aci) < 0) + return -1; + + memset(databuffer, 0, datasize); + + for (i = 0; i < 8 * datasize; i++) { + j = rds_readcycle_nowait(aci); + if (j < 0) + return -EIO; + databuffer[i / 8] |= RDS_DATA(j) << (7 - (i % 8)); + } + return 0; +} + static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) { unsigned char freql; @@ -54,17 +190,10 @@ static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) freql = freq & 0xff; freqh = freq >> 8; + rds_cmd(aci, RDS_RESET, 0, 0); return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh); } -static const struct v4l2_file_operations pcm20_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .poll = v4l2_ctrl_poll, - .release = v4l2_fh_release, - .unlocked_ioctl = video_ioctl2, -}; - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { @@ -73,16 +202,31 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(v->driver, "Miro PCM20", sizeof(v->driver)); strlcpy(v->card, "Miro PCM20", sizeof(v->card)); snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", dev->v4l2_dev.name); - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } +static bool sanitize(char *p, int size) +{ + int i; + bool ret = true; + + for (i = 0; i < size; i++) { + if (p[i] < 32 || p[i] >= 128) { + p[i] = ' '; + ret = false; + } + } + return ret; +} + static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { struct pcm20 *dev = video_drvdata(file); int res; + u8 buf; if (v->index) return -EINVAL; @@ -97,8 +241,12 @@ static int vidioc_g_tuner(struct file *file, void *priv, res = snd_aci_cmd(dev->aci, ACI_READ_TUNERSTEREO, -1, -1); v->rxsubchans = (res & 0x40) ? V4L2_TUNER_SUB_MONO : V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_CONTROLS; v->audmode = dev->audmode; + res = rds_cmd(dev->aci, RDS_RXVALUE, &buf, 1); + if (res >= 0 && buf) + v->rxsubchans |= V4L2_TUNER_SUB_RDS; return 0; } @@ -157,6 +305,115 @@ static int pcm20_s_ctrl(struct v4l2_ctrl *ctrl) return -EINVAL; } +static int pcm20_thread(void *data) +{ + struct pcm20 *dev = data; + const unsigned no_rds_start_counter = 5; + const unsigned sleep_msecs = 2000; + unsigned no_rds_counter = no_rds_start_counter; + + for (;;) { + char text_buffer[66]; + u8 buf; + int res; + + msleep_interruptible(sleep_msecs); + + if (kthread_should_stop()) + break; + + res = rds_cmd(dev->aci, RDS_RXVALUE, &buf, 1); + if (res) + continue; + if (buf == 0) { + if (no_rds_counter == 0) + continue; + no_rds_counter--; + if (no_rds_counter) + continue; + + /* + * No RDS seen for no_rds_start_counter * sleep_msecs + * milliseconds, clear all RDS controls to their + * default values. + */ + v4l2_ctrl_s_ctrl_string(dev->rds_ps_name, ""); + v4l2_ctrl_s_ctrl(dev->rds_ms, 1); + v4l2_ctrl_s_ctrl(dev->rds_ta, 0); + v4l2_ctrl_s_ctrl(dev->rds_tp, 0); + v4l2_ctrl_s_ctrl(dev->rds_pty, 0); + v4l2_ctrl_s_ctrl_string(dev->rds_radio_test, ""); + continue; + } + no_rds_counter = no_rds_start_counter; + + res = rds_cmd(dev->aci, RDS_STATUS, &buf, 1); + if (res) + continue; + if ((buf >> 3) & 1) { + res = rds_cmd(dev->aci, RDS_STATIONNAME, text_buffer, 8); + text_buffer[8] = 0; + if (!res && sanitize(text_buffer, 8)) + v4l2_ctrl_s_ctrl_string(dev->rds_ps_name, text_buffer); + } + if ((buf >> 6) & 1) { + u8 pty; + + res = rds_cmd(dev->aci, RDS_PTYTATP, &pty, 1); + if (!res) { + v4l2_ctrl_s_ctrl(dev->rds_ms, !!(pty & 0x01)); + v4l2_ctrl_s_ctrl(dev->rds_ta, !!(pty & 0x02)); + v4l2_ctrl_s_ctrl(dev->rds_tp, !!(pty & 0x80)); + v4l2_ctrl_s_ctrl(dev->rds_pty, (pty >> 2) & 0x1f); + } + } + if ((buf >> 4) & 1) { + res = rds_cmd(dev->aci, RDS_TEXT, text_buffer, 65); + text_buffer[65] = 0; + if (!res && sanitize(text_buffer + 1, 64)) + v4l2_ctrl_s_ctrl_string(dev->rds_radio_test, text_buffer + 1); + } + } + return 0; +} + +static int pcm20_open(struct file *file) +{ + struct pcm20 *dev = video_drvdata(file); + int res = v4l2_fh_open(file); + + if (!res && v4l2_fh_is_singular_file(file) && + IS_ERR_OR_NULL(dev->kthread)) { + dev->kthread = kthread_run(pcm20_thread, dev, "%s", + dev->v4l2_dev.name); + if (IS_ERR(dev->kthread)) { + v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); + v4l2_fh_release(file); + return PTR_ERR(dev->kthread); + } + } + return res; +} + +static int pcm20_release(struct file *file) +{ + struct pcm20 *dev = video_drvdata(file); + + if (v4l2_fh_is_singular_file(file) && !IS_ERR_OR_NULL(dev->kthread)) { + kthread_stop(dev->kthread); + dev->kthread = NULL; + } + return v4l2_fh_release(file); +} + +static const struct v4l2_file_operations pcm20_fops = { + .owner = THIS_MODULE, + .open = pcm20_open, + .poll = v4l2_ctrl_poll, + .release = pcm20_release, + .unlocked_ioctl = video_ioctl2, +}; + static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, @@ -195,9 +452,21 @@ static int __init pcm20_init(void) } hdl = &dev->ctrl_handler; - v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_handler_init(hdl, 7); v4l2_ctrl_new_std(hdl, &pcm20_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + dev->rds_pty = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_RDS_RX_PTY, 0, 0x1f, 1, 0); + dev->rds_ps_name = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_RDS_RX_PS_NAME, 0, 8, 8, 0); + dev->rds_radio_test = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_RDS_RX_RADIO_TEXT, 0, 64, 64, 0); + dev->rds_ta = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT, 0, 1, 1, 0); + dev->rds_tp = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_RDS_RX_TRAFFIC_PROGRAM, 0, 1, 1, 0); + dev->rds_ms = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_RDS_RX_MUSIC_SPEECH, 0, 1, 1, 1); v4l2_dev->ctrl_handler = hdl; if (hdl->error) { res = hdl->error; -- cgit v0.10.2 From f5b44da1ac4146e06147a5df3058f4c265c932ec Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 25 Jul 2014 06:17:59 -0300 Subject: [media] Kconfig: fix tuners build warnings [next:master 7472/8702] warning: (USB_MSI2500) selects MEDIA_TUNER_MSI001 which has unmet direct dependencies ((MEDIA_ANALOG_TV_SUPPORT || ..) && ..) [next:master 7698/8702] warning: (MEDIA_TUNER && ..) selects MEDIA_TUNER_XC5000 which has unmet direct dependencies ((MEDIA_ANALOG_TV_SUPPORT || ..) && ..) Reported-by: kbuild test robot Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 51edd10..d79fd1c 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -16,7 +16,7 @@ config MEDIA_TUNER menu "Customize TV tuners" visible if !MEDIA_SUBDRV_AUTOSELECT - depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT + depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT config MEDIA_TUNER_SIMPLE tristate "Simple tuner support" @@ -74,6 +74,7 @@ config MEDIA_TUNER_TEA5767 config MEDIA_TUNER_MSI001 tristate "Mirics MSi001" depends on MEDIA_SUPPORT && SPI && VIDEO_V4L2 + default m if !MEDIA_SUBDRV_AUTOSELECT help Mirics MSi001 silicon tuner driver. -- cgit v0.10.2 From 69cc4bfab9e65168f50a1f1d163c0d70a394bae3 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 25 Jul 2014 06:31:32 -0300 Subject: [media] Kconfig: rtl2832_sdr must depend on USB Fixes error: [next:master 7435/8702] ERROR: "usb_alloc_urb [drivers/media/dvb-frontends/rtl2832_sdr.ko] undefined! rtl2832_sdr driver implements own USB streaming for SDR data. Logically that functionality belongs to USB interface driver, but currently it is implemented here. Reported-by: kbuild test robot Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 78a95a6..5b8b04c 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -448,7 +448,7 @@ config DVB_RTL2832 config DVB_RTL2832_SDR tristate "Realtek RTL2832 SDR" - depends on DVB_CORE && I2C && I2C_MUX && VIDEO_V4L2 && MEDIA_SDR_SUPPORT + depends on DVB_CORE && I2C && I2C_MUX && VIDEO_V4L2 && MEDIA_SDR_SUPPORT && USB select DVB_RTL2832 select VIDEOBUF2_VMALLOC default m if !MEDIA_SUBDRV_AUTOSELECT -- cgit v0.10.2 From 9c46c7facda384f8fb6078e70b4e84ff84ed949d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 25 Jul 2014 19:33:40 -0300 Subject: [media] radio-miropcm20: fix a compilation warning drivers/media/radio/radio-miropcm20.c: In function 'sanitize': drivers/media/radio/radio-miropcm20.c:216:3: warning: comparison is always false due to limited range of data type [-Wtype-limits] if (p[i] < 32 || p[i] >= 128) { ^ As p is declared as a char array, it is signed. So, it can never be bigger than 127. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 72df00e..ac9915d 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -213,7 +213,7 @@ static bool sanitize(char *p, int size) bool ret = true; for (i = 0; i < size; i++) { - if (p[i] < 32 || p[i] >= 128) { + if (p[i] < 32) { p[i] = ' '; ret = false; } -- cgit v0.10.2 From 4924a311a62f7326dc1220f3abd72b7785d72e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 3 Apr 2014 20:34:28 -0300 Subject: [media] rc-core: rename ir-raw.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Better to be coherent and prefix this file with rc-, in order to help to identify to what subsystem it belongs. This is in preparaton for a latter patch that will transform the raw handling into a separate module. Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index d07576f..096eb06 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -1,4 +1,4 @@ -rc-core-objs := rc-main.o ir-raw.o +rc-core-objs := rc-main.o rc-ir-raw.o obj-y += keymaps/ diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c deleted file mode 100644 index c72f57c..0000000 --- a/drivers/media/rc/ir-raw.c +++ /dev/null @@ -1,369 +0,0 @@ -/* ir-raw.c - handle IR pulse/space events - * - * Copyright (C) 2010 by Mauro Carvalho Chehab - * - * 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 version 2 of the License. - * - * 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 -#include -#include -#include -#include -#include -#include "rc-core-priv.h" - -/* Define the max number of pulse/space transitions to buffer */ -#define MAX_IR_EVENT_SIZE 512 - -/* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */ -static LIST_HEAD(ir_raw_client_list); - -/* Used to handle IR raw handler extensions */ -static DEFINE_MUTEX(ir_raw_handler_lock); -static LIST_HEAD(ir_raw_handler_list); -static u64 available_protocols; - -static int ir_raw_event_thread(void *data) -{ - struct ir_raw_event ev; - struct ir_raw_handler *handler; - struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data; - int retval; - - while (!kthread_should_stop()) { - - spin_lock_irq(&raw->lock); - retval = kfifo_len(&raw->kfifo); - - if (retval < sizeof(ev)) { - set_current_state(TASK_INTERRUPTIBLE); - - if (kthread_should_stop()) - set_current_state(TASK_RUNNING); - - spin_unlock_irq(&raw->lock); - schedule(); - continue; - } - - retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev)); - spin_unlock_irq(&raw->lock); - - mutex_lock(&ir_raw_handler_lock); - list_for_each_entry(handler, &ir_raw_handler_list, list) - handler->decode(raw->dev, ev); - raw->prev_ev = ev; - mutex_unlock(&ir_raw_handler_lock); - } - - return 0; -} - -/** - * ir_raw_event_store() - pass a pulse/space duration to the raw ir decoders - * @dev: the struct rc_dev device descriptor - * @ev: the struct ir_raw_event descriptor of the pulse/space - * - * This routine (which may be called from an interrupt context) stores a - * pulse/space duration for the raw ir decoding state machines. Pulses are - * signalled as positive values and spaces as negative values. A zero value - * will reset the decoding state machines. - */ -int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev) -{ - if (!dev->raw) - return -EINVAL; - - IR_dprintk(2, "sample: (%05dus %s)\n", - TO_US(ev->duration), TO_STR(ev->pulse)); - - if (kfifo_in(&dev->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev)) - return -ENOMEM; - - return 0; -} -EXPORT_SYMBOL_GPL(ir_raw_event_store); - -/** - * ir_raw_event_store_edge() - notify raw ir decoders of the start of a pulse/space - * @dev: the struct rc_dev device descriptor - * @type: the type of the event that has occurred - * - * This routine (which may be called from an interrupt context) is used to - * store the beginning of an ir pulse or space (or the start/end of ir - * reception) for the raw ir decoding state machines. This is used by - * hardware which does not provide durations directly but only interrupts - * (or similar events) on state change. - */ -int ir_raw_event_store_edge(struct rc_dev *dev, enum raw_event_type type) -{ - ktime_t now; - s64 delta; /* ns */ - DEFINE_IR_RAW_EVENT(ev); - int rc = 0; - int delay; - - if (!dev->raw) - return -EINVAL; - - now = ktime_get(); - delta = ktime_to_ns(ktime_sub(now, dev->raw->last_event)); - delay = MS_TO_NS(dev->input_dev->rep[REP_DELAY]); - - /* Check for a long duration since last event or if we're - * being called for the first time, note that delta can't - * possibly be negative. - */ - if (delta > delay || !dev->raw->last_type) - type |= IR_START_EVENT; - else - ev.duration = delta; - - if (type & IR_START_EVENT) - ir_raw_event_reset(dev); - else if (dev->raw->last_type & IR_SPACE) { - ev.pulse = false; - rc = ir_raw_event_store(dev, &ev); - } else if (dev->raw->last_type & IR_PULSE) { - ev.pulse = true; - rc = ir_raw_event_store(dev, &ev); - } else - return 0; - - dev->raw->last_event = now; - dev->raw->last_type = type; - return rc; -} -EXPORT_SYMBOL_GPL(ir_raw_event_store_edge); - -/** - * ir_raw_event_store_with_filter() - pass next pulse/space to decoders with some processing - * @dev: the struct rc_dev device descriptor - * @type: the type of the event that has occurred - * - * This routine (which may be called from an interrupt context) works - * in similar manner to ir_raw_event_store_edge. - * This routine is intended for devices with limited internal buffer - * It automerges samples of same type, and handles timeouts. Returns non-zero - * if the event was added, and zero if the event was ignored due to idle - * processing. - */ -int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev) -{ - if (!dev->raw) - return -EINVAL; - - /* Ignore spaces in idle mode */ - if (dev->idle && !ev->pulse) - return 0; - else if (dev->idle) - ir_raw_event_set_idle(dev, false); - - if (!dev->raw->this_ev.duration) - dev->raw->this_ev = *ev; - else if (ev->pulse == dev->raw->this_ev.pulse) - dev->raw->this_ev.duration += ev->duration; - else { - ir_raw_event_store(dev, &dev->raw->this_ev); - dev->raw->this_ev = *ev; - } - - /* Enter idle mode if nessesary */ - if (!ev->pulse && dev->timeout && - dev->raw->this_ev.duration >= dev->timeout) - ir_raw_event_set_idle(dev, true); - - return 1; -} -EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter); - -/** - * ir_raw_event_set_idle() - provide hint to rc-core when the device is idle or not - * @dev: the struct rc_dev device descriptor - * @idle: whether the device is idle or not - */ -void ir_raw_event_set_idle(struct rc_dev *dev, bool idle) -{ - if (!dev->raw) - return; - - IR_dprintk(2, "%s idle mode\n", idle ? "enter" : "leave"); - - if (idle) { - dev->raw->this_ev.timeout = true; - ir_raw_event_store(dev, &dev->raw->this_ev); - init_ir_raw_event(&dev->raw->this_ev); - } - - if (dev->s_idle) - dev->s_idle(dev, idle); - - dev->idle = idle; -} -EXPORT_SYMBOL_GPL(ir_raw_event_set_idle); - -/** - * ir_raw_event_handle() - schedules the decoding of stored ir data - * @dev: the struct rc_dev device descriptor - * - * This routine will tell rc-core to start decoding stored ir data. - */ -void ir_raw_event_handle(struct rc_dev *dev) -{ - unsigned long flags; - - if (!dev->raw) - return; - - spin_lock_irqsave(&dev->raw->lock, flags); - wake_up_process(dev->raw->thread); - spin_unlock_irqrestore(&dev->raw->lock, flags); -} -EXPORT_SYMBOL_GPL(ir_raw_event_handle); - -/* used internally by the sysfs interface */ -u64 -ir_raw_get_allowed_protocols(void) -{ - u64 protocols; - mutex_lock(&ir_raw_handler_lock); - protocols = available_protocols; - mutex_unlock(&ir_raw_handler_lock); - return protocols; -} - -static int change_protocol(struct rc_dev *dev, u64 *rc_type) -{ - /* the caller will update dev->enabled_protocols */ - return 0; -} - -/* - * Used to (un)register raw event clients - */ -int ir_raw_event_register(struct rc_dev *dev) -{ - int rc; - struct ir_raw_handler *handler; - - if (!dev) - return -EINVAL; - - dev->raw = kzalloc(sizeof(*dev->raw), GFP_KERNEL); - if (!dev->raw) - return -ENOMEM; - - dev->raw->dev = dev; - dev->enabled_protocols = ~0; - dev->change_protocol = change_protocol; - rc = kfifo_alloc(&dev->raw->kfifo, - sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE, - GFP_KERNEL); - if (rc < 0) - goto out; - - spin_lock_init(&dev->raw->lock); - dev->raw->thread = kthread_run(ir_raw_event_thread, dev->raw, - "rc%ld", dev->devno); - - if (IS_ERR(dev->raw->thread)) { - rc = PTR_ERR(dev->raw->thread); - goto out; - } - - mutex_lock(&ir_raw_handler_lock); - list_add_tail(&dev->raw->list, &ir_raw_client_list); - list_for_each_entry(handler, &ir_raw_handler_list, list) - if (handler->raw_register) - handler->raw_register(dev); - mutex_unlock(&ir_raw_handler_lock); - - return 0; - -out: - kfree(dev->raw); - dev->raw = NULL; - return rc; -} - -void ir_raw_event_unregister(struct rc_dev *dev) -{ - struct ir_raw_handler *handler; - - if (!dev || !dev->raw) - return; - - kthread_stop(dev->raw->thread); - - mutex_lock(&ir_raw_handler_lock); - list_del(&dev->raw->list); - list_for_each_entry(handler, &ir_raw_handler_list, list) - if (handler->raw_unregister) - handler->raw_unregister(dev); - mutex_unlock(&ir_raw_handler_lock); - - kfifo_free(&dev->raw->kfifo); - kfree(dev->raw); - dev->raw = NULL; -} - -/* - * Extension interface - used to register the IR decoders - */ - -int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler) -{ - struct ir_raw_event_ctrl *raw; - - mutex_lock(&ir_raw_handler_lock); - list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list); - if (ir_raw_handler->raw_register) - list_for_each_entry(raw, &ir_raw_client_list, list) - ir_raw_handler->raw_register(raw->dev); - available_protocols |= ir_raw_handler->protocols; - mutex_unlock(&ir_raw_handler_lock); - - return 0; -} -EXPORT_SYMBOL(ir_raw_handler_register); - -void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) -{ - struct ir_raw_event_ctrl *raw; - - mutex_lock(&ir_raw_handler_lock); - list_del(&ir_raw_handler->list); - if (ir_raw_handler->raw_unregister) - list_for_each_entry(raw, &ir_raw_client_list, list) - ir_raw_handler->raw_unregister(raw->dev); - available_protocols &= ~ir_raw_handler->protocols; - mutex_unlock(&ir_raw_handler_lock); -} -EXPORT_SYMBOL(ir_raw_handler_unregister); - -void ir_raw_init(void) -{ - /* Load the decoder modules */ - - load_nec_decode(); - load_rc5_decode(); - load_rc6_decode(); - load_jvc_decode(); - load_sony_decode(); - load_sanyo_decode(); - load_sharp_decode(); - load_mce_kbd_decode(); - load_lirc_codec(); - - /* If needed, we may later add some init code. In this case, - it is needed to change the CONFIG_MODULE test at rc-core.h - */ -} diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c new file mode 100644 index 0000000..e772c4c --- /dev/null +++ b/drivers/media/rc/rc-ir-raw.c @@ -0,0 +1,369 @@ +/* rc-ir-raw.c - handle IR pulse/space events + * + * Copyright (C) 2010 by Mauro Carvalho Chehab + * + * 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 version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include "rc-core-priv.h" + +/* Define the max number of pulse/space transitions to buffer */ +#define MAX_IR_EVENT_SIZE 512 + +/* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */ +static LIST_HEAD(ir_raw_client_list); + +/* Used to handle IR raw handler extensions */ +static DEFINE_MUTEX(ir_raw_handler_lock); +static LIST_HEAD(ir_raw_handler_list); +static u64 available_protocols; + +static int ir_raw_event_thread(void *data) +{ + struct ir_raw_event ev; + struct ir_raw_handler *handler; + struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data; + int retval; + + while (!kthread_should_stop()) { + + spin_lock_irq(&raw->lock); + retval = kfifo_len(&raw->kfifo); + + if (retval < sizeof(ev)) { + set_current_state(TASK_INTERRUPTIBLE); + + if (kthread_should_stop()) + set_current_state(TASK_RUNNING); + + spin_unlock_irq(&raw->lock); + schedule(); + continue; + } + + retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev)); + spin_unlock_irq(&raw->lock); + + mutex_lock(&ir_raw_handler_lock); + list_for_each_entry(handler, &ir_raw_handler_list, list) + handler->decode(raw->dev, ev); + raw->prev_ev = ev; + mutex_unlock(&ir_raw_handler_lock); + } + + return 0; +} + +/** + * ir_raw_event_store() - pass a pulse/space duration to the raw ir decoders + * @dev: the struct rc_dev device descriptor + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This routine (which may be called from an interrupt context) stores a + * pulse/space duration for the raw ir decoding state machines. Pulses are + * signalled as positive values and spaces as negative values. A zero value + * will reset the decoding state machines. + */ +int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev) +{ + if (!dev->raw) + return -EINVAL; + + IR_dprintk(2, "sample: (%05dus %s)\n", + TO_US(ev->duration), TO_STR(ev->pulse)); + + if (kfifo_in(&dev->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev)) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_GPL(ir_raw_event_store); + +/** + * ir_raw_event_store_edge() - notify raw ir decoders of the start of a pulse/space + * @dev: the struct rc_dev device descriptor + * @type: the type of the event that has occurred + * + * This routine (which may be called from an interrupt context) is used to + * store the beginning of an ir pulse or space (or the start/end of ir + * reception) for the raw ir decoding state machines. This is used by + * hardware which does not provide durations directly but only interrupts + * (or similar events) on state change. + */ +int ir_raw_event_store_edge(struct rc_dev *dev, enum raw_event_type type) +{ + ktime_t now; + s64 delta; /* ns */ + DEFINE_IR_RAW_EVENT(ev); + int rc = 0; + int delay; + + if (!dev->raw) + return -EINVAL; + + now = ktime_get(); + delta = ktime_to_ns(ktime_sub(now, dev->raw->last_event)); + delay = MS_TO_NS(dev->input_dev->rep[REP_DELAY]); + + /* Check for a long duration since last event or if we're + * being called for the first time, note that delta can't + * possibly be negative. + */ + if (delta > delay || !dev->raw->last_type) + type |= IR_START_EVENT; + else + ev.duration = delta; + + if (type & IR_START_EVENT) + ir_raw_event_reset(dev); + else if (dev->raw->last_type & IR_SPACE) { + ev.pulse = false; + rc = ir_raw_event_store(dev, &ev); + } else if (dev->raw->last_type & IR_PULSE) { + ev.pulse = true; + rc = ir_raw_event_store(dev, &ev); + } else + return 0; + + dev->raw->last_event = now; + dev->raw->last_type = type; + return rc; +} +EXPORT_SYMBOL_GPL(ir_raw_event_store_edge); + +/** + * ir_raw_event_store_with_filter() - pass next pulse/space to decoders with some processing + * @dev: the struct rc_dev device descriptor + * @type: the type of the event that has occurred + * + * This routine (which may be called from an interrupt context) works + * in similar manner to ir_raw_event_store_edge. + * This routine is intended for devices with limited internal buffer + * It automerges samples of same type, and handles timeouts. Returns non-zero + * if the event was added, and zero if the event was ignored due to idle + * processing. + */ +int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev) +{ + if (!dev->raw) + return -EINVAL; + + /* Ignore spaces in idle mode */ + if (dev->idle && !ev->pulse) + return 0; + else if (dev->idle) + ir_raw_event_set_idle(dev, false); + + if (!dev->raw->this_ev.duration) + dev->raw->this_ev = *ev; + else if (ev->pulse == dev->raw->this_ev.pulse) + dev->raw->this_ev.duration += ev->duration; + else { + ir_raw_event_store(dev, &dev->raw->this_ev); + dev->raw->this_ev = *ev; + } + + /* Enter idle mode if nessesary */ + if (!ev->pulse && dev->timeout && + dev->raw->this_ev.duration >= dev->timeout) + ir_raw_event_set_idle(dev, true); + + return 1; +} +EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter); + +/** + * ir_raw_event_set_idle() - provide hint to rc-core when the device is idle or not + * @dev: the struct rc_dev device descriptor + * @idle: whether the device is idle or not + */ +void ir_raw_event_set_idle(struct rc_dev *dev, bool idle) +{ + if (!dev->raw) + return; + + IR_dprintk(2, "%s idle mode\n", idle ? "enter" : "leave"); + + if (idle) { + dev->raw->this_ev.timeout = true; + ir_raw_event_store(dev, &dev->raw->this_ev); + init_ir_raw_event(&dev->raw->this_ev); + } + + if (dev->s_idle) + dev->s_idle(dev, idle); + + dev->idle = idle; +} +EXPORT_SYMBOL_GPL(ir_raw_event_set_idle); + +/** + * ir_raw_event_handle() - schedules the decoding of stored ir data + * @dev: the struct rc_dev device descriptor + * + * This routine will tell rc-core to start decoding stored ir data. + */ +void ir_raw_event_handle(struct rc_dev *dev) +{ + unsigned long flags; + + if (!dev->raw) + return; + + spin_lock_irqsave(&dev->raw->lock, flags); + wake_up_process(dev->raw->thread); + spin_unlock_irqrestore(&dev->raw->lock, flags); +} +EXPORT_SYMBOL_GPL(ir_raw_event_handle); + +/* used internally by the sysfs interface */ +u64 +ir_raw_get_allowed_protocols(void) +{ + u64 protocols; + mutex_lock(&ir_raw_handler_lock); + protocols = available_protocols; + mutex_unlock(&ir_raw_handler_lock); + return protocols; +} + +static int change_protocol(struct rc_dev *dev, u64 *rc_type) +{ + /* the caller will update dev->enabled_protocols */ + return 0; +} + +/* + * Used to (un)register raw event clients + */ +int ir_raw_event_register(struct rc_dev *dev) +{ + int rc; + struct ir_raw_handler *handler; + + if (!dev) + return -EINVAL; + + dev->raw = kzalloc(sizeof(*dev->raw), GFP_KERNEL); + if (!dev->raw) + return -ENOMEM; + + dev->raw->dev = dev; + dev->enabled_protocols = ~0; + dev->change_protocol = change_protocol; + rc = kfifo_alloc(&dev->raw->kfifo, + sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE, + GFP_KERNEL); + if (rc < 0) + goto out; + + spin_lock_init(&dev->raw->lock); + dev->raw->thread = kthread_run(ir_raw_event_thread, dev->raw, + "rc%ld", dev->devno); + + if (IS_ERR(dev->raw->thread)) { + rc = PTR_ERR(dev->raw->thread); + goto out; + } + + mutex_lock(&ir_raw_handler_lock); + list_add_tail(&dev->raw->list, &ir_raw_client_list); + list_for_each_entry(handler, &ir_raw_handler_list, list) + if (handler->raw_register) + handler->raw_register(dev); + mutex_unlock(&ir_raw_handler_lock); + + return 0; + +out: + kfree(dev->raw); + dev->raw = NULL; + return rc; +} + +void ir_raw_event_unregister(struct rc_dev *dev) +{ + struct ir_raw_handler *handler; + + if (!dev || !dev->raw) + return; + + kthread_stop(dev->raw->thread); + + mutex_lock(&ir_raw_handler_lock); + list_del(&dev->raw->list); + list_for_each_entry(handler, &ir_raw_handler_list, list) + if (handler->raw_unregister) + handler->raw_unregister(dev); + mutex_unlock(&ir_raw_handler_lock); + + kfifo_free(&dev->raw->kfifo); + kfree(dev->raw); + dev->raw = NULL; +} + +/* + * Extension interface - used to register the IR decoders + */ + +int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler) +{ + struct ir_raw_event_ctrl *raw; + + mutex_lock(&ir_raw_handler_lock); + list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list); + if (ir_raw_handler->raw_register) + list_for_each_entry(raw, &ir_raw_client_list, list) + ir_raw_handler->raw_register(raw->dev); + available_protocols |= ir_raw_handler->protocols; + mutex_unlock(&ir_raw_handler_lock); + + return 0; +} +EXPORT_SYMBOL(ir_raw_handler_register); + +void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) +{ + struct ir_raw_event_ctrl *raw; + + mutex_lock(&ir_raw_handler_lock); + list_del(&ir_raw_handler->list); + if (ir_raw_handler->raw_unregister) + list_for_each_entry(raw, &ir_raw_client_list, list) + ir_raw_handler->raw_unregister(raw->dev); + available_protocols &= ~ir_raw_handler->protocols; + mutex_unlock(&ir_raw_handler_lock); +} +EXPORT_SYMBOL(ir_raw_handler_unregister); + +void ir_raw_init(void) +{ + /* Load the decoder modules */ + + load_nec_decode(); + load_rc5_decode(); + load_rc6_decode(); + load_jvc_decode(); + load_sony_decode(); + load_sanyo_decode(); + load_sharp_decode(); + load_mce_kbd_decode(); + load_lirc_codec(); + + /* If needed, we may later add some init code. In this case, + it is needed to change the CONFIG_MODULE test at rc-core.h + */ +} -- cgit v0.10.2 From d6740d86deda4acc8dc2cf196ccb9d8eb30bcb9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Thu, 3 Apr 2014 20:34:53 -0300 Subject: [media] rc-core: fix various sparse warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix various sparse warnings under drivers/media/rc/*.c, mostly by making functions static. Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index f000faf..f0a1f7d 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -686,12 +686,12 @@ static struct pnp_driver fintek_driver = { .shutdown = fintek_shutdown, }; -static int fintek_init(void) +static int __init fintek_init(void) { return pnp_register_driver(&fintek_driver); } -static void fintek_exit(void) +static void __exit fintek_exit(void) { pnp_unregister_driver(&fintek_driver); } diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index ac05c36..7115e68 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -78,11 +78,11 @@ static int display_open(struct inode *inode, struct file *file); static int display_close(struct inode *inode, struct file *file); /* VFD write operation */ -static ssize_t vfd_write(struct file *file, const char *buf, +static ssize_t vfd_write(struct file *file, const char __user *buf, size_t n_bytes, loff_t *pos); /* LCD file_operations override function prototypes */ -static ssize_t lcd_write(struct file *file, const char *buf, +static ssize_t lcd_write(struct file *file, const char __user *buf, size_t n_bytes, loff_t *pos); /*** G L O B A L S ***/ @@ -825,7 +825,7 @@ static struct attribute_group imon_rf_attr_group = { * than 32 bytes are provided spaces will be appended to * generate a full screen. */ -static ssize_t vfd_write(struct file *file, const char *buf, +static ssize_t vfd_write(struct file *file, const char __user *buf, size_t n_bytes, loff_t *pos) { int i; @@ -912,7 +912,7 @@ exit: * display whatever diacritics you need, and so on), but it's also * a lot more complicated than most LCDs... */ -static ssize_t lcd_write(struct file *file, const char *buf, +static ssize_t lcd_write(struct file *file, const char __user *buf, size_t n_bytes, loff_t *pos) { int retval = 0; diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 32fd5f4..447fe35 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1709,12 +1709,12 @@ static struct pnp_driver ite_driver = { .shutdown = ite_shutdown, }; -static int ite_init(void) +static int __init ite_init(void) { return pnp_register_driver(&ite_driver); } -static void ite_exit(void) +static void __exit ite_exit(void) { pnp_unregister_driver(&ite_driver); } diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 8c6008f..7f4fd85 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1221,12 +1221,12 @@ static struct pnp_driver nvt_driver = { .shutdown = nvt_shutdown, }; -static int nvt_init(void) +static int __init nvt_init(void) { return pnp_register_driver(&nvt_driver); } -static void nvt_exit(void) +static void __exit nvt_exit(void) { pnp_unregister_driver(&nvt_driver); } -- cgit v0.10.2 From 7cec72ce00c174fd74d1183599d3d3dcc5dedc26 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 25 Jul 2014 23:25:36 -0300 Subject: [media] rc-core: don't use dynamic_pr_debug for IR_dprintk() The hole point of IR_dprintk() is that, once a level is given at debug parameter, all enabled IR parsers will show their debug messages. While converting it to dynamic_printk might be a good idea, right now it just makes very hard to debug the drivers, as one needs to both pass debug=1 or debug=2 to rc-core and to use the dynamic printk to enable all the desired lines. That doesn't make sense! So, revert to the old way, as a single line is changed, and the debug parameter will now work as expected. Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 3047837..2c7fbca 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -26,7 +26,7 @@ extern int rc_core_debug; #define IR_dprintk(level, fmt, ...) \ do { \ if (rc_core_debug >= level) \ - pr_debug("%s: " fmt, __func__, ##__VA_ARGS__); \ + printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \ } while (0) enum rc_driver_type { -- cgit v0.10.2 From 20d7e3ca1be76a13fb4ea22246bb9d0c4c365809 Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sun, 11 May 2014 08:12:09 -0300 Subject: [media] ati_remote: Check the checksum An input report is 4 bytes long, but there are only 12 bits of actual payload. The 4 bytes are: data[0] = 0x14 data[1] = data[2] + data[3] + 0xd5 (a checksum byte) data[2] = the raw scancode (plus toggle bit in msbit) data[3] = channel << 4 (the low 4 bits must be zero) Ignore reports with a bad checksum. Signed-off-by: George Spelvin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 2e13353..52a5af6 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -507,8 +507,9 @@ static void ati_remote_input_report(struct urb *urb) */ /* Deal with strange looking inputs */ - if ( (urb->actual_length != 4) || (data[0] != 0x14) || - ((data[3] & 0x0f) != 0x00) ) { + if ( urb->actual_length != 4 || data[0] != 0x14 || + data[1] != (unsigned char)(data[2] + data[3] + 0xD5) || + (data[3] & 0x0f) != 0x00) { ati_remote_dump(&urb->dev->dev, data, urb->actual_length); return; } @@ -524,9 +525,9 @@ static void ati_remote_input_report(struct urb *urb) remote_num = (data[3] >> 4) & 0x0f; if (channel_mask & (1 << (remote_num + 1))) { dbginfo(&ati_remote->interface->dev, - "Masked input from channel 0x%02x: data %02x,%02x, " + "Masked input from channel 0x%02x: data %02x, " "mask= 0x%02lx\n", - remote_num, data[1], data[2], channel_mask); + remote_num, data[2], channel_mask); return; } -- cgit v0.10.2 From 528abb9888d8031d60994e5a393631a1f34b3007 Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sun, 11 May 2014 08:12:55 -0300 Subject: [media] ati_remote: Shrink ati_remote_tbl structure The variable types are simply larger than they need to be. Shrink to signed and unsigned chars. Signed-off-by: George Spelvin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 52a5af6..c768c26 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -289,11 +289,11 @@ struct ati_remote { /* Translation table from hardware messages to input events. */ static const struct { - short kind; + unsigned char kind; unsigned char data; - int type; - unsigned int code; - int value; + unsigned char type; + unsigned short code; + signed char value; } ati_remote_tbl[] = { /* Directional control pad axes */ {KIND_ACCEL, 0x70, EV_REL, REL_X, -1}, /* left */ -- cgit v0.10.2 From f95589cc41ff973bd5d6aecc6205b048bdb95ed7 Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sun, 11 May 2014 08:13:30 -0300 Subject: [media] ati_remote: Delete superfluous input_sync() It's not necessary, and since both events happen "at the same time" in response to a single input event, the input device framework prefers not to have it there. (It's not a big deal one way or the other, but deleting cruft is generally a good thing.) Signed-off-by: George Spelvin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index c768c26..658171f 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -632,7 +632,6 @@ static void ati_remote_input_report(struct urb *urb) input_event(dev, ati_remote_tbl[index].type, ati_remote_tbl[index].code, 1); - input_sync(dev); input_event(dev, ati_remote_tbl[index].type, ati_remote_tbl[index].code, 0); input_sync(dev); -- cgit v0.10.2 From 8ecd5e32d8c8fc02d565aa0c1f3f997dea75f5de Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sun, 11 May 2014 08:14:18 -0300 Subject: [media] ati_remote: Generalize KIND_ACCEL to accept diagonals Rather than having special code cases for diagonal mouse movements, extend the general purpose code used for the cardinal directions to handle arbitrary (x,y) deltas. The deltas themselves are stored in translation table's "code" field; this is also progress toward the goal of eliminating the "value" element entirely. Signed-off-by: George Spelvin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 658171f..401a9f4 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -281,11 +281,7 @@ struct ati_remote { #define KIND_END 0 #define KIND_LITERAL 1 /* Simply pass to input system */ #define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */ -#define KIND_LU 3 /* Directional keypad diagonals - left up, */ -#define KIND_RU 4 /* right up, */ -#define KIND_LD 5 /* left down, */ -#define KIND_RD 6 /* right down */ -#define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/ +#define KIND_ACCEL 3 /* Directional keypad - left, right, up, down.*/ /* Translation table from hardware messages to input events. */ static const struct { @@ -295,16 +291,17 @@ static const struct { unsigned short code; signed char value; } ati_remote_tbl[] = { - /* Directional control pad axes */ - {KIND_ACCEL, 0x70, EV_REL, REL_X, -1}, /* left */ - {KIND_ACCEL, 0x71, EV_REL, REL_X, 1}, /* right */ - {KIND_ACCEL, 0x72, EV_REL, REL_Y, -1}, /* up */ - {KIND_ACCEL, 0x73, EV_REL, REL_Y, 1}, /* down */ + /* Directional control pad axes. Code is xxyy */ + {KIND_ACCEL, 0x70, EV_REL, 0xff00, 0}, /* left */ + {KIND_ACCEL, 0x71, EV_REL, 0x0100, 0}, /* right */ + {KIND_ACCEL, 0x72, EV_REL, 0x00ff, 0}, /* up */ + {KIND_ACCEL, 0x73, EV_REL, 0x0001, 0}, /* down */ + /* Directional control pad diagonals */ - {KIND_LU, 0x74, EV_REL, 0, 0}, /* left up */ - {KIND_RU, 0x75, EV_REL, 0, 0}, /* right up */ - {KIND_LD, 0x77, EV_REL, 0, 0}, /* left down */ - {KIND_RD, 0x76, EV_REL, 0, 0}, /* right down */ + {KIND_ACCEL, 0x74, EV_REL, 0xffff, 0}, /* left up */ + {KIND_ACCEL, 0x75, EV_REL, 0x01ff, 0}, /* right up */ + {KIND_ACCEL, 0x77, EV_REL, 0xff01, 0}, /* left down */ + {KIND_ACCEL, 0x76, EV_REL, 0x0101, 0}, /* right down */ /* "Mouse button" buttons */ {KIND_LITERAL, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */ @@ -493,7 +490,6 @@ static void ati_remote_input_report(struct urb *urb) unsigned char *data= ati_remote->inbuf; struct input_dev *dev = ati_remote->idev; int index = -1; - int acc; int remote_num; unsigned char scancode; u32 wheel_keycode = KEY_RESERVED; @@ -573,10 +569,8 @@ static void ati_remote_input_report(struct urb *urb) input_sync(dev); ati_remote->old_jiffies = jiffies; - return; - } - if (index < 0 || ati_remote_tbl[index].kind == KIND_FILTERED) { + } else if (index < 0 || ati_remote_tbl[index].kind == KIND_FILTERED) { unsigned long now = jiffies; /* Filter duplicate events which happen "too close" together. */ @@ -636,46 +630,27 @@ static void ati_remote_input_report(struct urb *urb) ati_remote_tbl[index].code, 0); input_sync(dev); - } else { + } else if (ati_remote_tbl[index].kind == KIND_ACCEL) { + signed char dx = ati_remote_tbl[index].code >> 8; + signed char dy = ati_remote_tbl[index].code & 255; /* * Other event kinds are from the directional control pad, and * have an acceleration factor applied to them. Without this * acceleration, the control pad is mostly unusable. */ - acc = ati_remote_compute_accel(ati_remote); - - switch (ati_remote_tbl[index].kind) { - case KIND_ACCEL: - input_event(dev, ati_remote_tbl[index].type, - ati_remote_tbl[index].code, - ati_remote_tbl[index].value * acc); - break; - case KIND_LU: - input_report_rel(dev, REL_X, -acc); - input_report_rel(dev, REL_Y, -acc); - break; - case KIND_RU: - input_report_rel(dev, REL_X, acc); - input_report_rel(dev, REL_Y, -acc); - break; - case KIND_LD: - input_report_rel(dev, REL_X, -acc); - input_report_rel(dev, REL_Y, acc); - break; - case KIND_RD: - input_report_rel(dev, REL_X, acc); - input_report_rel(dev, REL_Y, acc); - break; - default: - dev_dbg(&ati_remote->interface->dev, - "ati_remote kind=%d\n", - ati_remote_tbl[index].kind); - } + int acc = ati_remote_compute_accel(ati_remote); + if (dx) + input_report_rel(dev, REL_X, dx * acc); + if (dy) + input_report_rel(dev, REL_Y, dy * acc); input_sync(dev); ati_remote->old_jiffies = jiffies; ati_remote->old_data = data[2]; + } else { + dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n", + ati_remote_tbl[index].kind); } } -- cgit v0.10.2 From 3f245b9cb44453b911499e702dc9301f0c4981d3 Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sun, 11 May 2014 08:14:54 -0300 Subject: [media] ati_remote: Shrink the ati_remote_tbl even more Get rid of the unnecessary "type" and "value" fields. Signed-off-by: George Spelvin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 401a9f4..a41986c 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -279,43 +279,42 @@ struct ati_remote { /* "Kinds" of messages sent from the hardware to the driver. */ #define KIND_END 0 -#define KIND_LITERAL 1 /* Simply pass to input system */ +#define KIND_LITERAL 1 /* Simply pass to input system as EV_KEY */ #define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */ -#define KIND_ACCEL 3 /* Directional keypad - left, right, up, down.*/ +#define KIND_ACCEL 3 /* Translate to EV_REL mouse-move events */ /* Translation table from hardware messages to input events. */ static const struct { unsigned char kind; - unsigned char data; - unsigned char type; - unsigned short code; - signed char value; + unsigned char data; /* Raw key code from remote */ + unsigned short code; /* Input layer translation */ } ati_remote_tbl[] = { /* Directional control pad axes. Code is xxyy */ - {KIND_ACCEL, 0x70, EV_REL, 0xff00, 0}, /* left */ - {KIND_ACCEL, 0x71, EV_REL, 0x0100, 0}, /* right */ - {KIND_ACCEL, 0x72, EV_REL, 0x00ff, 0}, /* up */ - {KIND_ACCEL, 0x73, EV_REL, 0x0001, 0}, /* down */ + {KIND_ACCEL, 0x70, 0xff00}, /* left */ + {KIND_ACCEL, 0x71, 0x0100}, /* right */ + {KIND_ACCEL, 0x72, 0x00ff}, /* up */ + {KIND_ACCEL, 0x73, 0x0001}, /* down */ /* Directional control pad diagonals */ - {KIND_ACCEL, 0x74, EV_REL, 0xffff, 0}, /* left up */ - {KIND_ACCEL, 0x75, EV_REL, 0x01ff, 0}, /* right up */ - {KIND_ACCEL, 0x77, EV_REL, 0xff01, 0}, /* left down */ - {KIND_ACCEL, 0x76, EV_REL, 0x0101, 0}, /* right down */ - - /* "Mouse button" buttons */ - {KIND_LITERAL, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */ - {KIND_LITERAL, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */ - {KIND_LITERAL, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */ - {KIND_LITERAL, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */ + {KIND_ACCEL, 0x74, 0xffff}, /* left up */ + {KIND_ACCEL, 0x75, 0x01ff}, /* right up */ + {KIND_ACCEL, 0x77, 0xff01}, /* left down */ + {KIND_ACCEL, 0x76, 0x0101}, /* right down */ + + /* "Mouse button" buttons. The code below uses the fact that the + * lsbit of the raw code is a down/up indicator. */ + {KIND_LITERAL, 0x78, BTN_LEFT}, /* left btn down */ + {KIND_LITERAL, 0x79, BTN_LEFT}, /* left btn up */ + {KIND_LITERAL, 0x7c, BTN_RIGHT},/* right btn down */ + {KIND_LITERAL, 0x7d, BTN_RIGHT},/* right btn up */ /* Artificial "doubleclick" events are generated by the hardware. * They are mapped to the "side" and "extra" mouse buttons here. */ - {KIND_FILTERED, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */ - {KIND_FILTERED, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */ + {KIND_FILTERED, 0x7a, BTN_SIDE}, /* left dblclick */ + {KIND_FILTERED, 0x7e, BTN_EXTRA},/* right dblclick */ /* Non-mouse events are handled by rc-core */ - {KIND_END, 0x00, EV_MAX + 1, 0, 0} + {KIND_END, 0x00, 0} }; /* @@ -563,9 +562,12 @@ static void ati_remote_input_report(struct urb *urb) } if (index >= 0 && ati_remote_tbl[index].kind == KIND_LITERAL) { - input_event(dev, ati_remote_tbl[index].type, - ati_remote_tbl[index].code, - ati_remote_tbl[index].value); + /* + * The lsbit of the raw key code is a down/up flag. + * Invert it to match the input layer's conventions. + */ + input_event(dev, EV_KEY, ati_remote_tbl[index].code, + !(data[2] & 1)); input_sync(dev); ati_remote->old_jiffies = jiffies; @@ -586,9 +588,9 @@ static void ati_remote_input_report(struct urb *urb) ati_remote->old_data = data[2]; ati_remote->old_jiffies = now; - /* Ensure we skip at least the 4 first duplicate events (generated - * by a single keypress), and continue skipping until repeat_delay - * msecs have passed + /* Ensure we skip at least the 4 first duplicate events + * (generated by a single keypress), and continue skipping + * until repeat_delay msecs have passed. */ if (ati_remote->repeat_count > 0 && (ati_remote->repeat_count < 5 || @@ -624,10 +626,8 @@ static void ati_remote_input_report(struct urb *urb) return; } - input_event(dev, ati_remote_tbl[index].type, - ati_remote_tbl[index].code, 1); - input_event(dev, ati_remote_tbl[index].type, - ati_remote_tbl[index].code, 0); + input_event(dev, EV_KEY, ati_remote_tbl[index].code, 1); + input_event(dev, EV_KEY, ati_remote_tbl[index].code, 0); input_sync(dev); } else if (ati_remote_tbl[index].kind == KIND_ACCEL) { @@ -738,7 +738,8 @@ static void ati_remote_input_init(struct ati_remote *ati_remote) BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA); idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) - if (ati_remote_tbl[i].type == EV_KEY) + if (ati_remote_tbl[i].kind == KIND_LITERAL || + ati_remote_tbl[i].kind == KIND_FILTERED) set_bit(ati_remote_tbl[i].code, idev->keybit); input_set_drvdata(idev, ati_remote); -- cgit v0.10.2 From 89b0c0d2779ef05d753c125155a362452cc0f155 Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sun, 11 May 2014 08:15:44 -0300 Subject: [media] ati_remote: Merge some duplicate code The KIND_FILTERED assignment of old_jiffies can't be merged, because it must precede repeat handling. Signed-off-by: George Spelvin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index a41986c..8a15ee5 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -568,7 +568,6 @@ static void ati_remote_input_report(struct urb *urb) */ input_event(dev, EV_KEY, ati_remote_tbl[index].code, !(data[2] & 1)); - input_sync(dev); ati_remote->old_jiffies = jiffies; @@ -585,7 +584,6 @@ static void ati_remote_input_report(struct urb *urb) ati_remote->first_jiffies = now; } - ati_remote->old_data = data[2]; ati_remote->old_jiffies = now; /* Ensure we skip at least the 4 first duplicate events @@ -598,7 +596,10 @@ static void ati_remote_input_report(struct urb *urb) msecs_to_jiffies(repeat_delay)))) return; - if (index < 0) { + if (index >= 0) { + input_event(dev, EV_KEY, ati_remote_tbl[index].code, 1); + input_event(dev, EV_KEY, ati_remote_tbl[index].code, 0); + } else { /* Not a mouse event, hand it to rc-core. */ int count = 1; @@ -623,13 +624,9 @@ static void ati_remote_input_report(struct urb *urb) scancode, data[2]); rc_keyup(ati_remote->rdev); } - return; + goto nosync; } - input_event(dev, EV_KEY, ati_remote_tbl[index].code, 1); - input_event(dev, EV_KEY, ati_remote_tbl[index].code, 0); - input_sync(dev); - } else if (ati_remote_tbl[index].kind == KIND_ACCEL) { signed char dx = ati_remote_tbl[index].code >> 8; signed char dy = ati_remote_tbl[index].code & 255; @@ -644,14 +641,16 @@ static void ati_remote_input_report(struct urb *urb) input_report_rel(dev, REL_X, dx * acc); if (dy) input_report_rel(dev, REL_Y, dy * acc); - input_sync(dev); - ati_remote->old_jiffies = jiffies; - ati_remote->old_data = data[2]; + } else { dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n", ati_remote_tbl[index].kind); + return; } + input_sync(dev); +nosync: + ati_remote->old_data = data[2]; } /* -- cgit v0.10.2 From 1e182708c26cf92b3de160f12175c849d884b3f3 Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sun, 11 May 2014 08:16:15 -0300 Subject: [media] ati_remote: Use non-alomic __set_bit There's no reason to use a LOCK prefix here. Signed-off-by: George Spelvin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 8a15ee5..a356318 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -739,7 +739,7 @@ static void ati_remote_input_init(struct ati_remote *ati_remote) for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) if (ati_remote_tbl[i].kind == KIND_LITERAL || ati_remote_tbl[i].kind == KIND_FILTERED) - set_bit(ati_remote_tbl[i].code, idev->keybit); + __set_bit(ati_remote_tbl[i].code, idev->keybit); input_set_drvdata(idev, ati_remote); -- cgit v0.10.2 From d701c8318bc233f90ba6a7a6d1d2aace9e8ea2cf Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sun, 11 May 2014 08:16:56 -0300 Subject: [media] ati_remote: Sort buttons in top-to-bottom order Since numerical order corresponds to top-left-to-bottom-right order on the remote, this makes the table easier to read. Signed-off-by: George Spelvin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/keymaps/rc-ati-x10.c b/drivers/media/rc/keymaps/rc-ati-x10.c index 81506440..4e2cbba 100644 --- a/drivers/media/rc/keymaps/rc-ati-x10.c +++ b/drivers/media/rc/keymaps/rc-ati-x10.c @@ -27,6 +27,23 @@ #include static struct rc_map_table ati_x10[] = { + /* keyboard - Above the cursor pad */ + { 0x00, KEY_A }, + { 0x01, KEY_B }, + { 0x02, KEY_POWER }, /* Power */ + + { 0x03, KEY_TV }, /* TV */ + { 0x04, KEY_DVD }, /* DVD */ + { 0x05, KEY_WWW }, /* WEB */ + { 0x06, KEY_BOOKMARKS }, /* "book" */ + { 0x07, KEY_EDIT }, /* "hand" */ + /* Below the cursor pad */ + { 0x09, KEY_VOLUMEDOWN }, /* VOL + */ + { 0x08, KEY_VOLUMEUP }, /* VOL - */ + { 0x0a, KEY_MUTE }, /* MUTE */ + { 0x0b, KEY_CHANNELUP }, /* CH + */ + { 0x0c, KEY_CHANNELDOWN },/* CH - */ + { 0x0d, KEY_1 }, { 0x0e, KEY_2 }, { 0x0f, KEY_3 }, @@ -36,42 +53,36 @@ static struct rc_map_table ati_x10[] = { { 0x13, KEY_7 }, { 0x14, KEY_8 }, { 0x15, KEY_9 }, + { 0x16, KEY_MENU }, /* "menu" */ { 0x17, KEY_0 }, - { 0x00, KEY_A }, - { 0x01, KEY_B }, + { 0x18, KEY_KPENTER }, /* "check" */ + + /* DVD navigation buttons */ { 0x19, KEY_C }, + { 0x1a, KEY_UP }, /* up */ { 0x1b, KEY_D }, - { 0x21, KEY_E }, - { 0x23, KEY_F }, - { 0x18, KEY_KPENTER }, /* "check" */ - { 0x16, KEY_MENU }, /* "menu" */ - { 0x02, KEY_POWER }, /* Power */ - { 0x03, KEY_TV }, /* TV */ - { 0x04, KEY_DVD }, /* DVD */ - { 0x05, KEY_WWW }, /* WEB */ - { 0x06, KEY_BOOKMARKS }, /* "book" */ - { 0x07, KEY_EDIT }, /* "hand" */ { 0x1c, KEY_COFFEE }, /* "timer" */ - { 0x20, KEY_FRONT }, /* "max" */ { 0x1d, KEY_LEFT }, /* left */ + { 0x1e, KEY_OK }, /* "OK" */ { 0x1f, KEY_RIGHT }, /* right */ + { 0x20, KEY_FRONT }, /* "max" */ + + { 0x21, KEY_E }, { 0x22, KEY_DOWN }, /* down */ - { 0x1a, KEY_UP }, /* up */ - { 0x1e, KEY_OK }, /* "OK" */ - { 0x09, KEY_VOLUMEDOWN }, /* VOL + */ - { 0x08, KEY_VOLUMEUP }, /* VOL - */ - { 0x0a, KEY_MUTE }, /* MUTE */ - { 0x0b, KEY_CHANNELUP }, /* CH + */ - { 0x0c, KEY_CHANNELDOWN },/* CH - */ - { 0x27, KEY_RECORD }, /* ( o) red */ - { 0x25, KEY_PLAY }, /* ( >) */ + { 0x23, KEY_F }, + /* Play/stop/pause buttons */ { 0x24, KEY_REWIND }, /* (<<) */ + { 0x25, KEY_PLAY }, /* ( >) */ { 0x26, KEY_FORWARD }, /* (>>) */ + + { 0x27, KEY_RECORD }, /* ( o) red */ { 0x28, KEY_STOP }, /* ([]) */ { 0x29, KEY_PAUSE }, /* ('') */ - { 0x2b, KEY_PREVIOUS }, /* (<-) */ + + /* Extra keys, not on the original ATI remote */ { 0x2a, KEY_NEXT }, /* (>+) */ + { 0x2b, KEY_PREVIOUS }, /* (<-) */ { 0x2d, KEY_INFO }, /* PLAYING */ { 0x2e, KEY_HOME }, /* TOP */ { 0x2f, KEY_END }, /* END */ -- cgit v0.10.2 From 220edfb0f724594327e291582249d47e6017a697 Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sun, 11 May 2014 08:17:37 -0300 Subject: [media] ati_remote: Add comments to keycode table A more detailed description of what the buttons look like and their intended function makes it easier for people to maintain this code without access to the hardware. [m.chehab@samsung.com: Fixed a typo "Mdeia" instead of "Media"] Signed-off-by: George Spelvin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/keymaps/rc-ati-x10.c b/drivers/media/rc/keymaps/rc-ati-x10.c index 4e2cbba..8b01a72 100644 --- a/drivers/media/rc/keymaps/rc-ati-x10.c +++ b/drivers/media/rc/keymaps/rc-ati-x10.c @@ -26,6 +26,17 @@ #include #include +/* + * Intended usage comments below are from vendor-supplied + * Source: ATI REMOTE WONDER™ Installation Guide + * http://www2.ati.com/manuals/remctrl.pdf + * + * Scancodes were in strict left-right, top-bottom order on the + * original ATI Remote Wonder, but were moved on later models. + * + * Keys A-F are intended to be user-programmable. + */ + static struct rc_map_table ati_x10[] = { /* keyboard - Above the cursor pad */ { 0x00, KEY_A }, @@ -35,9 +46,11 @@ static struct rc_map_table ati_x10[] = { { 0x03, KEY_TV }, /* TV */ { 0x04, KEY_DVD }, /* DVD */ { 0x05, KEY_WWW }, /* WEB */ - { 0x06, KEY_BOOKMARKS }, /* "book" */ - { 0x07, KEY_EDIT }, /* "hand" */ - /* Below the cursor pad */ + { 0x06, KEY_BOOKMARKS }, /* "book": Open Media Library */ + { 0x07, KEY_EDIT }, /* "hand": Toggle left mouse button (grab) */ + + /* Mouse emulation pad goes here, handled by driver separately */ + { 0x09, KEY_VOLUMEDOWN }, /* VOL + */ { 0x08, KEY_VOLUMEUP }, /* VOL - */ { 0x0a, KEY_MUTE }, /* MUTE */ @@ -53,9 +66,9 @@ static struct rc_map_table ati_x10[] = { { 0x13, KEY_7 }, { 0x14, KEY_8 }, { 0x15, KEY_9 }, - { 0x16, KEY_MENU }, /* "menu" */ + { 0x16, KEY_MENU }, /* "menu": DVD root menu */ { 0x17, KEY_0 }, - { 0x18, KEY_KPENTER }, /* "check" */ + { 0x18, KEY_KPENTER }, /* "check": DVD setup menu */ /* DVD navigation buttons */ { 0x19, KEY_C }, @@ -72,13 +85,13 @@ static struct rc_map_table ati_x10[] = { { 0x22, KEY_DOWN }, /* down */ { 0x23, KEY_F }, /* Play/stop/pause buttons */ - { 0x24, KEY_REWIND }, /* (<<) */ - { 0x25, KEY_PLAY }, /* ( >) */ - { 0x26, KEY_FORWARD }, /* (>>) */ + { 0x24, KEY_REWIND }, /* (<<) Rewind */ + { 0x25, KEY_PLAY }, /* ( >) Play */ + { 0x26, KEY_FORWARD }, /* (>>) Fast forward */ { 0x27, KEY_RECORD }, /* ( o) red */ - { 0x28, KEY_STOP }, /* ([]) */ - { 0x29, KEY_PAUSE }, /* ('') */ + { 0x28, KEY_STOP }, /* ([]) Stop */ + { 0x29, KEY_PAUSE }, /* ('') Pause */ /* Extra keys, not on the original ATI remote */ { 0x2a, KEY_NEXT }, /* (>+) */ -- cgit v0.10.2 From 356237d67a447138ec0cae723c0a3e755e3a5a85 Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sun, 11 May 2014 08:19:01 -0300 Subject: [media] ati_remote: Better default keycodes This tries to make them more like other remotes, and/or the button labels. Notably, the (>>) button is made KEY_FASTFORWARD, which is the correct opposite of (<<)'s KEY_REVERSE. (It was KEY_FORWARD, something else entirely.) Likewise, KEY_STOP is the Sun keyboard "interrupt program" key; the media key is KEY_STOPCD. A restriction is that I try to avoid keycodes above 255, as the X11 client/server protocol is limited to 8-bit key codes. If not for this, I would have used the KEY_NUMERIC_x codes for the numbers. Signed-off-by: George Spelvin Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/keymaps/rc-ati-x10.c b/drivers/media/rc/keymaps/rc-ati-x10.c index 8b01a72..4bdc709 100644 --- a/drivers/media/rc/keymaps/rc-ati-x10.c +++ b/drivers/media/rc/keymaps/rc-ati-x10.c @@ -57,6 +57,11 @@ static struct rc_map_table ati_x10[] = { { 0x0b, KEY_CHANNELUP }, /* CH + */ { 0x0c, KEY_CHANNELDOWN },/* CH - */ + /* + * We could use KEY_NUMERIC_x for these, but the X11 protocol + * has problems with keycodes greater than 255, so avoid those high + * keycodes in default maps. + */ { 0x0d, KEY_1 }, { 0x0e, KEY_2 }, { 0x0f, KEY_3 }, @@ -67,39 +72,44 @@ static struct rc_map_table ati_x10[] = { { 0x14, KEY_8 }, { 0x15, KEY_9 }, { 0x16, KEY_MENU }, /* "menu": DVD root menu */ + /* KEY_NUMERIC_STAR? */ { 0x17, KEY_0 }, - { 0x18, KEY_KPENTER }, /* "check": DVD setup menu */ + { 0x18, KEY_SETUP }, /* "check": DVD setup menu */ + /* KEY_NUMERIC_POUND? */ /* DVD navigation buttons */ { 0x19, KEY_C }, { 0x1a, KEY_UP }, /* up */ { 0x1b, KEY_D }, - { 0x1c, KEY_COFFEE }, /* "timer" */ + { 0x1c, KEY_PROPS }, /* "timer" Should be Data On Screen */ + /* Symbol is "circle nailed to box" */ { 0x1d, KEY_LEFT }, /* left */ { 0x1e, KEY_OK }, /* "OK" */ { 0x1f, KEY_RIGHT }, /* right */ - { 0x20, KEY_FRONT }, /* "max" */ - + { 0x20, KEY_SCREEN }, /* "max" (X11 warning: 0x177) */ + /* Should be AC View Toggle, but + that's not in . + KEY_ZOOM (0x174)? */ { 0x21, KEY_E }, { 0x22, KEY_DOWN }, /* down */ { 0x23, KEY_F }, /* Play/stop/pause buttons */ { 0x24, KEY_REWIND }, /* (<<) Rewind */ - { 0x25, KEY_PLAY }, /* ( >) Play */ - { 0x26, KEY_FORWARD }, /* (>>) Fast forward */ + { 0x25, KEY_PLAY }, /* ( >) Play (KEY_PLAYCD?) */ + { 0x26, KEY_FASTFORWARD }, /* (>>) Fast forward */ { 0x27, KEY_RECORD }, /* ( o) red */ - { 0x28, KEY_STOP }, /* ([]) Stop */ - { 0x29, KEY_PAUSE }, /* ('') Pause */ + { 0x28, KEY_STOPCD }, /* ([]) Stop (KEY_STOP is something else!) */ + { 0x29, KEY_PAUSE }, /* ('') Pause (KEY_PAUSECD?) */ /* Extra keys, not on the original ATI remote */ { 0x2a, KEY_NEXT }, /* (>+) */ { 0x2b, KEY_PREVIOUS }, /* (<-) */ - { 0x2d, KEY_INFO }, /* PLAYING */ + { 0x2d, KEY_INFO }, /* PLAYING (X11 warning: 0x166) */ { 0x2e, KEY_HOME }, /* TOP */ { 0x2f, KEY_END }, /* END */ - { 0x30, KEY_SELECT }, /* SELECT */ + { 0x30, KEY_SELECT }, /* SELECT (X11 warning: 0x161) */ }; static struct rc_map_list ati_x10_map = { -- cgit v0.10.2 From 7d37743a3ef0980432cb4fdf8e3906c54d6fb1dd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 4 Jun 2014 15:46:23 -0300 Subject: [media] coda: Return the real error on platform_get_irq() No need to return a 'fake' return value on platform_get_irq() failure. Propagate the real error instead. Signed-off-by: Fabio Estevam Acked-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 18758e2..840402f 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -3770,7 +3770,7 @@ static int coda_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "failed to get irq resource\n"); - return -ENOENT; + return irq; } if (devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler, -- cgit v0.10.2 From 08c8d14e7507d318cbd6ecae2c6c7cb31d0037af Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 4 Jun 2014 15:46:24 -0300 Subject: [media] coda: Propagate the correct error on devm_request_threaded_irq() If devm_request_threaded_irq() fails, we should better propagate the real error. Also, print out the error code in the dev_err message. Signed-off-by: Fabio Estevam Acked-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 840402f..c44feee 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -3773,10 +3773,11 @@ static int coda_probe(struct platform_device *pdev) return irq; } - if (devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler, - IRQF_ONESHOT, dev_name(&pdev->dev), dev) < 0) { - dev_err(&pdev->dev, "failed to request irq\n"); - return -ENOENT; + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler, + IRQF_ONESHOT, dev_name(&pdev->dev), dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq: %d\n", ret); + return ret; } dev->rstc = devm_reset_control_get(&pdev->dev, NULL); -- cgit v0.10.2 From 64438fe65a5470cef68c82cf585405cac3050212 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 5 Jun 2014 17:48:11 -0300 Subject: [media] staging: lirc: remove sa1100 support The LIRC support for sa1100 appears to have never worked because it relies on header files that have never been present in git history. Actually trying to build the driver on an ARM sa1100 kernel fails, so let's just remove the broken support. Signed-off-by: Arnd Bergmann Cc: Jarod Wilson Cc: Tuomas Tynkkynen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c index e31cbb8..79da3ad 100644 --- a/drivers/staging/media/lirc/lirc_sir.c +++ b/drivers/staging/media/lirc/lirc_sir.c @@ -55,13 +55,6 @@ #include #include #include -#ifdef LIRC_ON_SA1100 -#include -#ifdef CONFIG_SA1100_COLLIE -#include -#include -#endif -#endif #include @@ -94,35 +87,6 @@ static void init_act200(void); static void init_act220(void); #endif -/*** SA1100 ***/ -#ifdef LIRC_ON_SA1100 -struct sa1100_ser2_registers { - /* HSSP control register */ - unsigned char hscr0; - /* UART registers */ - unsigned char utcr0; - unsigned char utcr1; - unsigned char utcr2; - unsigned char utcr3; - unsigned char utcr4; - unsigned char utdr; - unsigned char utsr0; - unsigned char utsr1; -} sr; - -static int irq = IRQ_Ser2ICP; - -#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0 - -/* pulse/space ratio of 50/50 */ -static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY); -/* 1000000/freq-pulse_width */ -static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY); -static unsigned int freq = 38000; /* modulation frequency */ -static unsigned int duty_cycle = 50; /* duty cycle of 50% */ - -#endif - #define RBUF_LEN 1024 #define WBUF_LEN 1024 @@ -205,17 +169,6 @@ static void drop_hardware(void); static int init_port(void); static void drop_port(void); -#ifdef LIRC_ON_SA1100 -static void on(void) -{ - PPSR |= PPC_TXD2; -} - -static void off(void) -{ - PPSR &= ~PPC_TXD2; -} -#else static inline unsigned int sinp(int offset) { return inb(io + offset); @@ -225,7 +178,6 @@ static inline void soutp(int offset, int value) { outb(value, io + offset); } -#endif #ifndef MAX_UDELAY_MS #define MAX_UDELAY_US 5000 @@ -305,10 +257,6 @@ static ssize_t lirc_write(struct file *file, const char __user *buf, size_t n, if (IS_ERR(tx_buf)) return PTR_ERR(tx_buf); i = 0; -#ifdef LIRC_ON_SA1100 - /* disable receiver */ - Ser2UTCR3 = 0; -#endif local_irq_save(flags); while (1) { if (i >= count) @@ -323,15 +271,6 @@ static ssize_t lirc_write(struct file *file, const char __user *buf, size_t n, i++; } local_irq_restore(flags); -#ifdef LIRC_ON_SA1100 - off(); - udelay(1000); /* wait 1ms for IR diode to recover */ - Ser2UTCR3 = 0; - /* clear status register to prevent unwanted interrupts */ - Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); - /* enable receiver */ - Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE; -#endif kfree(tx_buf); return count; } @@ -341,25 +280,12 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) u32 __user *uptr = (u32 __user *)arg; int retval = 0; u32 value = 0; -#ifdef LIRC_ON_SA1100 - - if (cmd == LIRC_GET_FEATURES) - value = LIRC_CAN_SEND_PULSE | - LIRC_CAN_SET_SEND_DUTY_CYCLE | - LIRC_CAN_SET_SEND_CARRIER | - LIRC_CAN_REC_MODE2; - else if (cmd == LIRC_GET_SEND_MODE) - value = LIRC_MODE_PULSE; - else if (cmd == LIRC_GET_REC_MODE) - value = LIRC_MODE_MODE2; -#else if (cmd == LIRC_GET_FEATURES) value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2; else if (cmd == LIRC_GET_SEND_MODE) value = LIRC_MODE_PULSE; else if (cmd == LIRC_GET_REC_MODE) value = LIRC_MODE_MODE2; -#endif switch (cmd) { case LIRC_GET_FEATURES: @@ -372,37 +298,6 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) case LIRC_SET_REC_MODE: retval = get_user(value, uptr); break; -#ifdef LIRC_ON_SA1100 - case LIRC_SET_SEND_DUTY_CYCLE: - retval = get_user(value, uptr); - if (retval) - return retval; - if (value <= 0 || value > 100) - return -EINVAL; - /* (value/100)*(1000000/freq) */ - duty_cycle = value; - pulse_width = (unsigned long) duty_cycle*10000/freq; - space_width = (unsigned long) 1000000L/freq-pulse_width; - if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) - pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; - if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) - space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; - break; - case LIRC_SET_SEND_CARRIER: - retval = get_user(value, uptr); - if (retval) - return retval; - if (value > 500000 || value < 20000) - return -EINVAL; - freq = value; - pulse_width = (unsigned long) duty_cycle*10000/freq; - space_width = (unsigned long) 1000000L/freq-pulse_width; - if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) - pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; - if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) - space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; - break; -#endif default: retval = -ENOIOCTLCMD; @@ -539,10 +434,8 @@ static void sir_timeout(unsigned long data) /* avoid interference with interrupt */ spin_lock_irqsave(&timer_lock, flags); if (last_value) { -#ifndef LIRC_ON_SA1100 /* clear unread bits in UART and restart */ outb(UART_FCR_CLEAR_RCVR, io + UART_FCR); -#endif /* determine 'virtual' pulse end: */ pulse_end = delta(&last_tv, &last_intr_tv); dprintk("timeout add %d for %lu usec\n", last_value, pulse_end); @@ -558,62 +451,6 @@ static irqreturn_t sir_interrupt(int irq, void *dev_id) unsigned char data; struct timeval curr_tv; static unsigned long deltv; -#ifdef LIRC_ON_SA1100 - int status; - static int n; - - status = Ser2UTSR0; - /* - * Deal with any receive errors first. The bytes in error may be - * the only bytes in the receive FIFO, so we do this first. - */ - while (status & UTSR0_EIF) { - int bstat; - - if (debug) { - dprintk("EIF\n"); - bstat = Ser2UTSR1; - - if (bstat & UTSR1_FRE) - dprintk("frame error\n"); - if (bstat & UTSR1_ROR) - dprintk("receive fifo overrun\n"); - if (bstat & UTSR1_PRE) - dprintk("parity error\n"); - } - - bstat = Ser2UTDR; - n++; - status = Ser2UTSR0; - } - - if (status & (UTSR0_RFS | UTSR0_RID)) { - do_gettimeofday(&curr_tv); - deltv = delta(&last_tv, &curr_tv); - do { - data = Ser2UTDR; - dprintk("%d data: %u\n", n, (unsigned int) data); - n++; - } while (status & UTSR0_RID && /* do not empty fifo in order to - * get UTSR0_RID in any case */ - Ser2UTSR1 & UTSR1_RNE); /* data ready */ - - if (status&UTSR0_RID) { - add_read_queue(0 , deltv - n * TIME_CONST); /*space*/ - add_read_queue(1, n * TIME_CONST); /*pulse*/ - n = 0; - last_tv = curr_tv; - } - } - - if (status & UTSR0_TFS) - pr_err("transmit fifo not full, shouldn't happen\n"); - - /* We must clear certain bits. */ - status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); - if (status) - Ser2UTSR0 = status; -#else unsigned long deltintrtv; unsigned long flags; int iir, lsr; @@ -698,44 +535,9 @@ static irqreturn_t sir_interrupt(int irq, void *dev_id) break; } } -#endif return IRQ_RETVAL(IRQ_HANDLED); } -#ifdef LIRC_ON_SA1100 -static void send_pulse(unsigned long length) -{ - unsigned long k, delay; - int flag; - - if (length == 0) - return; - /* - * this won't give us the carrier frequency we really want - * due to integer arithmetic, but we can accept this inaccuracy - */ - - for (k = flag = 0; k < length; k += delay, flag = !flag) { - if (flag) { - off(); - delay = space_width; - } else { - on(); - delay = pulse_width; - } - safe_udelay(delay); - } - off(); -} - -static void send_space(unsigned long length) -{ - if (length == 0) - return; - off(); - safe_udelay(length); -} -#else static void send_space(unsigned long len) { safe_udelay(len); @@ -755,31 +557,6 @@ static void send_pulse(unsigned long len) ; } } -#endif - -#ifdef CONFIG_SA1100_COLLIE -static int sa1100_irda_set_power_collie(int state) -{ - if (state) { - /* - * 0 - off - * 1 - short range, lowest power - * 2 - medium range, medium power - * 3 - maximum range, high power - */ - ucb1200_set_io_direction(TC35143_GPIO_IR_ON, - TC35143_IODIR_OUTPUT); - ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW); - udelay(100); - } else { - /* OFF */ - ucb1200_set_io_direction(TC35143_GPIO_IR_ON, - TC35143_IODIR_OUTPUT); - ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH); - } - return 0; -} -#endif static int init_hardware(void) { @@ -787,51 +564,7 @@ static int init_hardware(void) spin_lock_irqsave(&hardware_lock, flags); /* reset UART */ -#ifdef LIRC_ON_SA1100 -#ifdef CONFIG_SA1100_COLLIE - sa1100_irda_set_power_collie(3); /* power on */ -#endif - sr.hscr0 = Ser2HSCR0; - - sr.utcr0 = Ser2UTCR0; - sr.utcr1 = Ser2UTCR1; - sr.utcr2 = Ser2UTCR2; - sr.utcr3 = Ser2UTCR3; - sr.utcr4 = Ser2UTCR4; - - sr.utdr = Ser2UTDR; - sr.utsr0 = Ser2UTSR0; - sr.utsr1 = Ser2UTSR1; - - /* configure GPIO */ - /* output */ - PPDR |= PPC_TXD2; - PSDR |= PPC_TXD2; - /* set output to 0 */ - off(); - - /* Enable HP-SIR modulation, and ensure that the port is disabled. */ - Ser2UTCR3 = 0; - Ser2HSCR0 = sr.hscr0 & (~HSCR0_HSSP); - - /* clear status register to prevent unwanted interrupts */ - Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); - - /* 7N1 */ - Ser2UTCR0 = UTCR0_1StpBit|UTCR0_7BitData; - /* 115200 */ - Ser2UTCR1 = 0; - Ser2UTCR2 = 1; - /* use HPSIR, 1.6 usec pulses */ - Ser2UTCR4 = UTCR4_HPSIR|UTCR4_Z1_6us; - - /* enable receiver, receive fifo interrupt */ - Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE; - - /* clear status register to prevent unwanted interrupts */ - Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); - -#elif defined(LIRC_SIR_TEKRAM) +#if defined(LIRC_SIR_TEKRAM) /* disable FIFO */ soutp(UART_FCR, UART_FCR_CLEAR_RCVR| @@ -927,23 +660,9 @@ static void drop_hardware(void) spin_lock_irqsave(&hardware_lock, flags); -#ifdef LIRC_ON_SA1100 - Ser2UTCR3 = 0; - - Ser2UTCR0 = sr.utcr0; - Ser2UTCR1 = sr.utcr1; - Ser2UTCR2 = sr.utcr2; - Ser2UTCR4 = sr.utcr4; - Ser2UTCR3 = sr.utcr3; - - Ser2HSCR0 = sr.hscr0; -#ifdef CONFIG_SA1100_COLLIE - sa1100_irda_set_power_collie(0); /* power off */ -#endif -#else /* turn off interrupts */ outb(0, io + UART_IER); -#endif + spin_unlock_irqrestore(&hardware_lock, flags); } @@ -954,24 +673,18 @@ static int init_port(void) int retval; /* get I/O port access and IRQ line */ -#ifndef LIRC_ON_SA1100 if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { pr_err("i/o port 0x%.4x already in use.\n", io); return -EBUSY; } -#endif retval = request_irq(irq, sir_interrupt, 0, LIRC_DRIVER_NAME, NULL); if (retval < 0) { -# ifndef LIRC_ON_SA1100 release_region(io, 8); -# endif pr_err("IRQ %d already in use.\n", irq); return retval; } -#ifndef LIRC_ON_SA1100 pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq); -#endif init_timer(&timerlist); timerlist.function = sir_timeout; @@ -984,9 +697,7 @@ static void drop_port(void) { free_irq(irq, NULL); del_timer_sync(&timerlist); -#ifndef LIRC_ON_SA1100 release_region(io, 8); -#endif } #ifdef LIRC_SIR_ACTISYS_ACT200L @@ -1284,9 +995,6 @@ module_exit(lirc_sir_exit); #ifdef LIRC_SIR_TEKRAM MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210"); MODULE_AUTHOR("Christoph Bartelmus"); -#elif defined(LIRC_ON_SA1100) -MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor"); -MODULE_AUTHOR("Christoph Bartelmus"); #elif defined(LIRC_SIR_ACTISYS_ACT200L) MODULE_DESCRIPTION("LIRC driver for Actisys Act200L"); MODULE_AUTHOR("Karl Bongers"); @@ -1299,10 +1007,6 @@ MODULE_AUTHOR("Milan Pikula"); #endif MODULE_LICENSE("GPL"); -#ifdef LIRC_ON_SA1100 -module_param(irq, int, S_IRUGO); -MODULE_PARM_DESC(irq, "Interrupt (16)"); -#else module_param(io, int, S_IRUGO); MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)"); @@ -1311,7 +1015,6 @@ MODULE_PARM_DESC(irq, "Interrupt (4 or 3)"); module_param(threshold, int, S_IRUGO); MODULE_PARM_DESC(threshold, "space detection threshold (3)"); -#endif module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Enable debugging messages"); -- cgit v0.10.2 From 9e334c75642b6e5bfb956bf0e45f1d3afa271b67 Mon Sep 17 00:00:00 2001 From: James Harper Date: Sun, 8 Jun 2014 21:24:20 -0300 Subject: [media] Fix regression in some dib0700 based devices Fix regression in some dib0700 based devices. Set size_of_priv, and don't call dvb_detach unnecessarily. This resolves the oops(s) for my "Leadtek Winfast DTV Dongle (STK7700P based)" Signed-off-by: James Harper Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 91e5ebb0..ce47d3f 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -723,7 +723,6 @@ static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config); st->is_dib7000pc = 1; } else { - dvb_detach(&state->dib7000p_ops); memset(&state->dib7000p_ops, 0, sizeof(state->dib7000p_ops)); adap->fe_adap[0].fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config); } @@ -3804,6 +3803,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, + .size_of_priv = sizeof(struct dib0700_adapter_state), }, }, -- cgit v0.10.2 From 89c5ff0557f8faf15cd1461c8d91608c0e404921 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 12 Jun 2014 03:22:45 -0300 Subject: [media] rtl28xxu: add [1b80:d3b0] Sveon STV21 Added Sveon STV21 device based on Realtek RTL2832U and FC0013 tuner Signed-off-by: Sebastian Kemper Acked-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index f8e3150..5135a09 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -364,6 +364,7 @@ #define USB_PID_TVWAY_PLUS 0x0002 #define USB_PID_SVEON_STV20 0xe39d #define USB_PID_SVEON_STV20_RTL2832U 0xd39d +#define USB_PID_SVEON_STV21 0xd3b0 #define USB_PID_SVEON_STV22 0xe401 #define USB_PID_SVEON_STV22_IT9137 0xe411 #define USB_PID_AZUREWAVE_AZ6027 0x3275 diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 57ac62d..27b1e03 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1541,6 +1541,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "Peak DVB-T USB", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20_RTL2832U, &rtl2832u_props, "Sveon STV20", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV21, + &rtl2832u_props, "Sveon STV21", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV27, &rtl2832u_props, "Sveon STV27", NULL) }, -- cgit v0.10.2 From 6eb5e3399e8f45aa191ad21c0556bece8ea559f2 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 9 Jul 2014 10:21:27 -0300 Subject: [media] media: em28xx-dvb - fix em28xx_dvb_resume() to not unregister i2c and dvb em28xx_dvb_resume() unregisters i2c tuner, i2c demod, and dvb. This erroneous cleanup results in i2c tuner, i2c demod, and dvb devices unregistered and removed during resume. This error is a result of merge conflict between two patches that went into 3.15. Signed-off-by: Shuah Khan Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 96a0bdb..5f90890 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1713,7 +1713,6 @@ static int em28xx_dvb_resume(struct em28xx *dev) em28xx_info("Resuming DVB extension"); if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; - struct i2c_client *client = dvb->i2c_client_tuner; if (dvb->fe[0]) { ret = dvb_frontend_resume(dvb->fe[0]); @@ -1724,22 +1723,6 @@ static int em28xx_dvb_resume(struct em28xx *dev) ret = dvb_frontend_resume(dvb->fe[1]); em28xx_info("fe1 resume %d", ret); } - /* remove I2C tuner */ - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } - - /* remove I2C demod */ - client = dvb->i2c_client_demod; - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } - - em28xx_unregister_dvb(dvb); - kfree(dvb); - dev->dvb = NULL; } return 0; -- cgit v0.10.2 From 4db8954afa8f7cad2397cfb9312b3eaf24a05df1 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 9 Jul 2014 17:36:23 -0300 Subject: [media] media: em28xx - add error handling for KWORLD dvb_attach failures Add error hanlding when EM2870_BOARD_KWORLD_A340 dvb_attach() for fe and tuner fail in em28xx_dvb_init(). Signed-off-by: Shuah Khan Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 5f90890..2cf34ef 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1213,9 +1213,17 @@ static int em28xx_dvb_init(struct em28xx *dev) dvb->fe[0] = dvb_attach(lgdt3305_attach, &em2870_lgdt3304_dev, &dev->i2c_adap[dev->def_i2c_bus]); - if (dvb->fe[0] != NULL) - dvb_attach(tda18271_attach, dvb->fe[0], 0x60, - &dev->i2c_adap[dev->def_i2c_bus], &kworld_a340_config); + if (!dvb->fe[0]) { + result = -EINVAL; + goto out_free; + } + if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, + &dev->i2c_adap[dev->def_i2c_bus], + &kworld_a340_config)) { + dvb_frontend_detach(dvb->fe[0]); + result = -EINVAL; + goto out_free; + } break; case EM28174_BOARD_PCTV_290E: /* set default GPIO0 for LNA, used if GPIOLIB is undefined */ -- cgit v0.10.2 From b89193e0b06f44f48e3bf897a5b5cb4a7aff3359 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 9 Jul 2014 17:36:03 -0300 Subject: [media] media: em28xx - remove reset_resume interface em28xx uses resume interface as its reset_resume interface. If usb device is reset during suspend, reset_resume doesn't do the necessary initialization which leads to resume failure. Many systems don't maintain do not maintain suspend current to the USB host controllers during hibernation. Remove reset_resume to allow disconnect to be called followed by device restore sequence. Signed-off-by: Shuah Khan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 9da812b..a7e24848 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3524,7 +3524,6 @@ static struct usb_driver em28xx_usb_driver = { .disconnect = em28xx_usb_disconnect, .suspend = em28xx_usb_suspend, .resume = em28xx_usb_resume, - .reset_resume = em28xx_usb_resume, .id_table = em28xx_id_table, }; -- cgit v0.10.2 From cc5c5d20c3cb9066576043b937b35d6b669a52e0 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Fri, 11 Jul 2014 18:25:55 -0300 Subject: [media] media: em28xx - fix i2c_xfer to return -ENODEV when dev is removed In em28xx usb disconnect code path, some dvb fe and tuner drivers attempt i2c transfers from their release interfaces. When device is removed, return -ENODEV instead of attempting to transfer data over i2c. Signed-off-by: Shuah Khan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index b58d4eb..1048c1a 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -501,6 +501,12 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, int addr, rc, i; u8 reg; + /* prevent i2c xfer attempts after device is disconnected + some fe's try to do i2c writes/reads from their release + interfaces when called in disconnect path */ + if (dev->disconnected) + return -ENODEV; + rc = rt_mutex_trylock(&dev->i2c_bus_lock); if (rc < 0) return rc; -- cgit v0.10.2 From 18ed2860cb1345c1c99f62b089a15ac858f7bd48 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Sat, 12 Jul 2014 13:44:12 -0300 Subject: [media] media: dvb-core move fe exit flag from fepriv to fe for driver access Some fe drivers attempt to access the device for power control from their release routines. When release routines are called after device is disconnected, the attempts fail. fe drivers should avoid accessing the device, from their release interfaces when called from disconnect path. dvb-frontend maintains exit flag to keep track when fe device is disconnected in its private data structures. Export the flag in fe to enable drivers to check the device status from their release interfaces. Signed-off-by: Shuah Khan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 6cc2631..af88aeb 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -96,10 +96,6 @@ MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to seconds on open( * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again. */ -#define DVB_FE_NO_EXIT 0 -#define DVB_FE_NORMAL_EXIT 1 -#define DVB_FE_DEVICE_REMOVED 2 - static DEFINE_MUTEX(frontend_mutex); struct dvb_frontend_private { @@ -113,7 +109,6 @@ struct dvb_frontend_private { wait_queue_head_t wait_queue; struct task_struct *thread; unsigned long release_jiffies; - unsigned int exit; unsigned int wakeup; fe_status_t status; unsigned long tune_mode_flags; @@ -565,7 +560,7 @@ static int dvb_frontend_is_exiting(struct dvb_frontend *fe) { struct dvb_frontend_private *fepriv = fe->frontend_priv; - if (fepriv->exit != DVB_FE_NO_EXIT) + if (fe->exit != DVB_FE_NO_EXIT) return 1; if (fepriv->dvbdev->writers == 1) @@ -629,7 +624,7 @@ restart: /* got signal or quitting */ if (!down_interruptible(&fepriv->sem)) semheld = true; - fepriv->exit = DVB_FE_NORMAL_EXIT; + fe->exit = DVB_FE_NORMAL_EXIT; break; } @@ -739,9 +734,9 @@ restart: fepriv->thread = NULL; if (kthread_should_stop()) - fepriv->exit = DVB_FE_DEVICE_REMOVED; + fe->exit = DVB_FE_DEVICE_REMOVED; else - fepriv->exit = DVB_FE_NO_EXIT; + fe->exit = DVB_FE_NO_EXIT; mb(); if (semheld) @@ -756,7 +751,8 @@ static void dvb_frontend_stop(struct dvb_frontend *fe) dev_dbg(fe->dvb->device, "%s:\n", __func__); - fepriv->exit = DVB_FE_NORMAL_EXIT; + if (fe->exit != DVB_FE_DEVICE_REMOVED) + fe->exit = DVB_FE_NORMAL_EXIT; mb(); if (!fepriv->thread) @@ -826,7 +822,7 @@ static int dvb_frontend_start(struct dvb_frontend *fe) dev_dbg(fe->dvb->device, "%s:\n", __func__); if (fepriv->thread) { - if (fepriv->exit == DVB_FE_NO_EXIT) + if (fe->exit == DVB_FE_NO_EXIT) return 0; else dvb_frontend_stop (fe); @@ -838,7 +834,7 @@ static int dvb_frontend_start(struct dvb_frontend *fe) return -EINTR; fepriv->state = FESTATE_IDLE; - fepriv->exit = DVB_FE_NO_EXIT; + fe->exit = DVB_FE_NO_EXIT; fepriv->thread = NULL; mb(); @@ -1906,7 +1902,7 @@ static int dvb_frontend_ioctl(struct file *file, if (down_interruptible(&fepriv->sem)) return -ERESTARTSYS; - if (fepriv->exit != DVB_FE_NO_EXIT) { + if (fe->exit != DVB_FE_NO_EXIT) { up(&fepriv->sem); return -ENODEV; } @@ -2424,7 +2420,7 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) int ret; dev_dbg(fe->dvb->device, "%s:\n", __func__); - if (fepriv->exit == DVB_FE_DEVICE_REMOVED) + if (fe->exit == DVB_FE_DEVICE_REMOVED) return -ENODEV; if (adapter->mfe_shared) { @@ -2529,7 +2525,7 @@ static int dvb_frontend_release(struct inode *inode, struct file *file) if (dvbdev->users == -1) { wake_up(&fepriv->wait_queue); - if (fepriv->exit != DVB_FE_NO_EXIT) + if (fe->exit != DVB_FE_NO_EXIT) wake_up(&dvbdev->wait_queue); if (fe->ops.ts_bus_ctrl) fe->ops.ts_bus_ctrl(fe, 0); diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 371b6ca..625a340 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -405,6 +405,10 @@ struct dtv_frontend_properties { struct dtv_fe_stats block_count; }; +#define DVB_FE_NO_EXIT 0 +#define DVB_FE_NORMAL_EXIT 1 +#define DVB_FE_DEVICE_REMOVED 2 + struct dvb_frontend { struct dvb_frontend_ops ops; struct dvb_adapter *dvb; @@ -418,6 +422,7 @@ struct dvb_frontend { #define DVB_FRONTEND_COMPONENT_DEMOD 1 int (*callback)(void *adapter_priv, int component, int cmd, int arg); int id; + unsigned int exit; }; extern int dvb_register_frontend(struct dvb_adapter *dvb, -- cgit v0.10.2 From f68e35af99cfbefdd250e37d4d9ba778d67e253d Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Sat, 12 Jul 2014 13:44:13 -0300 Subject: [media] media: em28xx-dvb update fe exit flag to indicate device disconnect Change em28xx_dvb_fini() to set fe exit flag to DVB_FE_DEVICE_REMOVED when device is disconnected. em28xx maintains device disconnect status in em28xx device. fe drivers will be able to now check the fe exit status to avoid accessing the device, from their release interfaces when called from disconnect path. This change depends on dvb-core change that exports fe exit flag by moving it from fepriv to fe. Signed-off-by: Shuah Khan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 2cf34ef..3a3e243 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1654,10 +1654,14 @@ static int em28xx_dvb_fini(struct em28xx *dev) if (dev->disconnected) { /* We cannot tell the device to sleep * once it has been unplugged. */ - if (dvb->fe[0]) + if (dvb->fe[0]) { prevent_sleep(&dvb->fe[0]->ops); - if (dvb->fe[1]) + dvb->fe[0]->exit = DVB_FE_DEVICE_REMOVED; + } + if (dvb->fe[1]) { prevent_sleep(&dvb->fe[1]->ops); + dvb->fe[1]->exit = DVB_FE_DEVICE_REMOVED; + } } /* remove I2C tuner */ -- cgit v0.10.2 From 5b22b1a4f78d7ba29528fdc70b0c699112cff16e Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Sat, 12 Jul 2014 13:44:14 -0300 Subject: [media] media: drx39xyj driver change to check fe exit flag from release Change drx39xyj_release() to check fe exit flag to detect the device disconnect state and avoid accessing the device after it has been removed. Signed-off-by: Shuah Khan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 3795f65..7007e0b 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -12238,7 +12238,9 @@ static void drx39xxj_release(struct dvb_frontend *fe) struct drx39xxj_state *state = fe->demodulator_priv; struct drx_demod_instance *demod = state->demod; - drxj_close(demod); + /* if device is removed don't access it */ + if (fe->exit != DVB_FE_DEVICE_REMOVED) + drxj_close(demod); kfree(demod->my_ext_attr); kfree(demod->my_common_attr); -- cgit v0.10.2 From 87cd0fa8114df9fa4ac6535d16b6e3b37ebb6230 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 24 Jul 2014 13:02:14 -0300 Subject: [media] media: dvb-core add new flag exit flag value for resume Some fe drivers will have to do additional initialization in their fe ops.init interfaces when called during resume. Without the additional initialization, fe and tuner driver resume fails. A new fe exit flag value DVB_FE_DEVICE_RESUME is necessary to detect resume case. This patch adds a new define and changes dvb_frontend_resume() to set it prior to calling fe init and tuner init calls and resets it back to DVB_FE_NO_EXIT once fe and tuner init is done. Signed-off-by: Shuah Khan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index af88aeb..c2a6a0a 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -2568,12 +2568,14 @@ int dvb_frontend_resume(struct dvb_frontend *fe) dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num, fe->id); + fe->exit = DVB_FE_DEVICE_RESUME; if (fe->ops.init) ret = fe->ops.init(fe); if (fe->ops.tuner_ops.init) ret = fe->ops.tuner_ops.init(fe); + fe->exit = DVB_FE_NO_EXIT; fepriv->state = FESTATE_RETUNE; dvb_frontend_wakeup(fe); diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 625a340..d398de4 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -408,6 +408,7 @@ struct dtv_frontend_properties { #define DVB_FE_NO_EXIT 0 #define DVB_FE_NORMAL_EXIT 1 #define DVB_FE_DEVICE_REMOVED 2 +#define DVB_FE_DEVICE_RESUME 3 struct dvb_frontend { struct dvb_frontend_ops ops; -- cgit v0.10.2 From 998819d2715eb765ba3752294bd694c3b06eaa04 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 24 Jul 2014 13:02:15 -0300 Subject: [media] media: drx39xyj - add resume support drx39xyj driver lacks resume support. Add support by changing its fe ops init interface to detect the resume status by checking fe exit flag and do the necessary initialization. With this change, driver resume correctly in both dvb adapter is not in use and in use by an application cases. Signed-off-by: Shuah Khan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 7007e0b..4712399 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -11315,6 +11315,7 @@ rw_error: static int drx_ctrl_u_code(struct drx_demod_instance *demod, struct drxu_code_info *mc_info, enum drxu_code_action action); +static int drxj_set_lna_state(struct drx_demod_instance *demod, bool state); /** * \fn drxj_open() @@ -11527,6 +11528,7 @@ static int drxj_open(struct drx_demod_instance *demod) ext_attr->aud_data = drxj_default_aud_data_g; demod->my_common_attr->is_opened = true; + drxj_set_lna_state(demod, false); return 0; rw_error: common_attr->is_opened = false; @@ -11890,6 +11892,33 @@ release: return rc; } +/* caller is expeced to check if lna is supported before enabling */ +static int drxj_set_lna_state(struct drx_demod_instance *demod, bool state) +{ + struct drxuio_cfg uio_cfg; + struct drxuio_data uio_data; + int result; + + uio_cfg.uio = DRX_UIO1; + uio_cfg.mode = DRX_UIO_MODE_READWRITE; + /* Configure user-I/O #3: enable read/write */ + result = ctrl_set_uio_cfg(demod, &uio_cfg); + if (result) { + pr_err("Failed to setup LNA GPIO!\n"); + return result; + } + + uio_data.uio = DRX_UIO1; + uio_data.value = state; + result = ctrl_uio_write(demod, &uio_data); + if (result != 0) { + pr_err("Failed to %sable LNA!\n", + state ? "en" : "dis"); + return result; + } + return 0; +} + /* * The Linux DVB Driver for Micronas DRX39xx family (drx3933j) * @@ -12180,10 +12209,20 @@ static int drx39xxj_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) static int drx39xxj_init(struct dvb_frontend *fe) { - /* Bring the demod out of sleep */ - drx39xxj_set_powerstate(fe, 1); + struct drx39xxj_state *state = fe->demodulator_priv; + struct drx_demod_instance *demod = state->demod; + int rc = 0; - return 0; + if (fe->exit == DVB_FE_DEVICE_RESUME) { + /* so drxj_open() does what it needs to do */ + demod->my_common_attr->is_opened = false; + rc = drxj_open(demod); + if (rc != 0) + pr_err("drx39xxj_init(): DRX open failed rc=%d!\n", rc); + } else + drx39xxj_set_powerstate(fe, 1); + + return rc; } static int drx39xxj_set_lna(struct dvb_frontend *fe) @@ -12261,8 +12300,6 @@ struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c) struct drxj_data *demod_ext_attr = NULL; struct drx_demod_instance *demod = NULL; struct dtv_frontend_properties *p; - struct drxuio_cfg uio_cfg; - struct drxuio_data uio_data; int result; /* allocate memory for the internal state */ @@ -12313,24 +12350,6 @@ struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c) goto error; } - /* Turn off the LNA */ - uio_cfg.uio = DRX_UIO1; - uio_cfg.mode = DRX_UIO_MODE_READWRITE; - /* Configure user-I/O #3: enable read/write */ - result = ctrl_set_uio_cfg(demod, &uio_cfg); - if (result) { - pr_err("Failed to setup LNA GPIO!\n"); - goto error; - } - - uio_data.uio = DRX_UIO1; - uio_data.value = false; - result = ctrl_uio_write(demod, &uio_data); - if (result != 0) { - pr_err("Failed to disable LNA!\n"); - goto error; - } - /* create dvb_frontend */ memcpy(&state->frontend.ops, &drx39xxj_ops, sizeof(struct dvb_frontend_ops)); -- cgit v0.10.2 From 30741871117e2be45a4ae26f0507004e5494f75c Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 23 Jul 2014 12:11:03 -0300 Subject: [media] media: drx39xyj - fix to return actual error codes instead of -EIO Several functions ignore the return values in error legs and always return -EIO. This makes it hard to debug and take proper action in calling routines. Signed-off-by: Shuah Khan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 4712399..bcf0a23 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -2159,7 +2159,7 @@ int drxj_dap_atomic_read_write_block(struct i2c_device_addr *dev_addr, return 0; rw_error: - return -EIO; + return rc; } @@ -2252,7 +2252,7 @@ static int hi_cfg_command(const struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /** @@ -2363,7 +2363,7 @@ hi_command(struct i2c_device_addr *dev_addr, const struct drxj_hi_cmd *cmd, u16 /* if ( powerdown_cmd == true ) */ return 0; rw_error: - return -EIO; + return rc; } /** @@ -2434,7 +2434,7 @@ static int init_hi(const struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -2650,7 +2650,7 @@ static int get_device_capabilities(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /** @@ -3338,7 +3338,7 @@ ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_o return 0; rw_error: - return -EIO; + return rc; } /*----------------------------------------------------------------------------*/ @@ -3421,7 +3421,7 @@ static int set_mpegtei_handling(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /*----------------------------------------------------------------------------*/ @@ -3464,7 +3464,7 @@ static int bit_reverse_mpeg_output(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /*----------------------------------------------------------------------------*/ @@ -3508,7 +3508,7 @@ static int set_mpeg_start_width(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /*----------------------------------------------------------------------------*/ @@ -3652,7 +3652,7 @@ static int ctrl_set_uio_cfg(struct drx_demod_instance *demod, struct drxuio_cfg return 0; rw_error: - return -EIO; + return rc; } /** @@ -3854,7 +3854,7 @@ ctrl_uio_write(struct drx_demod_instance *demod, struct drxuio_data *uio_data) return 0; rw_error: - return -EIO; + return rc; } /*---------------------------------------------------------------------------*/ @@ -3969,7 +3969,7 @@ static int smart_ant_init(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } static int scu_command(struct i2c_device_addr *dev_addr, struct drxjscu_cmd *cmd) @@ -4109,7 +4109,7 @@ static int scu_command(struct i2c_device_addr *dev_addr, struct drxjscu_cmd *cmd return 0; rw_error: - return -EIO; + return rc; } /** @@ -4178,7 +4178,7 @@ int drxj_dap_scu_atomic_read_write_block(struct i2c_device_addr *dev_addr, u32 a return 0; rw_error: - return -EIO; + return rc; } @@ -4290,7 +4290,7 @@ static int adc_sync_measurement(struct drx_demod_instance *demod, u16 *count) return 0; rw_error: - return -EIO; + return rc; } /** @@ -4349,7 +4349,7 @@ static int adc_synchronization(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -4734,7 +4734,7 @@ static int init_agc(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /** @@ -4831,7 +4831,7 @@ set_frequency(struct drx_demod_instance *demod, return 0; rw_error: - return -EIO; + return rc; } /** @@ -4879,7 +4879,7 @@ static int get_acc_pkt_err(struct drx_demod_instance *demod, u16 *packet_err) return 0; rw_error: - return -EIO; + return rc; } #endif @@ -5097,7 +5097,7 @@ set_agc_rf(struct drx_demod_instance *demod, struct drxj_cfg_agc *agc_settings, return 0; rw_error: - return -EIO; + return rc; } /** @@ -5326,7 +5326,7 @@ set_agc_if(struct drx_demod_instance *demod, struct drxj_cfg_agc *agc_settings, return 0; rw_error: - return -EIO; + return rc; } /** @@ -5362,7 +5362,7 @@ static int set_iqm_af(struct drx_demod_instance *demod, bool active) return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -5470,7 +5470,7 @@ static int power_down_vsb(struct drx_demod_instance *demod, bool primary) return 0; rw_error: - return -EIO; + return rc; } /** @@ -5686,7 +5686,7 @@ static int set_vsb_leak_n_gain(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /** @@ -6192,7 +6192,7 @@ static int set_vsb(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /** @@ -6231,7 +6231,7 @@ static int get_vsb_post_rs_pck_err(struct i2c_device_addr *dev_addr, return 0; rw_error: - return -EIO; + return rc; } /** @@ -6276,7 +6276,7 @@ static int get_vs_bpost_viterbi_ber(struct i2c_device_addr *dev_addr, return 0; rw_error: - return -EIO; + return rc; } /** @@ -6321,7 +6321,7 @@ static int get_vsbmer(struct i2c_device_addr *dev_addr, u16 *mer) return 0; rw_error: - return -EIO; + return rc; } @@ -6434,7 +6434,7 @@ static int power_down_qam(struct drx_demod_instance *demod, bool primary) return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -6646,7 +6646,7 @@ set_qam_measurement(struct drx_demod_instance *demod, return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -6881,7 +6881,7 @@ static int set_qam16(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -7116,7 +7116,7 @@ static int set_qam32(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -7351,7 +7351,7 @@ static int set_qam64(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -7586,7 +7586,7 @@ static int set_qam128(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -7821,7 +7821,7 @@ static int set_qam256(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -8650,7 +8650,7 @@ set_qam(struct drx_demod_instance *demod, return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -8831,7 +8831,7 @@ static int qam_flip_spec(struct drx_demod_instance *demod, struct drx_channel *c return 0; rw_error: - return -EIO; + return rc; } @@ -8984,7 +8984,7 @@ qam64auto(struct drx_demod_instance *demod, return 0; rw_error: - return -EIO; + return rc; } /** @@ -9068,7 +9068,7 @@ qam256auto(struct drx_demod_instance *demod, return 0; rw_error: - return -EIO; + return rc; } /** @@ -9273,7 +9273,7 @@ rw_error: /* restore starting value */ if (auto_flag) channel->constellation = DRX_CONSTELLATION_AUTO; - return -EIO; + return rc; } /*============================================================================*/ @@ -9344,7 +9344,7 @@ get_qamrs_err_count(struct i2c_device_addr *dev_addr, return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -9425,8 +9425,8 @@ static int get_sig_strength(struct drx_demod_instance *demod, u16 *sig_strength) *sig_strength = 0; return 0; - rw_error: - return -EIO; +rw_error: + return rc; } /** @@ -9643,7 +9643,7 @@ rw_error: p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - return -EIO; + return rc; } #endif /* #ifndef DRXJ_VSB_ONLY */ @@ -9810,7 +9810,7 @@ power_down_atv(struct drx_demod_instance *demod, enum drx_standard standard, boo return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -9840,7 +9840,7 @@ static int power_down_aud(struct drx_demod_instance *demod) return 0; rw_error: - return -EIO; + return rc; } /** @@ -9874,7 +9874,7 @@ static int set_orx_nsu_aox(struct drx_demod_instance *demod, bool active) return 0; rw_error: - return -EIO; + return rc; } /** @@ -10398,7 +10398,7 @@ static int ctrl_set_oob(struct drx_demod_instance *demod, struct drxoob *oob_par return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -10638,7 +10638,7 @@ ctrl_set_channel(struct drx_demod_instance *demod, struct drx_channel *channel) return 0; rw_error: - return -EIO; + return rc; } /*============================================================================= @@ -10756,7 +10756,7 @@ ctrl_sig_quality(struct drx_demod_instance *demod, return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -10844,7 +10844,7 @@ ctrl_lock_status(struct drx_demod_instance *demod, enum drx_lock_status *lock_st return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -10941,7 +10941,7 @@ ctrl_set_standard(struct drx_demod_instance *demod, enum drx_standard *standard) rw_error: /* Don't know what the standard is now ... try again */ ext_attr->standard = DRX_STANDARD_UNKNOWN; - return -EIO; + return rc; } /*============================================================================*/ @@ -11222,7 +11222,7 @@ ctrl_set_cfg_pre_saw(struct drx_demod_instance *demod, struct drxj_cfg_pre_saw * return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -11303,7 +11303,7 @@ ctrl_set_cfg_afe_gain(struct drx_demod_instance *demod, struct drxj_cfg_afe_gain return 0; rw_error: - return -EIO; + return rc; } /*============================================================================*/ @@ -11532,7 +11532,7 @@ static int drxj_open(struct drx_demod_instance *demod) return 0; rw_error: common_attr->is_opened = false; - return -EIO; + return rc; } /*============================================================================*/ @@ -11580,7 +11580,7 @@ static int drxj_close(struct drx_demod_instance *demod) rw_error: DRX_ATTR_ISOPENED(demod) = false; - return -EIO; + return rc; } /* -- cgit v0.10.2 From b601fe5688ae285693c64e833003c14acb38378a Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 24 Jul 2014 17:29:28 -0300 Subject: [media] media: drx39xyj - use drxj_set_lna_state() and remove duplicate LNA code drx39xxj_set_lna() and drx39xxj_set_frontend() set LNA. Instead of duplicating LNA configure code, change to use drxj_set_lna_state() which sets LNA to the caller requested state (on or off). Signed-off-by: Shuah Khan Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index bcf0a23..200554d 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -12154,13 +12154,7 @@ static int drx39xxj_set_frontend(struct dvb_frontend *fe) return -EINVAL; } /* Just for giggles, let's shut off the LNA again.... */ - uio_data.uio = DRX_UIO1; - uio_data.value = false; - result = ctrl_uio_write(demod, &uio_data); - if (result != 0) { - pr_err("Failed to disable LNA!\n"); - return 0; - } + drxj_set_lna_state(demod, false); /* After set_frontend, except for strength, stats aren't available */ p->strength.stat[0].scale = FE_SCALE_RELATIVE; @@ -12243,26 +12237,7 @@ static int drx39xxj_set_lna(struct dvb_frontend *fe) } } - /* Turn off the LNA */ - uio_cfg.uio = DRX_UIO1; - uio_cfg.mode = DRX_UIO_MODE_READWRITE; - /* Configure user-I/O #3: enable read/write */ - result = ctrl_set_uio_cfg(demod, &uio_cfg); - if (result) { - pr_err("Failed to setup LNA GPIO!\n"); - return result; - } - - uio_data.uio = DRX_UIO1; - uio_data.value = c->lna; - result = ctrl_uio_write(demod, &uio_data); - if (result != 0) { - pr_err("Failed to %sable LNA!\n", - c->lna ? "en" : "dis"); - return result; - } - - return 0; + return drxj_set_lna_state(demod, c->lna); } static int drx39xxj_get_tune_settings(struct dvb_frontend *fe, -- cgit v0.10.2 From 7b4eeed174b71c325705ff8c53f333bc79d0ee7a Mon Sep 17 00:00:00 2001 From: James Harper Date: Thu, 12 Jun 2014 06:53:38 -0300 Subject: [media] vmalloc_sg: make sure all pages in vmalloc area are really DMA-ready Patch originally written by Konrad. Rebased on current linux media tree. Under Xen, vmalloc_32() isn't guaranteed to return pages which are really under 4G in machine physical addresses (only in virtual pseudo-physical addresses). To work around this, implement a vmalloc variant which allocates each page with dma_alloc_coherent() to guarantee that each page is suitable for the device in question. Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: James Harper Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index 828e7c1..3c8cc02 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -211,13 +211,36 @@ EXPORT_SYMBOL_GPL(videobuf_dma_init_user); int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, int nr_pages) { + int i; + dprintk(1, "init kernel [%d pages]\n", nr_pages); dma->direction = direction; - dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); + dma->vaddr_pages = kcalloc(nr_pages, sizeof(*dma->vaddr_pages), + GFP_KERNEL); + if (!dma->vaddr_pages) + return -ENOMEM; + + dma->dma_addr = kcalloc(nr_pages, sizeof(*dma->dma_addr), GFP_KERNEL); + if (!dma->dma_addr) { + kfree(dma->vaddr_pages); + return -ENOMEM; + } + for (i = 0; i < nr_pages; i++) { + void *addr; + + addr = dma_alloc_coherent(dma->dev, PAGE_SIZE, + &(dma->dma_addr[i]), GFP_KERNEL); + if (addr == NULL) + goto out_free_pages; + + dma->vaddr_pages[i] = virt_to_page(addr); + } + dma->vaddr = vmap(dma->vaddr_pages, nr_pages, VM_MAP | VM_IOREMAP, + PAGE_KERNEL); if (NULL == dma->vaddr) { dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); - return -ENOMEM; + goto out_free_pages; } dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", @@ -228,6 +251,19 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, dma->nr_pages = nr_pages; return 0; +out_free_pages: + while (i > 0) { + void *addr = page_address(dma->vaddr_pages[i]); + dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]); + i--; + } + kfree(dma->dma_addr); + dma->dma_addr = NULL; + kfree(dma->vaddr_pages); + dma->vaddr_pages = NULL; + + return -ENOMEM; + } EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); @@ -322,8 +358,21 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) dma->pages = NULL; } - vfree(dma->vaddr); - dma->vaddr = NULL; + if (dma->dma_addr) { + for (i = 0; i < dma->nr_pages; i++) { + void *addr; + + addr = page_address(dma->vaddr_pages[i]); + dma_free_coherent(dma->dev, PAGE_SIZE, addr, + dma->dma_addr[i]); + } + kfree(dma->dma_addr); + dma->dma_addr = NULL; + kfree(dma->vaddr_pages); + dma->vaddr_pages = NULL; + vunmap(dma->vaddr); + dma->vaddr = NULL; + } if (dma->bus_addr) dma->bus_addr = 0; @@ -461,6 +510,11 @@ static int __videobuf_iolock(struct videobuf_queue *q, MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); + if (!mem->dma.dev) + mem->dma.dev = q->dev; + else + WARN_ON(mem->dma.dev != q->dev); + switch (vb->memory) { case V4L2_MEMORY_MMAP: case V4L2_MEMORY_USERPTR: diff --git a/include/media/videobuf-dma-sg.h b/include/media/videobuf-dma-sg.h index d8fb601..fb6fd4d8 100644 --- a/include/media/videobuf-dma-sg.h +++ b/include/media/videobuf-dma-sg.h @@ -53,6 +53,9 @@ struct videobuf_dmabuf { /* for kernel buffers */ void *vaddr; + struct page **vaddr_pages; + dma_addr_t *dma_addr; + struct device *dev; /* for overlay buffers (pci-pci dma) */ dma_addr_t bus_addr; -- cgit v0.10.2 From 46b21bbaa8a8a9d0c83afd7a6035a9d6c20064f1 Mon Sep 17 00:00:00 2001 From: James Harper Date: Thu, 12 Jun 2014 07:12:24 -0300 Subject: [media] Add support for DViCO FusionHDTV DVB-T Dual Express2 DViCO FusionHDTV DVB-T Dual Express2 is cx23885 + dib7070 [m.chehab@samsung.com: fix conflicts and make checkpatch happy] Signed-off-by: James Harper Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 9723067..1d1550a 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -650,6 +650,11 @@ struct cx23885_board cx23885_boards[] = { .amux = CX25840_AUDIO6, } }, }, + [CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2] = { + .name = "DViCO FusionHDTV DVB-T Dual Express2", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, [CX23885_BOARD_HAUPPAUGE_IMPACTVCBE] = { .name = "Hauppauge ImpactVCB-e", .tuner_type = TUNER_ABSENT, @@ -920,6 +925,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0070, .subdevice = 0x7133, .card = CX23885_BOARD_HAUPPAUGE_IMPACTVCBE, + }, { + .subvendor = 0x18ac, + .subdevice = 0xdb98, + .card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1163,6 +1172,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg) break; case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2: /* Two identical tuners on two different i2c buses, * we need to reset the correct gpio. */ if (port->nr == 1) @@ -1306,6 +1316,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x000f000f); break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2: /* GPIO-0 portb xc3028 reset */ /* GPIO-1 portb zl10353 reset */ /* GPIO-2 portc xc3028 reset */ @@ -1611,6 +1622,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg); break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2: request_module("ir-kbd-i2c"); break; } @@ -1747,6 +1759,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) break; case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index d037459..9135260 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -44,6 +44,7 @@ #include "tuner-xc2028.h" #include "tuner-simple.h" #include "dib7000p.h" +#include "dib0070.h" #include "dibx000_common.h" #include "zl10353.h" #include "stv0900.h" @@ -746,6 +747,106 @@ static int netup_altera_fpga_rw(void *device, int flag, int data, int read) return 0; }; +static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) +{ + struct dib7000p_ops *dib7000p_ops = fe->sec_priv; + + return dib7000p_ops->set_gpio(fe, 8, 0, !onoff); +} + +static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff) +{ + return 0; +} + +static struct dib0070_config dib7070p_dib0070_config = { + .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, + .reset = dib7070_tuner_reset, + .sleep = dib7070_tuner_sleep, + .clock_khz = 12000, + .freq_offset_khz_vhf = 950, + .freq_offset_khz_vhf = 550, + /* .flip_chip = 1, */ +}; + +/* DIB7070 generic */ +static struct dibx000_agc_config dib7070_agc_config = { + .band_caps = BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, + + /* + * P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, + * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, + * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 + */ + .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | + (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), + .inv_gain = 600, + .time_stabiliz = 10, + .alpha_level = 0, + .thlock = 118, + .wbd_inv = 0, + .wbd_ref = 3530, + .wbd_sel = 1, + .wbd_alpha = 5, + .agc1_max = 65535, + .agc1_min = 0, + .agc2_max = 65535, + .agc2_min = 0, + .agc1_pt1 = 0, + .agc1_pt2 = 40, + .agc1_pt3 = 183, + .agc1_slope1 = 206, + .agc1_slope2 = 255, + .agc2_pt1 = 72, + .agc2_pt2 = 152, + .agc2_slope1 = 88, + .agc2_slope2 = 90, + .alpha_mant = 17, + .alpha_exp = 27, + .beta_mant = 23, + .beta_exp = 51, + .perform_agc_softsplit = 0, +}; + +static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = { + .internal = 60000, + .sampling = 15000, + .pll_prediv = 1, + .pll_ratio = 20, + .pll_range = 3, + .pll_reset = 1, + .pll_bypass = 0, + .enable_refdiv = 0, + .bypclk_div = 0, + .IO_CLK_en_core = 1, + .ADClkSrc = 1, + .modulo = 2, + /* refsel, sel, freq_15k */ + .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0), + .ifreq = (0 << 25) | 0, + .timf = 20452225, + .xtal_hz = 12000000, +}; + +static struct dib7000p_config dib7070p_dib7000p_config = { + /* .output_mode = OUTMODE_MPEG2_FIFO, */ + .output_mode = OUTMODE_MPEG2_SERIAL, + /* .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, */ + .output_mpeg2_in_188_bytes = 1, + + .agc_config_count = 1, + .agc = &dib7070_agc_config, + .bw = &dib7070_bw_config_12_mhz, + .tuner_is_baseband = 1, + .spur_protect = 1, + + .gpio_dir = 0xfcef, /* DIB7000P_GPIO_DEFAULT_DIRECTIONS, */ + .gpio_val = 0x0110, /* DIB7000P_GPIO_DEFAULT_VALUES, */ + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .hostbus_diversity = 1, +}; + static int dvb_register(struct cx23885_tsport *port) { struct dib7000p_ops dib7000p_ops; @@ -993,6 +1094,30 @@ static int dvb_register(struct cx23885_tsport *port) } break; } + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2: { + i2c_bus = &dev->i2c_bus[port->nr - 1]; + /* cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); */ + /* cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); */ + + if (!dvb_attach(dib7000p_attach, &dib7000p_ops)) + return -ENODEV; + + if (dib7000p_ops.i2c_enumeration(&i2c_bus->i2c_adap, 1, 0x12, &dib7070p_dib7000p_config) < 0) { + printk(KERN_WARNING "Unable to enumerate dib7000p\n"); + return -ENODEV; + } + fe0->dvb.frontend = dib7000p_ops.init(&i2c_bus->i2c_adap, 0x80, &dib7070p_dib7000p_config); + if (fe0->dvb.frontend != NULL) { + struct i2c_adapter *tun_i2c; + + fe0->dvb.frontend->sec_priv = kmalloc(sizeof(dib7000p_ops), GFP_KERNEL); + memcpy(fe0->dvb.frontend->sec_priv, &dib7000p_ops, sizeof(dib7000p_ops)); + tun_i2c = dib7000p_ops.get_i2c_master(fe0->dvb.frontend, DIBX000_I2C_INTERFACE_TUNER, 1); + if (!dvb_attach(dib0070_attach, fe0->dvb.frontend, tun_i2c, &dib7070p_dib0070_config)) + return -ENODEV; + } + break; + } case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 6a4b20e..0e086c0 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -97,6 +97,7 @@ #define CX23885_BOARD_TBS_6980 41 #define CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200 42 #define CX23885_BOARD_HAUPPAUGE_IMPACTVCBE 43 +#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2 44 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 -- cgit v0.10.2 From a39e2866e5912a6da9d6261a3c004969fe657194 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 26 Jul 2014 11:49:44 -0300 Subject: [media] cx23885 now needs to select dib0070 Due to DViCO FusionHDTV DVB-T Dual Express2, we also need to autoselect this tuner. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index d1dcb1d..5158133 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -37,6 +37,7 @@ config VIDEO_CX23885 select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Conexant 23885 based TV cards. -- cgit v0.10.2 From 0444bf957fa6bc051e0c3b13c82011061f55fbf5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 26 Jul 2014 11:52:25 -0300 Subject: [media] update cx23885 and em28xx cardlists A few new devices were added. Update the lists accordingly. Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 index fc009d0..a74eecc 100644 --- a/Documentation/video4linux/CARDLIST.cx23885 +++ b/Documentation/video4linux/CARDLIST.cx23885 @@ -41,3 +41,5 @@ 40 -> TurboSight TBS 6981 [6981:8888] 41 -> TurboSight TBS 6980 [6980:8888] 42 -> Leadtek Winfast PxPVR2200 [107d:6f21] + 43 -> Hauppauge ImpactVCB-e [0070:7133] + 44 -> DViCO FusionHDTV DVB-T Dual Express2 [18ac:db98] diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 5a3ddcd3..bc3351b 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -77,7 +77,7 @@ 76 -> KWorld PlusTV 340U or UB435-Q (ATSC) (em2870) [1b80:a340] 77 -> EM2874 Leadership ISDBT (em2874) 78 -> PCTV nanoStick T2 290e (em28174) - 79 -> Terratec Cinergy H5 (em2884) [0ccd:10a2,0ccd:10ad,0ccd:10b6] + 79 -> Terratec Cinergy H5 (em2884) [eb1a:2885,0ccd:10a2,0ccd:10ad,0ccd:10b6] 80 -> PCTV DVB-S2 Stick (460e) (em28174) 81 -> Hauppauge WinTV HVR 930C (em2884) [2040:1605] 82 -> Terratec Cinergy HTC Stick (em2884) [0ccd:00b2] -- cgit v0.10.2 From b862b3736ebb11b90e0709df3d3a3353d4a952c0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 26 Jul 2014 11:53:44 -0300 Subject: [media] cx23885-dvb: remove previously overriden value drivers/media/pci/cx23885/cx23885-dvb.c:768:2: warning: initialized field overwritten [-Woverride-init] .freq_offset_khz_vhf = 550, ^ drivers/media/pci/cx23885/cx23885-dvb.c:768:2: warning: (near initialization for 'dib7070p_dib0070_config.freq_offset_khz_vhf') [-Woverride-init] Cc: James Harper Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 9135260..6968706 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -764,7 +764,6 @@ static struct dib0070_config dib7070p_dib0070_config = { .reset = dib7070_tuner_reset, .sleep = dib7070_tuner_sleep, .clock_khz = 12000, - .freq_offset_khz_vhf = 950, .freq_offset_khz_vhf = 550, /* .flip_chip = 1, */ }; -- cgit v0.10.2 From 60ecb71a72e93384f6b0f3c5a18a2d1e73f48036 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 26 Jul 2014 11:57:09 -0300 Subject: [media] remove some new warnings on drxj changeset b601fe5688ae did some cleanup, but didn't remove some now unused vars: drivers/media/dvb-frontends/drx39xyj/drxj.c: In function 'drx39xxj_set_frontend': drivers/media/dvb-frontends/drx39xyj/drxj.c:12072:21: warning: unused variable 'uio_data' [-Wunused-variable] drivers/media/dvb-frontends/drx39xyj/drxj.c: In function 'drx39xxj_set_lna': drivers/media/dvb-frontends/drx39xyj/drxj.c:12230:21: warning: unused variable 'uio_data' [-Wunused-variable] drivers/media/dvb-frontends/drx39xyj/drxj.c:12229:20: warning: unused variable 'uio_cfg' [-Wunused-variable] drivers/media/dvb-frontends/drx39xyj/drxj.c:12224:6: warning: unused variable 'result' [-Wunused-variable] Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 200554d..7ca7a21 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -12069,7 +12069,6 @@ static int drx39xxj_set_frontend(struct dvb_frontend *fe) enum drx_standard standard = DRX_STANDARD_8VSB; struct drx_channel channel; int result; - struct drxuio_data uio_data; static const struct drx_channel def_channel = { /* frequency */ 0, /* bandwidth */ DRX_BANDWIDTH_6MHZ, @@ -12221,13 +12220,10 @@ static int drx39xxj_init(struct dvb_frontend *fe) static int drx39xxj_set_lna(struct dvb_frontend *fe) { - int result; struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct drx39xxj_state *state = fe->demodulator_priv; struct drx_demod_instance *demod = state->demod; struct drxj_data *ext_attr = demod->my_ext_attr; - struct drxuio_cfg uio_cfg; - struct drxuio_data uio_data; if (c->lna) { if (!ext_attr->has_lna) { -- cgit v0.10.2 From 3ab25123373270152a9fae98e3c48ef1b2a878c0 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 12 Jun 2014 20:12:24 -0300 Subject: [media] af9035: override tuner for AVerMedia A835B devices Tuner ID set into EEPROM is wrong, which causes driver to select wrong tuner profile. That leads device non-working. Fix issue by overriding known bad tuner IDs with suitable default value. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 221af2b..75ec1c6 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -799,6 +799,25 @@ static int af9035_read_config(struct dvb_usb_device *d) addr += 0x10; /* shift for the 2nd tuner params */ } + /* + * These AVerMedia devices has a bad EEPROM content :-( + * Override some wrong values here. + */ + if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_AVERMEDIA) { + switch (le16_to_cpu(d->udev->descriptor.idProduct)) { + case USB_PID_AVERMEDIA_A835B_1835: + case USB_PID_AVERMEDIA_A835B_2835: + case USB_PID_AVERMEDIA_A835B_3835: + dev_info(&d->udev->dev, + "%s: overriding tuner from %02x to %02x\n", + KBUILD_MODNAME, state->af9033_config[0].tuner, + AF9033_TUNER_IT9135_60); + + state->af9033_config[0].tuner = AF9033_TUNER_IT9135_60; + break; + } + } + skip_eeprom: /* get demod clock */ ret = af9035_rd_reg(d, 0x00d800, &tmp); -- cgit v0.10.2 From aecfa5af1e2e282404fc56285d494701ed040a5d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 18 Jun 2014 19:31:27 -0300 Subject: [media] v4l: omap4iss: configuration using uninitialized variable Variable reg is not initialized. Random values are written to OMAP4 ISS registers if !ctx->eof_enabled. Cc: Laurent Pinchart Signed-off-by: Heinrich Schuchardt Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index bf8a657..9ae4871 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -317,7 +317,7 @@ static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable) static void csi2_ctx_config(struct iss_csi2_device *csi2, struct iss_csi2_ctx_cfg *ctx) { - u32 reg; + u32 reg = 0; /* Set up CSI2_CTx_CTRL1 */ if (ctx->eof_enabled) -- cgit v0.10.2 From 40feb52fb6e4fe6fcdbfb533c5c811d56f9bd98c Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 18 Jun 2014 18:28:10 -0300 Subject: [media] media: saa7134: remove if based on uninitialized variable Variable b is not initialized. Only with a small chance it has random value 0xFF. Remove if statement based on this value. Signed-off-by: Heinrich Schuchardt Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 887429b..dc3d651 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -133,10 +133,6 @@ static int get_key_flydvb_trio(struct IR_i2c *ir, enum rc_type *protocol, if (0x40000 & ~gpio) return 0; /* No button press */ - /* No button press - only before first key pressed */ - if (b == 0xFF) - return 0; - /* poll IR chip */ /* weak up the IR chip */ b = 0; -- cgit v0.10.2 From 0ee4c2ac1858a60ea2458347f12371437f922400 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sat, 28 Jun 2014 07:57:39 -0300 Subject: [media] drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c: remove unnecessary null test before usb_free_urb Fix checkpatch warning: WARNING: usb_free_urb(NULL) is safe this check is probably not required Signed-off-by: Fabian Frederick Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c index f8a60c1..f166ffc 100644 --- a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c @@ -791,8 +791,7 @@ static void ttusb_free_iso_urbs(struct ttusb *ttusb) int i; for (i = 0; i < ISO_BUF_COUNT; i++) - if (ttusb->iso_urb[i]) - usb_free_urb(ttusb->iso_urb[i]); + usb_free_urb(ttusb->iso_urb[i]); pci_free_consistent(NULL, ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF * -- cgit v0.10.2 From 93961435d07df8f07732890f96fd953a8953be2d Mon Sep 17 00:00:00 2001 From: Christopher Reimer Date: Mon, 30 Jun 2014 10:44:00 -0300 Subject: [media] ddbridge: Add IDs for several newer Digital Devices cards This patch adds the necessary IDs for the following dvb cards: Digital Devices Octopus Mini Digital Devices Cine S2 V6.5 Digital Devices DVBCT V6.1 Digital Devices Octopus V3 Mystique SaTiX-S2 V3 All these changes are taken from the official driver package by Digital Devices. http://download.digital-devices.de/download/linux/ [m.chehab@samsung.com: Fixed whitespace mangling] Signed-off-by: Christopher Reimer Tested-by: "D. Herrendoerfer" Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index fb52bda8..da8f848 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1663,11 +1663,40 @@ static struct ddb_info ddb_octopus_le = { .port_num = 2, }; +static struct ddb_info ddb_octopus_mini = { + .type = DDB_OCTOPUS, + .name = "Digital Devices Octopus Mini", + .port_num = 4, +}; + static struct ddb_info ddb_v6 = { .type = DDB_OCTOPUS, .name = "Digital Devices Cine S2 V6 DVB adapter", .port_num = 3, }; +static struct ddb_info ddb_v6_5 = { + .type = DDB_OCTOPUS, + .name = "Digital Devices Cine S2 V6.5 DVB adapter", + .port_num = 4, +}; + +static struct ddb_info ddb_dvbct = { + .type = DDB_OCTOPUS, + .name = "Digital Devices DVBCT V6.1 DVB adapter", + .port_num = 3, +}; + +static struct ddb_info ddb_satixS2v3 = { + .type = DDB_OCTOPUS, + .name = "Mystique SaTiX-S2 V3 DVB adapter", + .port_num = 3, +}; + +static struct ddb_info ddb_octopusv3 = { + .type = DDB_OCTOPUS, + .name = "Digital Devices Octopus V3 DVB adapter", + .port_num = 4, +}; #define DDVID 0xdd01 /* Digital Devices Vendor ID */ @@ -1680,8 +1709,12 @@ static const struct pci_device_id ddb_id_tbl[] = { DDB_ID(DDVID, 0x0002, DDVID, 0x0001, ddb_octopus), DDB_ID(DDVID, 0x0003, DDVID, 0x0001, ddb_octopus), DDB_ID(DDVID, 0x0003, DDVID, 0x0002, ddb_octopus_le), - DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus), + DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus_mini), DDB_ID(DDVID, 0x0003, DDVID, 0x0020, ddb_v6), + DDB_ID(DDVID, 0x0003, DDVID, 0x0021, ddb_v6_5), + DDB_ID(DDVID, 0x0003, DDVID, 0x0030, ddb_dvbct), + DDB_ID(DDVID, 0x0003, DDVID, 0xdb03, ddb_satixS2v3), + DDB_ID(DDVID, 0x0005, DDVID, 0x0004, ddb_octopusv3), /* in case sub-ids got deleted in flash */ DDB_ID(DDVID, 0x0003, PCI_ANY_ID, PCI_ANY_ID, ddb_none), {0} -- cgit v0.10.2 From b799455ef3349feaaae585b0c07d5868d5cb65a4 Mon Sep 17 00:00:00 2001 From: Raphael Poggi Date: Tue, 1 Jul 2014 04:12:33 -0300 Subject: [media] staging: lirc: fix checkpath errors: blank lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fix checkpath "WARNING: Missing a blank line after declarations" Signed-off-by: Raphaël Poggi Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index a5b62ee..f8c3375 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -189,6 +189,7 @@ MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)"); static void free_imon_context(struct imon_context *context) { struct device *dev = context->driver->dev; + usb_free_urb(context->tx_urb); usb_free_urb(context->rx_urb); lirc_buffer_free(context->driver->rbuf); @@ -656,6 +657,7 @@ static void imon_incoming_packet(struct imon_context *context, mask = 0x80; for (bit = 0; bit < 8; ++bit) { int curr_bit = !(buf[octet] & mask); + if (curr_bit != context->rx.prev_bit) { if (context->rx.count) { submit_data(context); @@ -775,6 +777,7 @@ static int imon_probe(struct usb_interface *interface, struct usb_endpoint_descriptor *ep; int ep_dir; int ep_type; + ep = &iface_desc->endpoint[i].desc; ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK; ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; -- cgit v0.10.2 From 280373533f5b04372ec99dee0459b3bf70ea604f Mon Sep 17 00:00:00 2001 From: Raphael Poggi Date: Tue, 1 Jul 2014 04:12:34 -0300 Subject: [media] staging: lirc: remove return void function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fix checkpath "WARNING: void function return statements are not generally useful". The removed return were useless in that case. Signed-off-by: Raphaël Poggi Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index f8c3375..96c76b3 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -482,8 +482,6 @@ static void usb_tx_callback(struct urb *urb) /* notify waiters that write has finished */ atomic_set(&context->tx.busy, 0); complete(&context->tx.finished); - - return; } /** @@ -548,7 +546,6 @@ static void ir_close(void *data) } mutex_unlock(&context->ctx_lock); - return; } /** @@ -573,7 +570,6 @@ static void submit_data(struct imon_context *context) lirc_buffer_write(context->driver->rbuf, buf); wake_up(&context->driver->rbuf->wait_poll); - return; } static inline int tv2int(const struct timeval *a, const struct timeval *b) @@ -709,8 +705,6 @@ static void usb_rx_callback(struct urb *urb) } usb_submit_urb(context->rx_urb, GFP_ATOMIC); - - return; } /** -- cgit v0.10.2 From 56a65a1eb0151c60f4fd059e8e47cb4c98fec871 Mon Sep 17 00:00:00 2001 From: Himangi Saraogi Date: Thu, 3 Jul 2014 16:38:40 -0300 Subject: [media] staging: lirc: Introduce the use of managed interfaces This patch introduces the use of managed interfaces like devm_request_mem_region and devm_request_irq and does away with the calls to free the allocated memory in the probe and remove functions. The remove function is no longer required and is removed completely. Signed-off-by: Himangi Saraogi Acked-by: Julia Lawall Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c index efe561c..bae0d46 100644 --- a/drivers/staging/media/lirc/lirc_serial.c +++ b/drivers/staging/media/lirc/lirc_serial.c @@ -782,7 +782,7 @@ static int lirc_serial_probe(struct platform_device *dev) { int i, nlow, nhigh, result; - result = request_irq(irq, lirc_irq_handler, + result = devm_request_irq(&dev->dev, irq, lirc_irq_handler, (share_irq ? IRQF_SHARED : 0), LIRC_DRIVER_NAME, (void *)&hardware); if (result < 0) { @@ -800,22 +800,22 @@ static int lirc_serial_probe(struct platform_device *dev) * for the NSLU2 it's done in boot code. */ if (((iommap != 0) - && (request_mem_region(iommap, 8 << ioshift, - LIRC_DRIVER_NAME) == NULL)) + && (devm_request_mem_region(&dev->dev, iommap, 8 << ioshift, + LIRC_DRIVER_NAME) == NULL)) || ((iommap == 0) - && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) { + && (devm_request_region(&dev->dev, io, 8, + LIRC_DRIVER_NAME) == NULL))) { dev_err(&dev->dev, "port %04x already in use\n", io); dev_warn(&dev->dev, "use 'setserial /dev/ttySX uart none'\n"); dev_warn(&dev->dev, "or compile the serial port driver as module and\n"); dev_warn(&dev->dev, "make sure this module is loaded first\n"); - result = -EBUSY; - goto exit_free_irq; + return -EBUSY; } result = hardware_init_port(); if (result < 0) - goto exit_release_region; + return result; /* Initialize pulse/space widths */ init_timing_params(duty_cycle, freq); @@ -847,28 +847,6 @@ static int lirc_serial_probe(struct platform_device *dev) dprintk("Interrupt %d, port %04x obtained\n", irq, io); return 0; - -exit_release_region: - if (iommap != 0) - release_mem_region(iommap, 8 << ioshift); - else - release_region(io, 8); -exit_free_irq: - free_irq(irq, (void *)&hardware); - - return result; -} - -static int lirc_serial_remove(struct platform_device *dev) -{ - free_irq(irq, (void *)&hardware); - - if (iommap != 0) - release_mem_region(iommap, 8 << ioshift); - else - release_region(io, 8); - - return 0; } static int set_use_inc(void *data) @@ -1081,7 +1059,6 @@ static int lirc_serial_resume(struct platform_device *dev) static struct platform_driver lirc_serial_driver = { .probe = lirc_serial_probe, - .remove = lirc_serial_remove, .suspend = lirc_serial_suspend, .resume = lirc_serial_resume, .driver = { -- cgit v0.10.2 From c6e58110da131a866f26b3860aea746de134318b Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 2 Jul 2014 10:49:46 -0300 Subject: [media] staging: omap4iss: Fix type of struct iss_device::crashed The crashed member of struct iss_device is documented to be a bitmask, but a bool doesn't hold that many (usable) bits. Lines 589 and 659 of iss.c strongly suggest that "unsigned int" was meant (the same type as struct iss_pipeline::entities). Currently, any crashed entity will be blamed on index 0, which is unlikely to be what was intended. Signed-off-by: Rasmus Villemoes Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h index 05cd9bf..734cfee 100644 --- a/drivers/staging/media/omap4iss/iss.h +++ b/drivers/staging/media/omap4iss/iss.h @@ -97,7 +97,7 @@ struct iss_device { u64 raw_dmamask; struct mutex iss_mutex; /* For handling ref_count field */ - bool crashed; + unsigned int crashed; int has_context; int ref_count; -- cgit v0.10.2 From ad86faada7d8dd4268ea8ad7b0b5dbc308a6994f Mon Sep 17 00:00:00 2001 From: Raimonds Cicans Date: Tue, 8 Jul 2014 07:38:20 -0300 Subject: [media] Fix typo in comments Expression ((7bit i2c_addr << 1) & 0x01) can not be right because it is always 0 Signed-off-by: Raimonds Cicans Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/dibusb.h b/drivers/media/usb/dvb-usb/dibusb.h index e47c321..32ab139 100644 --- a/drivers/media/usb/dvb-usb/dibusb.h +++ b/drivers/media/usb/dvb-usb/dibusb.h @@ -36,7 +36,7 @@ /* * i2c read - * bulk write: 0x02 ((7bit i2c_addr << 1) & 0x01) register_bytes length_word + * bulk write: 0x02 ((7bit i2c_addr << 1) | 0x01) register_bytes length_word * bulk read: byte_buffer (length_word bytes) */ #define DIBUSB_REQ_I2C_READ 0x02 -- cgit v0.10.2 From 381d7f79da1bad30f5228b7e505508ea74c642fb Mon Sep 17 00:00:00 2001 From: Zheng Di Date: Sun, 6 Jul 2014 10:37:06 -0300 Subject: [media] staging: media: lirc_parallel.c: fix coding style This patch fix checkpatch: WARNING: else is not generally useful after a break or return WARNING: line over 80 characters [m.chehab@samsung.com: Fix conflicts and use a better coding style for passing the parameters to parport_register_device()] Signed-off-by: Zheng Di Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c index 1394f02..672858a 100644 --- a/drivers/staging/media/lirc/lirc_parallel.c +++ b/drivers/staging/media/lirc/lirc_parallel.c @@ -169,23 +169,22 @@ static unsigned int init_lirc_timer(void) newtimer = (1000000*count)/timeelapsed; pr_info("%u Hz timer detected\n", newtimer); return newtimer; - } else { - newtimer = (1000000*count)/timeelapsed; - if (abs(newtimer - default_timer) > default_timer/10) { - /* bad timer */ - pr_notice("bad timer: %u Hz\n", newtimer); - pr_notice("using default timer: %u Hz\n", - default_timer); - return default_timer; - } else { - pr_info("%u Hz timer detected\n", newtimer); - return newtimer; /* use detected value */ - } } - } else { - pr_notice("no timer detected\n"); - return 0; + newtimer = (1000000*count)/timeelapsed; + if (abs(newtimer - default_timer) > default_timer/10) { + /* bad timer */ + pr_notice("bad timer: %u Hz\n", newtimer); + pr_notice("using default timer: %u Hz\n", + default_timer); + return default_timer; + } else { + pr_info("%u Hz timer detected\n", newtimer); + return newtimer; /* use detected value */ + } } + + pr_notice("no timer detected\n"); + return 0; } static int lirc_claim(void) @@ -661,7 +660,8 @@ static int __init lirc_parallel_init(void) goto exit_device_put; } ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME, - pf, kf, lirc_lirc_irq_handler, 0, NULL); + pf, kf, lirc_lirc_irq_handler, 0, + NULL); parport_put_port(pport); if (ppdevice == NULL) { pr_notice("parport_register_device() failed\n"); -- cgit v0.10.2 From 5015389fda673928e173c2ddb7bd9250a3d64c0e Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Fri, 25 Jul 2014 14:48:55 -0300 Subject: [media] em28xx-v4l: simplify some pointers in em28xx_init_camera() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pointer v4l2" can be used instead of "dev->v4l2, which saves some characters. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c index 12d4c03..6d2ea9a 100644 --- a/drivers/media/usb/em28xx/em28xx-camera.c +++ b/drivers/media/usb/em28xx/em28xx-camera.c @@ -366,7 +366,7 @@ int em28xx_init_camera(struct em28xx *dev) v4l2->sensor_xtal = 4300000; pdata.xtal = v4l2->sensor_xtal; if (NULL == - v4l2_i2c_new_subdev_board(&dev->v4l2->v4l2_dev, adap, + v4l2_i2c_new_subdev_board(&v4l2->v4l2_dev, adap, &mt9v011_info, NULL)) { ret = -ENODEV; break; @@ -423,7 +423,7 @@ int em28xx_init_camera(struct em28xx *dev) v4l2->sensor_yres = 480; subdev = - v4l2_i2c_new_subdev_board(&dev->v4l2->v4l2_dev, adap, + v4l2_i2c_new_subdev_board(&v4l2->v4l2_dev, adap, &ov2640_info, NULL); if (NULL == subdev) { ret = -ENODEV; -- cgit v0.10.2 From 917ba6b0b6707b6e6ca257461ceadff1235e8b2c Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Fri, 25 Jul 2014 14:48:56 -0300 Subject: [media] em28xx-v4l: get rid of struct em28xx_fh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct em28xx_fh isn't needed anymore because the only used field which is left is struct v4l2_fh fh. Use struct v4l2_fh directly and remvove struct em28xx_fh. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 3f8b5aa..4eb4a6a 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1227,8 +1227,7 @@ static void scale_to_size(struct em28xx *dev, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; f->fmt.pix.width = v4l2->width; @@ -1261,8 +1260,7 @@ static struct em28xx_fmt *format_by_fourcc(unsigned int fourcc) static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; unsigned int width = f->fmt.pix.width; unsigned int height = f->fmt.pix.height; @@ -1355,8 +1353,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); *norm = dev->v4l2->norm; @@ -1365,8 +1362,7 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, video, querystd, norm); @@ -1375,8 +1371,7 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; struct v4l2_format f; @@ -1408,8 +1403,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) static int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *p) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; int rc = 0; @@ -1427,8 +1421,7 @@ static int vidioc_g_parm(struct file *file, void *priv, static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *p) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); p->parm.capture.readbuffers = EM28XX_MIN_BUF; return v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, @@ -1450,8 +1443,7 @@ static const char *iname[] = { static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); unsigned int n; n = i->index; @@ -1479,8 +1471,7 @@ static int vidioc_enum_input(struct file *file, void *priv, static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); *i = dev->ctl_input; @@ -1489,8 +1480,7 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); if (i >= MAX_EM28XX_INPUT) return -EINVAL; @@ -1503,8 +1493,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); switch (a->index) { case EM28XX_AMUX_VIDEO: @@ -1543,8 +1532,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); if (a->index >= MAX_EM28XX_INPUT) return -EINVAL; @@ -1563,8 +1551,7 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); if (0 != t->index) return -EINVAL; @@ -1578,8 +1565,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); if (0 != t->index) return -EINVAL; @@ -1591,8 +1577,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; if (0 != f->tuner) @@ -1606,8 +1591,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { struct v4l2_frequency new_freq = *f; - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; if (0 != f->tuner) @@ -1624,8 +1608,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_chip_info(struct file *file, void *priv, struct v4l2_dbg_chip_info *chip) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); if (chip->match.addr > 1) return -EINVAL; @@ -1652,8 +1635,7 @@ static int em28xx_reg_len(int reg) static int vidioc_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); int ret; if (reg->match.addr > 1) @@ -1693,8 +1675,7 @@ static int vidioc_g_register(struct file *file, void *priv, static int vidioc_s_register(struct file *file, void *priv, const struct v4l2_dbg_register *reg) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); __le16 buf; if (reg->match.addr > 1) @@ -1715,8 +1696,7 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct video_device *vdev = video_devdata(file); - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); @@ -1761,8 +1741,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); struct em28xx_fmt *fmt; unsigned int maxw = norm_maxw(dev); unsigned int maxh = norm_maxh(dev); @@ -1806,8 +1785,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_format *format) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; format->fmt.vbi.samples_per_line = v4l2->vbi_width; @@ -1840,7 +1818,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; + struct em28xx *dev = video_drvdata(file); if (unlikely(t->index > 0)) return -EINVAL; @@ -1855,7 +1833,7 @@ static int radio_g_tuner(struct file *file, void *priv, static int radio_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; + struct em28xx *dev = video_drvdata(file); if (0 != t->index) return -EINVAL; @@ -1890,7 +1868,7 @@ static int em28xx_v4l2_open(struct file *filp) struct em28xx *dev = video_drvdata(filp); struct em28xx_v4l2 *v4l2 = dev->v4l2; enum v4l2_buf_type fh_type = 0; - struct em28xx_fh *fh; + struct v4l2_fh *fh; switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: @@ -1911,15 +1889,13 @@ static int em28xx_v4l2_open(struct file *filp) if (mutex_lock_interruptible(&dev->lock)) return -ERESTARTSYS; - fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); + fh = kzalloc(sizeof(struct v4l2_fh), GFP_KERNEL); if (!fh) { em28xx_errdev("em28xx-video.c: Out of memory?!\n"); mutex_unlock(&dev->lock); return -ENOMEM; } - v4l2_fh_init(&fh->fh, vdev); - fh->dev = dev; - fh->type = fh_type; + v4l2_fh_init(fh, vdev); filp->private_data = fh; if (v4l2->users == 0) { @@ -1945,7 +1921,7 @@ static int em28xx_v4l2_open(struct file *filp) v4l2->users++; mutex_unlock(&dev->lock); - v4l2_fh_add(&fh->fh); + v4l2_fh_add(fh); return 0; } @@ -2046,8 +2022,7 @@ static int em28xx_v4l2_resume(struct em28xx *dev) */ static int em28xx_v4l2_close(struct file *filp) { - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; + struct em28xx *dev = video_drvdata(filp); struct em28xx_v4l2 *v4l2 = dev->v4l2; int errCode; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index b4c837d..4360338 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -576,13 +576,6 @@ struct em28xx_audio { struct em28xx; -struct em28xx_fh { - struct v4l2_fh fh; - struct em28xx *dev; - - enum v4l2_buf_type type; -}; - enum em28xx_i2c_algo_type { EM28XX_I2C_ALGO_EM28XX = 0, EM28XX_I2C_ALGO_EM2800, -- cgit v0.10.2 From 3c0f90e14caf97896cd347b3b9fec8cba6b56b14 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Fri, 25 Jul 2014 14:48:57 -0300 Subject: [media] em28xx-v4l: simplify em28xx_v4l2_open() by using v4l2_fh_open() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of calling ... struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL); filp->private_data = fh; v4l2_fh_init(fh, vdev); v4l2_fh_add(fh); ... simply use function v4l2_fh_open() which does all of these calls for us. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 4eb4a6a..3a7ec3b 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1868,7 +1868,7 @@ static int em28xx_v4l2_open(struct file *filp) struct em28xx *dev = video_drvdata(filp); struct em28xx_v4l2 *v4l2 = dev->v4l2; enum v4l2_buf_type fh_type = 0; - struct v4l2_fh *fh; + int ret; switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: @@ -1889,14 +1889,14 @@ static int em28xx_v4l2_open(struct file *filp) if (mutex_lock_interruptible(&dev->lock)) return -ERESTARTSYS; - fh = kzalloc(sizeof(struct v4l2_fh), GFP_KERNEL); - if (!fh) { - em28xx_errdev("em28xx-video.c: Out of memory?!\n"); + + ret = v4l2_fh_open(filp); + if (ret) { + em28xx_errdev("%s: v4l2_fh_open() returned error %d\n", + __func__, ret); mutex_unlock(&dev->lock); - return -ENOMEM; + return ret; } - v4l2_fh_init(fh, vdev); - filp->private_data = fh; if (v4l2->users == 0) { em28xx_set_mode(dev, EM28XX_ANALOG_MODE); @@ -1921,7 +1921,6 @@ static int em28xx_v4l2_open(struct file *filp) v4l2->users++; mutex_unlock(&dev->lock); - v4l2_fh_add(fh); return 0; } -- cgit v0.10.2 From 747dba7de2a51a3db58b665ed3bc8c07921546ec Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Fri, 25 Jul 2014 14:48:58 -0300 Subject: [media] em28xx-v4l: get rid of field "users" in struct em28xx_v4l2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of counting the number of opened file handles, use function v4l2_fh_is_singular_file() in em28xx_v4l2_open() and em28xx_v4l2_close() to determine if the file handle is the first/last opened one. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 3a7ec3b..087ccf9 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1883,9 +1883,8 @@ static int em28xx_v4l2_open(struct file *filp) return -EINVAL; } - em28xx_videodbg("open dev=%s type=%s users=%d\n", - video_device_node_name(vdev), v4l2_type_names[fh_type], - v4l2->users); + em28xx_videodbg("open dev=%s type=%s\n", + video_device_node_name(vdev), v4l2_type_names[fh_type]); if (mutex_lock_interruptible(&dev->lock)) return -ERESTARTSYS; @@ -1898,7 +1897,9 @@ static int em28xx_v4l2_open(struct file *filp) return ret; } - if (v4l2->users == 0) { + if (v4l2_fh_is_singular_file(filp)) { + em28xx_videodbg("first opened filehandle, initializing device\n"); + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); if (vdev->vfl_type != VFL_TYPE_RADIO) @@ -1909,6 +1910,8 @@ static int em28xx_v4l2_open(struct file *filp) * of some i2c devices */ em28xx_wake_i2c(dev); + } else { + em28xx_videodbg("further filehandles are already opened\n"); } if (vdev->vfl_type == VFL_TYPE_RADIO) { @@ -1918,7 +1921,6 @@ static int em28xx_v4l2_open(struct file *filp) kref_get(&dev->ref); kref_get(&v4l2->ref); - v4l2->users++; mutex_unlock(&dev->lock); @@ -2025,12 +2027,11 @@ static int em28xx_v4l2_close(struct file *filp) struct em28xx_v4l2 *v4l2 = dev->v4l2; int errCode; - em28xx_videodbg("users=%d\n", v4l2->users); - - vb2_fop_release(filp); mutex_lock(&dev->lock); - if (v4l2->users == 1) { + if (v4l2_fh_is_singular_file(filp)) { + em28xx_videodbg("last opened filehandle, shutting down device\n"); + /* No sense to try to write to the device */ if (dev->disconnected) goto exit; @@ -2049,10 +2050,12 @@ static int em28xx_v4l2_close(struct file *filp) em28xx_errdev("cannot change alternate number to " "0 (error=%i)\n", errCode); } + } else { + em28xx_videodbg("further opened filehandles left\n"); } exit: - v4l2->users--; + vb2_fop_release(filp); kref_put(&v4l2->ref, em28xx_free_v4l2); mutex_unlock(&dev->lock); kref_put(&dev->ref, em28xx_free_device); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 4360338..84ef8ef 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -524,7 +524,6 @@ struct em28xx_v4l2 { int sensor_yres; int sensor_xtal; - int users; /* user count for exclusive use */ int streaming_users; /* number of actively streaming users */ u32 frequency; /* selected tuner frequency */ -- cgit v0.10.2 From 3d3a41098e4b9580e7570026ab361cb5b0c7f037 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Fri, 25 Jul 2014 15:08:30 -0300 Subject: [media] em28xx-v4l: fix disabling ioctl VIDIOC_S_PARM for vbi devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes an old copy+paste bug that has survived all recent code changes in this code area. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 087ccf9..90dec29 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2528,7 +2528,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2->vbi_dev->queue->lock = &v4l2->vb_vbi_queue_lock; /* disable inapplicable ioctls */ - v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_PARM); + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_PARM); if (dev->tuner_type == TUNER_ABSENT) { v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_TUNER); v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_TUNER); -- cgit v0.10.2 From f7d0e6d67fdc300c316dfb7ca1d4aaa234171053 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Wed, 23 Jul 2014 06:57:14 -0300 Subject: [media] media: blackfin: ppi: Pass device pointer to request peripheral pins if the pinctrl driver is enabled. Signed-off-by: Sonic Zhang Acked-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 6ef9866..7c70d51 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -939,7 +939,7 @@ static int bcap_probe(struct platform_device *pdev) bcap_dev->cfg = config; - bcap_dev->ppi = ppi_create_instance(config->ppi_info); + bcap_dev->ppi = ppi_create_instance(pdev, config->ppi_info); if (!bcap_dev->ppi) { v4l2_err(pdev->dev.driver, "Unable to create ppi\n"); ret = -ENODEV; diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index 15e9c2b..90c4a93 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -307,7 +308,8 @@ static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr) set_dma_start_addr(ppi->info->dma_ch, addr); } -struct ppi_if *ppi_create_instance(const struct ppi_info *info) +struct ppi_if *ppi_create_instance(struct platform_device *pdev, + const struct ppi_info *info) { struct ppi_if *ppi; @@ -315,14 +317,14 @@ struct ppi_if *ppi_create_instance(const struct ppi_info *info) return NULL; if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) { - pr_err("request peripheral failed\n"); + dev_err(&pdev->dev, "request peripheral failed\n"); return NULL; } ppi = kzalloc(sizeof(*ppi), GFP_KERNEL); if (!ppi) { peripheral_free_list(info->pin_req); - pr_err("unable to allocate memory for ppi handle\n"); + dev_err(&pdev->dev, "unable to allocate memory for ppi handle\n"); return NULL; } ppi->ops = &ppi_ops; diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h index d0697f4..61a283f 100644 --- a/include/media/blackfin/ppi.h +++ b/include/media/blackfin/ppi.h @@ -91,6 +91,7 @@ struct ppi_if { void *priv; }; -struct ppi_if *ppi_create_instance(const struct ppi_info *info); +struct ppi_if *ppi_create_instance(struct platform_device *pdev, + const struct ppi_info *info); void ppi_delete_instance(struct ppi_if *ppi); #endif -- cgit v0.10.2 From 7ba3c21c17d0fec397dc5e0337a3548eb14220bd Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Wed, 23 Jul 2014 06:57:15 -0300 Subject: [media] v4l2: bfin: Ensure delete and reinit list entry on NOMMU architecture On NOMMU architecture page fault is not triggered if a deleted list entry is accessed without reinit. Signed-off-by: Sonic Zhang Acked-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 7c70d51..9b5daa6 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -446,7 +446,7 @@ static void bcap_stop_streaming(struct vb2_queue *vq) while (!list_empty(&bcap_dev->dma_queue)) { bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, struct bcap_buffer, list); - list_del(&bcap_dev->cur_frm->list); + list_del_init(&bcap_dev->cur_frm->list); vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR); } } @@ -533,7 +533,7 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) } bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, struct bcap_buffer, list); - list_del(&bcap_dev->cur_frm->list); + list_del_init(&bcap_dev->cur_frm->list); } else { /* clear error flag, we will get a new frame */ if (ppi->err) @@ -583,7 +583,7 @@ static int bcap_streamon(struct file *file, void *priv, bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, struct bcap_buffer, list); /* remove buffer from the dma queue */ - list_del(&bcap_dev->cur_frm->list); + list_del_init(&bcap_dev->cur_frm->list); addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); /* update DMA address */ ppi->ops->update_addr(ppi, (unsigned long)addr); -- cgit v0.10.2 From 0a6824bc10de58dac8beca4ad6744753f1cf9a6f Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Wed, 23 Jul 2014 06:57:16 -0300 Subject: [media] v4l2: blackfin: select proper pinctrl state in ppi_set_params if CONFIG_PINCTRL is enabled Multiple pinctrl states are defined for 8, 16 and 24 data pin groups in PPI peripheral. The driver should select correct group before set up further PPI parameters. Signed-off-by: Sonic Zhang Acked-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index 90c4a93..cff63e5 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -206,6 +206,20 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) int dma_config, bytes_per_line; int hcount, hdelay, samples_per_line; +#ifdef CONFIG_PINCTRL + static const char * const pin_state[] = {"8bit", "16bit", "24bit"}; + struct pinctrl *pctrl; + struct pinctrl_state *pstate; + + if (params->dlen > 24 || params->dlen <= 0) + return -EINVAL; + pctrl = devm_pinctrl_get(ppi->dev); + pstate = pinctrl_lookup_state(pctrl, + pin_state[(params->dlen + 7) / 8 - 1]); + if (pinctrl_select_state(pctrl, pstate)) + return -EINVAL; +#endif + bytes_per_line = params->width * params->bpp / 8; /* convert parameters unit from pixels to samples */ hcount = params->width * params->bpp / params->dlen; @@ -316,10 +330,12 @@ struct ppi_if *ppi_create_instance(struct platform_device *pdev, if (!info || !info->pin_req) return NULL; +#ifndef CONFIG_PINCTRL if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) { dev_err(&pdev->dev, "request peripheral failed\n"); return NULL; } +#endif ppi = kzalloc(sizeof(*ppi), GFP_KERNEL); if (!ppi) { @@ -329,6 +345,7 @@ struct ppi_if *ppi_create_instance(struct platform_device *pdev, } ppi->ops = &ppi_ops; ppi->info = info; + ppi->dev = &pdev->dev; pr_info("ppi probe success\n"); return ppi; diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h index 61a283f..4900bae 100644 --- a/include/media/blackfin/ppi.h +++ b/include/media/blackfin/ppi.h @@ -83,6 +83,7 @@ struct ppi_info { }; struct ppi_if { + struct device *dev; unsigned long ppi_control; const struct ppi_ops *ops; const struct ppi_info *info; -- cgit v0.10.2 From 1dee9b59d69a15d566c16ee6fbd7216108ad5cac Mon Sep 17 00:00:00 2001 From: "Marcel J.E. Mol" Date: Sat, 26 Jul 2014 17:28:26 -0300 Subject: [media] rc: Add support for decoding XMP protocol This protocol is found on Dreambox remotes [m.chehab@samsung.com: CodingStyle fixes and conflict fix] Signed-off-by: Marcel Mol Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 9d15a57..5e626af 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -113,6 +113,16 @@ config IR_MCE_KBD_DECODER Enable this option if you have a Microsoft Remote Keyboard for Windows Media Center Edition, which you would like to use with a raw IR receiver in your system. + +config IR_XMP_DECODER + tristate "Enable IR raw decoder for the XMP protocol" + depends on RC_CORE + select BITREVERSE + default y + + ---help--- + Enable this option if you have IR with XMP protocol, and + if the IR is decoded in software endif #RC_DECODERS menuconfig RC_DEVICES diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 096eb06..9f9843a1 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o +obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o # stand-alone IR receivers/transmitters obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c new file mode 100644 index 0000000..1017d48 --- /dev/null +++ b/drivers/media/rc/ir-xmp-decoder.c @@ -0,0 +1,225 @@ +/* ir-xmp-decoder.c - handle XMP IR Pulse/Space protocol + * + * Copyright (C) 2014 by Marcel Mol + * + * 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 version 2 of the License. + * + * 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. + * + * - Based on info from http://www.hifi-remote.com + * - Ignore Toggle=9 frames + * - Ignore XMP-1 XMP-2 difference, always store 16 bit OBC + */ + +#include +#include +#include "rc-core-priv.h" + +#define XMP_UNIT 136000 /* ns */ +#define XMP_LEADER 210000 /* ns */ +#define XMP_NIBBLE_PREFIX 760000 /* ns */ +#define XMP_HALFFRAME_SPACE 13800000 /* ns */ +#define XMP_TRAILER_SPACE 20000000 /* should be 80ms but not all dureation supliers can go that high */ + +enum xmp_state { + STATE_INACTIVE, + STATE_LEADER_PULSE, + STATE_NIBBLE_SPACE, +}; + +/** + * ir_xmp_decode() - Decode one XMP pulse or space + * @dev: the struct rc_dev descriptor of the device + * @duration: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) +{ + struct xmp_dec *data = &dev->raw->xmp; + + if (!(dev->enabled_protocols & RC_BIT_XMP)) + return 0; + + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; + return 0; + } + + IR_dprintk(2, "XMP decode started at state %d %d (%uus %s)\n", + data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); + + switch (data->state) { + + case STATE_INACTIVE: + if (!ev.pulse) + break; + + if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2)) { + data->count = 0; + data->state = STATE_NIBBLE_SPACE; + } + + return 0; + + case STATE_LEADER_PULSE: + if (!ev.pulse) + break; + + if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2)) + data->state = STATE_NIBBLE_SPACE; + + return 0; + + case STATE_NIBBLE_SPACE: + if (ev.pulse) + break; + + if (geq_margin(ev.duration, XMP_TRAILER_SPACE, XMP_NIBBLE_PREFIX)) { + int divider, i; + u8 addr, subaddr, subaddr2, toggle, oem, obc1, obc2, sum1, sum2; + u32 *n; + u32 scancode; + + if (data->count != 16) { + IR_dprintk(2, "received TRAILER period at index %d: %u\n", + data->count, ev.duration); + data->state = STATE_INACTIVE; + return -EINVAL; + } + + n = data->durations; + /* + * the 4th nibble should be 15 so base the divider on this + * to transform durations into nibbles. Substract 2000 from + * the divider to compensate for fluctuations in the signal + */ + divider = (n[3] - XMP_NIBBLE_PREFIX) / 15 - 2000; + if (divider < 50) { + IR_dprintk(2, "divider to small %d.\n", divider); + data->state = STATE_INACTIVE; + return -EINVAL; + } + + /* convert to nibbles and do some sanity checks */ + for (i = 0; i < 16; i++) + n[i] = (n[i] - XMP_NIBBLE_PREFIX) / divider; + sum1 = (15 + n[0] + n[1] + n[2] + n[3] + + n[4] + n[5] + n[6] + n[7]) % 16; + sum2 = (15 + n[8] + n[9] + n[10] + n[11] + + n[12] + n[13] + n[14] + n[15]) % 16; + + if (sum1 != 15 || sum2 != 15) { + IR_dprintk(2, "checksum errors sum1=0x%X sum2=0x%X\n", + sum1, sum2); + data->state = STATE_INACTIVE; + return -EINVAL; + } + + subaddr = n[0] << 4 | n[2]; + subaddr2 = n[8] << 4 | n[11]; + oem = n[4] << 4 | n[5]; + addr = n[6] << 4 | n[7]; + toggle = n[10]; + obc1 = n[12] << 4 | n[13]; + obc2 = n[14] << 4 | n[15]; + if (subaddr != subaddr2) { + IR_dprintk(2, "subaddress nibbles mismatch 0x%02X != 0x%02X\n", + subaddr, subaddr2); + data->state = STATE_INACTIVE; + return -EINVAL; + } + if (oem != 0x44) + IR_dprintk(1, "Warning: OEM nibbles 0x%02X. Expected 0x44\n", + oem); + + scancode = addr << 24 | subaddr << 16 | + obc1 << 8 | obc2; + IR_dprintk(1, "XMP scancode 0x%06x\n", scancode); + + if (toggle == 0) { + rc_keydown(dev, RC_TYPE_XMP, scancode, 0); + } else { + rc_repeat(dev); + IR_dprintk(1, "Repeat last key\n"); + } + data->state = STATE_INACTIVE; + + return 0; + + } else if (geq_margin(ev.duration, XMP_HALFFRAME_SPACE, XMP_NIBBLE_PREFIX)) { + /* Expect 8 or 16 nibble pulses. 16 in case of 'final' frame */ + if (data->count == 16) { + IR_dprintk(2, "received half frame pulse at index %d. Probably a final frame key-up event: %u\n", + data->count, ev.duration); + /* + * TODO: for now go back to half frame position + * so trailer can be found and key press + * can be handled. + */ + data->count = 8; + } + + else if (data->count != 8) + IR_dprintk(2, "received half frame pulse at index %d: %u\n", + data->count, ev.duration); + data->state = STATE_LEADER_PULSE; + + return 0; + + } else if (geq_margin(ev.duration, XMP_NIBBLE_PREFIX, XMP_UNIT)) { + /* store nibble raw data, decode after trailer */ + if (data->count == 16) { + IR_dprintk(2, "to many pulses (%d) ignoring: %u\n", + data->count, ev.duration); + data->state = STATE_INACTIVE; + return -EINVAL; + } + data->durations[data->count] = ev.duration; + data->count++; + data->state = STATE_LEADER_PULSE; + + return 0; + + } + + break; + } + + IR_dprintk(1, "XMP decode failed at count %d state %d (%uus %s)\n", + data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state = STATE_INACTIVE; + return -EINVAL; +} + +static struct ir_raw_handler xmp_handler = { + .protocols = RC_BIT_XMP, + .decode = ir_xmp_decode, +}; + +static int __init ir_xmp_decode_init(void) +{ + ir_raw_handler_register(&xmp_handler); + + printk(KERN_INFO "IR XMP protocol handler initialized\n"); + return 0; +} + +static void __exit ir_xmp_decode_exit(void) +{ + ir_raw_handler_unregister(&xmp_handler); +} + +module_init(ir_xmp_decode_init); +module_exit(ir_xmp_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marcel Mol "); +MODULE_AUTHOR("MESA Consulting (http://www.mesa.nl)"); +MODULE_DESCRIPTION("XMP IR protocol decoder"); diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index dea7aff..b68d4f76 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -110,6 +110,11 @@ struct ir_raw_event_ctrl { bool send_timeout_reports; } lirc; + struct xmp_dec { + int state; + unsigned count; + u32 durations[16]; + } xmp; }; /* macros for IR decoders */ @@ -225,5 +230,12 @@ static inline void load_mce_kbd_decode(void) { } static inline void load_lirc_codec(void) { } #endif +/* from ir-xmp-decoder.c */ +#ifdef CONFIG_IR_XMP_DECODER_MODULE +#define load_xmp_decode() request_module_nowait("ir-xmp-decoder") +#else +static inline void load_xmp_decode(void) { } +#endif + #endif /* _RC_CORE_PRIV */ diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index e772c4c..e8fff2a 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -362,6 +362,7 @@ void ir_raw_init(void) load_sharp_decode(); load_mce_kbd_decode(); load_lirc_codec(); + load_xmp_decode(); /* If needed, we may later add some init code. In this case, it is needed to change the CONFIG_MODULE test at rc-core.h diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 39e4b5f..dfceeb4 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -800,6 +800,7 @@ static struct { { RC_BIT_SHARP, "sharp" }, { RC_BIT_MCE_KBD, "mce_kbd" }, { RC_BIT_LIRC, "lirc" }, + { RC_BIT_XMP, "xmp" }, }; /** diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 894c7e4..80f9518 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -31,6 +31,7 @@ enum rc_type { RC_TYPE_RC6_6A_32 = 16, /* Philips RC6-6A-32 protocol */ RC_TYPE_RC6_MCE = 17, /* MCE (Philips RC6-6A-32 subtype) protocol */ RC_TYPE_SHARP = 18, /* Sharp protocol */ + RC_TYPE_XMP = 19, /* XMP protocol */ }; #define RC_BIT_NONE 0 @@ -53,6 +54,7 @@ enum rc_type { #define RC_BIT_RC6_6A_32 (1 << RC_TYPE_RC6_6A_32) #define RC_BIT_RC6_MCE (1 << RC_TYPE_RC6_MCE) #define RC_BIT_SHARP (1 << RC_TYPE_SHARP) +#define RC_BIT_XMP (1 << RC_TYPE_XMP) #define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | RC_BIT_LIRC | \ RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \ @@ -60,7 +62,9 @@ enum rc_type { RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20 | \ RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \ RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \ - RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP) + RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP | \ + RC_BIT_XMP) + #define RC_SCANCODE_UNKNOWN(x) (x) #define RC_SCANCODE_OTHER(x) (x) -- cgit v0.10.2 From 139d28826b8e2bc7a9232fde0d2f14812914f501 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 27 Jul 2014 16:27:27 -0300 Subject: [media] cx231xx: Fix the max number of interfaces The max number of interfaces was read from the wrong descriptor. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 2ee03e4..6d785db 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1185,8 +1185,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface, dev->vbi_or_sliced_cc_mode = 0; /* get maximum no.of IAD interfaces */ - assoc_desc = udev->actconfig->intf_assoc[0]; - dev->max_iad_interface_count = assoc_desc->bInterfaceCount; + dev->max_iad_interface_count = udev->config->desc.bNumInterfaces; /* init CIR module TBD */ -- cgit v0.10.2 From dcb78ac761488f59ac16c2e76534c567017ef57b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 27 Jul 2014 16:27:28 -0300 Subject: [media] cx231xx: Don't let an interface number to go past the array On some newer boards, like HVR-930C HD, the information at the PCB tables are sometimes higher than the ones actually available on the device. That causes the probing code to go past the interfaces array. Add checks to the interface number before going past the array. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 6d785db..38d8510 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1135,6 +1135,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface, int nr = 0, ifnum; int i, isoc_pipe = 0; char *speed; + u8 idx; struct usb_interface_assoc_descriptor *assoc_desc; ifnum = interface->altsetting[0].desc.bInterfaceNumber; @@ -1254,8 +1255,13 @@ static int cx231xx_usb_probe(struct usb_interface *interface, goto err_init; /* compute alternate max packet sizes for video */ - uif = udev->actconfig->interface[dev->current_pcb_config. - hs_config_info[0].interface_info.video_index + 1]; + idx = dev->current_pcb_config.hs_config_info[0].interface_info.video_index + 1; + if (idx >= dev->max_iad_interface_count) { + cx231xx_errdev("Video PCB interface #%d doesn't exist\n", idx); + retval = -ENODEV; + goto err_init; + } + uif = udev->actconfig->interface[idx]; dev->video_mode.end_point_addr = uif->altsetting[0]. endpoint[isoc_pipe].desc.bEndpointAddress; @@ -1283,9 +1289,14 @@ static int cx231xx_usb_probe(struct usb_interface *interface, } /* compute alternate max packet sizes for vbi */ - uif = udev->actconfig->interface[dev->current_pcb_config. - hs_config_info[0].interface_info. - vanc_index + 1]; + + idx = dev->current_pcb_config.hs_config_info[0].interface_info.vanc_index + 1; + if (idx >= dev->max_iad_interface_count) { + cx231xx_errdev("VBI PCB interface #%d doesn't exist\n", idx); + retval = -ENODEV; + goto err_vbi_alt; + } + uif = udev->actconfig->interface[idx]; dev->vbi_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc. @@ -1315,9 +1326,13 @@ static int cx231xx_usb_probe(struct usb_interface *interface, } /* compute alternate max packet sizes for sliced CC */ - uif = udev->actconfig->interface[dev->current_pcb_config. - hs_config_info[0].interface_info. - hanc_index + 1]; + idx = dev->current_pcb_config.hs_config_info[0].interface_info.hanc_index + 1; + if (idx >= dev->max_iad_interface_count) { + cx231xx_errdev("Sliced CC PCB interface #%d doesn't exist\n", idx); + retval = -ENODEV; + goto err_sliced_cc_alt; + } + uif = udev->actconfig->interface[idx]; dev->sliced_cc_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc. @@ -1347,10 +1362,13 @@ static int cx231xx_usb_probe(struct usb_interface *interface, if (dev->current_pcb_config.ts1_source != 0xff) { /* compute alternate max packet sizes for TS1 */ - uif = udev->actconfig->interface[dev->current_pcb_config. - hs_config_info[0]. - interface_info. - ts1_index + 1]; + idx = dev->current_pcb_config.hs_config_info[0].interface_info.ts1_index + 1; + if (idx >= dev->max_iad_interface_count) { + cx231xx_errdev("TS1 PCB interface #%d doesn't exist\n", idx); + retval = -ENODEV; + goto err_ts1_alt; + } + uif = udev->actconfig->interface[idx]; dev->ts1_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe]. -- cgit v0.10.2 From 184a82784d50ad82aadc3917a3bdd8314a0e9b9b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 27 Jul 2014 16:27:29 -0300 Subject: [media] cx231xx: use devm_ functions to allocate memory The initialization is already too complex. Use devm_ functions to make the code simpler and easier to modify. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 38d8510..cd74310 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -964,12 +964,6 @@ void cx231xx_release_resources(struct cx231xx *dev) /* Mark device as unused */ clear_bit(dev->devno, &cx231xx_devused); - - kfree(dev->video_mode.alt_max_pkt_size); - kfree(dev->vbi_mode.alt_max_pkt_size); - kfree(dev->sliced_cc_mode.alt_max_pkt_size); - kfree(dev->ts1_mode.alt_max_pkt_size); - kfree(dev); } /* @@ -1158,16 +1152,16 @@ static int cx231xx_usb_probe(struct usb_interface *interface, } } while (test_and_set_bit(nr, &cx231xx_devused)); + udev = usb_get_dev(interface_to_usbdev(interface)); + /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL); if (dev == NULL) { cx231xx_err(DRIVER_NAME ": out of memory!\n"); clear_bit(nr, &cx231xx_devused); return -ENOMEM; } - udev = usb_get_dev(interface_to_usbdev(interface)); - snprintf(dev->name, 29, "cx231xx #%d", nr); dev->devno = nr; dev->model = id->driver_info; @@ -1270,9 +1264,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface, cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", dev->video_mode.end_point_addr, dev->video_mode.num_alt); - dev->video_mode.alt_max_pkt_size = - kmalloc(32 * dev->video_mode.num_alt, GFP_KERNEL); + dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->video_mode.num_alt, GFP_KERNEL); if (dev->video_mode.alt_max_pkt_size == NULL) { cx231xx_errdev("out of memory!\n"); retval = -ENOMEM; @@ -1294,7 +1287,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface, if (idx >= dev->max_iad_interface_count) { cx231xx_errdev("VBI PCB interface #%d doesn't exist\n", idx); retval = -ENODEV; - goto err_vbi_alt; + goto err_video_alt; } uif = udev->actconfig->interface[idx]; @@ -1306,13 +1299,12 @@ static int cx231xx_usb_probe(struct usb_interface *interface, cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", dev->vbi_mode.end_point_addr, dev->vbi_mode.num_alt); - dev->vbi_mode.alt_max_pkt_size = - kmalloc(32 * dev->vbi_mode.num_alt, GFP_KERNEL); + dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->vbi_mode.num_alt, GFP_KERNEL); if (dev->vbi_mode.alt_max_pkt_size == NULL) { cx231xx_errdev("out of memory!\n"); retval = -ENOMEM; - goto err_vbi_alt; + goto err_video_alt; } for (i = 0; i < dev->vbi_mode.num_alt; i++) { @@ -1330,7 +1322,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface, if (idx >= dev->max_iad_interface_count) { cx231xx_errdev("Sliced CC PCB interface #%d doesn't exist\n", idx); retval = -ENODEV; - goto err_sliced_cc_alt; + goto err_video_alt; } uif = udev->actconfig->interface[idx]; @@ -1342,13 +1334,12 @@ static int cx231xx_usb_probe(struct usb_interface *interface, cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", dev->sliced_cc_mode.end_point_addr, dev->sliced_cc_mode.num_alt); - dev->sliced_cc_mode.alt_max_pkt_size = - kmalloc(32 * dev->sliced_cc_mode.num_alt, GFP_KERNEL); + dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->sliced_cc_mode.num_alt, GFP_KERNEL); if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) { cx231xx_errdev("out of memory!\n"); retval = -ENOMEM; - goto err_sliced_cc_alt; + goto err_video_alt; } for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) { @@ -1366,7 +1357,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface, if (idx >= dev->max_iad_interface_count) { cx231xx_errdev("TS1 PCB interface #%d doesn't exist\n", idx); retval = -ENODEV; - goto err_ts1_alt; + goto err_video_alt; } uif = udev->actconfig->interface[idx]; @@ -1378,13 +1369,12 @@ static int cx231xx_usb_probe(struct usb_interface *interface, cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", dev->ts1_mode.end_point_addr, dev->ts1_mode.num_alt); - dev->ts1_mode.alt_max_pkt_size = - kmalloc(32 * dev->ts1_mode.num_alt, GFP_KERNEL); + dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->ts1_mode.num_alt, GFP_KERNEL); if (dev->ts1_mode.alt_max_pkt_size == NULL) { cx231xx_errdev("out of memory!\n"); retval = -ENOMEM; - goto err_ts1_alt; + goto err_video_alt; } for (i = 0; i < dev->ts1_mode.num_alt; i++) { @@ -1411,12 +1401,6 @@ static int cx231xx_usb_probe(struct usb_interface *interface, request_modules(dev); return 0; -err_ts1_alt: - kfree(dev->sliced_cc_mode.alt_max_pkt_size); -err_sliced_cc_alt: - kfree(dev->vbi_mode.alt_max_pkt_size); -err_vbi_alt: - kfree(dev->video_mode.alt_max_pkt_size); err_video_alt: /* cx231xx_uninit_dev: */ cx231xx_close_extension(dev); @@ -1432,7 +1416,6 @@ err_v4l2: err_if: usb_put_dev(udev); clear_bit(dev->devno, &cx231xx_devused); - kfree(dev); return retval; } -- cgit v0.10.2 From 4d2a7d3509f52106c50007c1dc0b8d79e8040128 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 27 Jul 2014 16:27:31 -0300 Subject: [media] cx231xx: move analog init code to a separate function That makes easier to understand the code. It would also help to add support for having boards with just digital support on some latter patch, as allowed by some PCB configs. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index cd74310..75b24b7 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1115,6 +1115,117 @@ static void flush_request_modules(struct cx231xx *dev) #define flush_request_modules(dev) #endif /* CONFIG_MODULES */ +static int cx231xx_init_v4l2(struct cx231xx *dev, + struct usb_device *udev, + struct usb_interface *interface, + int isoc_pipe) +{ + struct usb_interface *uif; + int i, idx; + + /* Video Init */ + + /* compute alternate max packet sizes for video */ + idx = dev->current_pcb_config.hs_config_info[0].interface_info.video_index + 1; + if (idx >= dev->max_iad_interface_count) { + cx231xx_errdev("Video PCB interface #%d doesn't exist\n", idx); + return -ENODEV; + } + + uif = udev->actconfig->interface[idx]; + + dev->video_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc.bEndpointAddress; + dev->video_mode.num_alt = uif->num_altsetting; + + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->video_mode.end_point_addr, + dev->video_mode.num_alt); + + dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->video_mode.num_alt, GFP_KERNEL); + if (dev->video_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + return -ENOMEM; + } + + for (i = 0; i < dev->video_mode.num_alt; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize); + dev->video_mode.alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->video_mode.alt_max_pkt_size[i]); + } + + /* VBI Init */ + + idx = dev->current_pcb_config.hs_config_info[0].interface_info.vanc_index + 1; + if (idx >= dev->max_iad_interface_count) { + cx231xx_errdev("VBI PCB interface #%d doesn't exist\n", idx); + return -ENODEV; + } + uif = udev->actconfig->interface[idx]; + + dev->vbi_mode.end_point_addr = + uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress; + + dev->vbi_mode.num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->vbi_mode.end_point_addr, + dev->vbi_mode.num_alt); + + /* compute alternate max packet sizes for vbi */ + dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->vbi_mode.num_alt, GFP_KERNEL); + if (dev->vbi_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + return -ENOMEM; + } + + for (i = 0; i < dev->vbi_mode.num_alt; i++) { + u16 tmp = + le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + desc.wMaxPacketSize); + dev->vbi_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->vbi_mode.alt_max_pkt_size[i]); + } + + /* Sliced CC VBI init */ + + /* compute alternate max packet sizes for sliced CC */ + idx = dev->current_pcb_config.hs_config_info[0].interface_info.hanc_index + 1; + if (idx >= dev->max_iad_interface_count) { + cx231xx_errdev("Sliced CC PCB interface #%d doesn't exist\n", idx); + return -ENODEV; + } + uif = udev->actconfig->interface[idx]; + + dev->sliced_cc_mode.end_point_addr = + uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress; + + dev->sliced_cc_mode.num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->sliced_cc_mode.end_point_addr, + dev->sliced_cc_mode.num_alt); + dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->sliced_cc_mode.num_alt, GFP_KERNEL); + + if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + return -ENOMEM; + } + + for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + desc.wMaxPacketSize); + dev->sliced_cc_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->sliced_cc_mode.alt_max_pkt_size[i]); + } + + return 0; +} + /* * cx231xx_usb_probe() * checks for supported devices @@ -1232,124 +1343,21 @@ static int cx231xx_usb_probe(struct usb_interface *interface, /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); - /* - * AV device initialization - only done at the last interface - */ - /* Create v4l2 device */ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); if (retval) { cx231xx_errdev("v4l2_device_register failed\n"); - retval = -EIO; goto err_v4l2; } + /* allocate device struct */ retval = cx231xx_init_dev(dev, udev, nr); if (retval) goto err_init; - /* compute alternate max packet sizes for video */ - idx = dev->current_pcb_config.hs_config_info[0].interface_info.video_index + 1; - if (idx >= dev->max_iad_interface_count) { - cx231xx_errdev("Video PCB interface #%d doesn't exist\n", idx); - retval = -ENODEV; + retval = cx231xx_init_v4l2(dev, udev, interface, isoc_pipe); + if (retval) goto err_init; - } - uif = udev->actconfig->interface[idx]; - - dev->video_mode.end_point_addr = uif->altsetting[0]. - endpoint[isoc_pipe].desc.bEndpointAddress; - - dev->video_mode.num_alt = uif->num_altsetting; - cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", - dev->video_mode.end_point_addr, - dev->video_mode.num_alt); - - dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->video_mode.num_alt, GFP_KERNEL); - if (dev->video_mode.alt_max_pkt_size == NULL) { - cx231xx_errdev("out of memory!\n"); - retval = -ENOMEM; - goto err_video_alt; - } - - for (i = 0; i < dev->video_mode.num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. - desc.wMaxPacketSize); - dev->video_mode.alt_max_pkt_size[i] = - (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - cx231xx_info("Alternate setting %i, max size= %i\n", i, - dev->video_mode.alt_max_pkt_size[i]); - } - - /* compute alternate max packet sizes for vbi */ - - idx = dev->current_pcb_config.hs_config_info[0].interface_info.vanc_index + 1; - if (idx >= dev->max_iad_interface_count) { - cx231xx_errdev("VBI PCB interface #%d doesn't exist\n", idx); - retval = -ENODEV; - goto err_video_alt; - } - uif = udev->actconfig->interface[idx]; - - dev->vbi_mode.end_point_addr = - uif->altsetting[0].endpoint[isoc_pipe].desc. - bEndpointAddress; - - dev->vbi_mode.num_alt = uif->num_altsetting; - cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", - dev->vbi_mode.end_point_addr, - dev->vbi_mode.num_alt); - - dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->vbi_mode.num_alt, GFP_KERNEL); - if (dev->vbi_mode.alt_max_pkt_size == NULL) { - cx231xx_errdev("out of memory!\n"); - retval = -ENOMEM; - goto err_video_alt; - } - - for (i = 0; i < dev->vbi_mode.num_alt; i++) { - u16 tmp = - le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. - desc.wMaxPacketSize); - dev->vbi_mode.alt_max_pkt_size[i] = - (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - cx231xx_info("Alternate setting %i, max size= %i\n", i, - dev->vbi_mode.alt_max_pkt_size[i]); - } - - /* compute alternate max packet sizes for sliced CC */ - idx = dev->current_pcb_config.hs_config_info[0].interface_info.hanc_index + 1; - if (idx >= dev->max_iad_interface_count) { - cx231xx_errdev("Sliced CC PCB interface #%d doesn't exist\n", idx); - retval = -ENODEV; - goto err_video_alt; - } - uif = udev->actconfig->interface[idx]; - - dev->sliced_cc_mode.end_point_addr = - uif->altsetting[0].endpoint[isoc_pipe].desc. - bEndpointAddress; - - dev->sliced_cc_mode.num_alt = uif->num_altsetting; - cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", - dev->sliced_cc_mode.end_point_addr, - dev->sliced_cc_mode.num_alt); - dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->sliced_cc_mode.num_alt, GFP_KERNEL); - - if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) { - cx231xx_errdev("out of memory!\n"); - retval = -ENOMEM; - goto err_video_alt; - } - - for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. - desc.wMaxPacketSize); - dev->sliced_cc_mode.alt_max_pkt_size[i] = - (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - cx231xx_info("Alternate setting %i, max size= %i\n", i, - dev->sliced_cc_mode.alt_max_pkt_size[i]); - } if (dev->current_pcb_config.ts1_source != 0xff) { /* compute alternate max packet sizes for TS1 */ -- cgit v0.10.2 From 52841e5e145afdf5cc070863c383f41db8472575 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 27 Jul 2014 16:27:32 -0300 Subject: [media] cx231xx: return an error if it can't read PCB config Instead of using some random value, return an error if the PCB config is not available or doesn't match a know profile Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 75b24b7..db69307 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -997,7 +997,11 @@ static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev, dev->cx231xx_gpio_i2c_write = cx231xx_gpio_i2c_write; /* Query cx231xx to find what pcb config it is related to */ - initialize_cx231xx(dev); + retval = initialize_cx231xx(dev); + if (retval < 0) { + cx231xx_errdev("Failed to read PCB config\n"); + return retval; + } /*To workaround error number=-71 on EP0 for VideoGrabber, need set alt here.*/ diff --git a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c index 2a34cee..3052c4c 100644 --- a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c +++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c @@ -654,8 +654,9 @@ static struct pcb_config cx231xx_Scenario[] = { /*****************************************************************/ -u32 initialize_cx231xx(struct cx231xx *dev) +int initialize_cx231xx(struct cx231xx *dev) { + int retval; u32 config_info = 0; struct pcb_config *p_pcb_info; u8 usb_speed = 1; /* from register,1--HS, 0--FS */ @@ -670,7 +671,10 @@ u32 initialize_cx231xx(struct cx231xx *dev) /* read board config register to find out which pcb config it is related to */ - cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4); + retval = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, + data, 4); + if (retval < 0) + return retval; config_info = le32_to_cpu(*((__le32 *)data)); usb_speed = (u8) (config_info & 0x1); @@ -767,7 +771,7 @@ u32 initialize_cx231xx(struct cx231xx *dev) cx231xx_info("bad senario!!!!!\n"); cx231xx_info("config_info=%x\n", (config_info & SELFPOWER_MASK)); - return 1; + return -ENODEV; } } diff --git a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h index b3c6190..4511dc5 100644 --- a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h +++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h @@ -221,6 +221,6 @@ enum INDEX_PCB_CONFIG{ /***************************************************************************/ struct cx231xx; -u32 initialize_cx231xx(struct cx231xx *p_dev); +int initialize_cx231xx(struct cx231xx *p_dev); #endif -- cgit v0.10.2 From 9ca72fa1f854a596092901f3e0ee90bb3d459ed5 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 22 Jul 2014 17:12:11 -0300 Subject: [media] get_dvb_firmware: Add firmware extractor for si2165 We don't have license to redistribute this firmware, so extract it from the driver, and add the needed headers. Signed-off-by: Matthias Schwarzott Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index d91b8be..26c623d 100755 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -29,7 +29,7 @@ use IO::Handle; "af9015", "ngene", "az6027", "lme2510_lg", "lme2510c_s7395", "lme2510c_s7395_old", "drxk", "drxk_terratec_h5", "drxk_hauppauge_hvr930c", "tda10071", "it9135", "drxk_pctv", - "drxk_terratec_htc_stick", "sms1xxx_hcw"); + "drxk_terratec_htc_stick", "sms1xxx_hcw", "si2165"); # Check args syntax() if (scalar(@ARGV) != 1); @@ -783,6 +783,37 @@ sub sms1xxx_hcw { $allfiles; } +sub si2165 { + my $sourcefile = "model_111xxx_122xxx_driver_6_0_119_31191_WHQL.zip"; + my $url = "http://www.hauppauge.de/files/drivers/"; + my $hash = "76633e7c76b0edee47c3ba18ded99336"; + my $fwfile = "dvb-demod-si2165.fw"; + my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1); + + checkstandard(); + + wgetfile($sourcefile, $url . $sourcefile); + verify($sourcefile, $hash); + unzip($sourcefile, $tmpdir); + extract("$tmpdir/Driver10/Hcw10bda.sys", 0x80788, 0x81E08-0x80788, "$tmpdir/fw1"); + + delzero("$tmpdir/fw1","$tmpdir/fw1-1"); + #verify("$tmpdir/fw1","5e0909858fdf0b5b09ad48b9fe622e70"); + + my $CRC="\x0A\xCC"; + my $BLOCKS_MAIN="\x27"; + open FW,">$fwfile"; + print FW "\x01\x00"; # just a version id for the driver itself + print FW "\x9A"; # fw version + print FW "\x00"; # padding + print FW "$BLOCKS_MAIN"; # number of blocks of main part + print FW "\x00"; # padding + print FW "$CRC"; # 16bit crc value of main part + appendfile(FW,"$tmpdir/fw1"); + + "$fwfile"; +} + # --------------------------------------------------------------- # Utilities -- cgit v0.10.2 From 3e54a1697ace99d1ad602ec268abf117ace1412f Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 22 Jul 2014 17:12:12 -0300 Subject: [media] si2165: Add demod driver for DVB-T only DVB-T was tested with 8MHz BW channels in germany This driver is the simplest possible, it uses automatic mode for all parameters (TPS). Added spectrum inversion support. Signed-off-by: Matthias Schwarzott Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 5b8b04c..fe0ddcc 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -63,6 +63,15 @@ config DVB_TDA18271C2DD Say Y when you want to support this tuner. +config DVB_SI2165 + tristate "Silicon Labs si2165 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-C/T demodulator. + + Say Y when you want to support this frontend. + comment "DVB-S (satellite) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 655e3c8..edf103d 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -105,6 +105,7 @@ obj-$(CONFIG_DVB_STV0367) += stv0367.o obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o obj-$(CONFIG_DVB_DRXK) += drxk.o obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o +obj-$(CONFIG_DVB_SI2165) += si2165.o obj-$(CONFIG_DVB_A8293) += a8293.o obj-$(CONFIG_DVB_TDA10071) += tda10071.o obj-$(CONFIG_DVB_RTL2830) += rtl2830.o diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c new file mode 100644 index 0000000..3a2d6c5 --- /dev/null +++ b/drivers/media/dvb-frontends/si2165.c @@ -0,0 +1,1040 @@ +/* + Driver for Silicon Labs SI2165 DVB-C/-T Demodulator + + Copyright (C) 2013-2014 Matthias Schwarzott + + 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. + + References: + http://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "dvb_math.h" +#include "si2165_priv.h" +#include "si2165.h" + +/* Hauppauge WinTV-HVR-930C-HD B130 / PCTV QuatroStick 521e 1113xx + * uses 16 MHz xtal */ + +/* Hauppauge WinTV-HVR-930C-HD B131 / PCTV QuatroStick 522e 1114xx + * uses 24 MHz clock provided by tuner */ + +struct si2165_state { + struct i2c_adapter *i2c; + + struct dvb_frontend frontend; + + struct si2165_config config; + + /* chip revision */ + u8 revcode; + /* chip type */ + u8 chip_type; + + /* calculated by xtal and div settings */ + u32 fvco_hz; + u32 sys_clk; + u32 adc_clk; + + bool has_dvbc; + bool has_dvbt; + bool firmware_loaded; +}; + +#define DEBUG_OTHER 0x01 +#define DEBUG_I2C_WRITE 0x02 +#define DEBUG_I2C_READ 0x04 +#define DEBUG_REG_READ 0x08 +#define DEBUG_REG_WRITE 0x10 +#define DEBUG_FW_LOAD 0x20 + +static int debug = 0x00; + +#define dprintk(args...) \ + do { \ + if (debug & DEBUG_OTHER) \ + printk(KERN_DEBUG "si2165: " args); \ + } while (0) + +#define deb_i2c_write(args...) \ + do { \ + if (debug & DEBUG_I2C_WRITE) \ + printk(KERN_DEBUG "si2165: i2c write: " args); \ + } while (0) + +#define deb_i2c_read(args...) \ + do { \ + if (debug & DEBUG_I2C_READ) \ + printk(KERN_DEBUG "si2165: i2c read: " args); \ + } while (0) + +#define deb_readreg(args...) \ + do { \ + if (debug & DEBUG_REG_READ) \ + printk(KERN_DEBUG "si2165: reg read: " args); \ + } while (0) + +#define deb_writereg(args...) \ + do { \ + if (debug & DEBUG_REG_WRITE) \ + printk(KERN_DEBUG "si2165: reg write: " args); \ + } while (0) + +#define deb_fw_load(args...) \ + do { \ + if (debug & DEBUG_FW_LOAD) \ + printk(KERN_DEBUG "si2165: fw load: " args); \ + } while (0) + +static int si2165_write(struct si2165_state *state, const u16 reg, + const u8 *src, const int count) +{ + int ret; + struct i2c_msg msg; + u8 buf[2 + 4]; /* write a maximum of 4 bytes of data */ + + if (count + 2 > sizeof(buf)) { + dev_warn(&state->i2c->dev, + "%s: i2c wr reg=%04x: count=%d is too big!\n", + KBUILD_MODNAME, reg, count); + return -EINVAL; + } + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + memcpy(buf + 2, src, count); + + msg.addr = state->config.i2c_addr; + msg.flags = 0; + msg.buf = buf; + msg.len = count + 2; + + if (debug & DEBUG_I2C_WRITE) + deb_i2c_write("reg: 0x%04x, data: %*ph\n", reg, count, src); + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) { + dev_err(&state->i2c->dev, "%s: ret == %d\n", __func__, ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + + return 0; +} + +static int si2165_read(struct si2165_state *state, + const u16 reg, u8 *val, const int count) +{ + int ret; + u8 reg_buf[] = { reg >> 8, reg & 0xff }; + struct i2c_msg msg[] = { + { .addr = state->config.i2c_addr, + .flags = 0, .buf = reg_buf, .len = 2 }, + { .addr = state->config.i2c_addr, + .flags = I2C_M_RD, .buf = val, .len = count }, + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + dev_err(&state->i2c->dev, "%s: error (addr %02x reg %04x error (ret == %i)\n", + __func__, state->config.i2c_addr, reg, ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + + if (debug & DEBUG_I2C_READ) + deb_i2c_read("reg: 0x%04x, data: %*ph\n", reg, count, val); + + return 0; +} + +static int si2165_readreg8(struct si2165_state *state, + const u16 reg, u8 *val) +{ + int ret; + + ret = si2165_read(state, reg, val, 1); + deb_readreg("R(0x%04x)=0x%02x\n", reg, *val); + return ret; +} + +static int si2165_readreg16(struct si2165_state *state, + const u16 reg, u16 *val) +{ + u8 buf[2]; + + int ret = si2165_read(state, reg, buf, 2); + *val = buf[0] | buf[1] << 8; + deb_readreg("R(0x%04x)=0x%04x\n", reg, *val); + return ret; +} + +static int si2165_writereg8(struct si2165_state *state, const u16 reg, u8 val) +{ + return si2165_write(state, reg, &val, 1); +} + +static int si2165_writereg16(struct si2165_state *state, const u16 reg, u16 val) +{ + u8 buf[2] = { val & 0xff, (val >> 8) & 0xff }; + + return si2165_write(state, reg, buf, 2); +} + +static int si2165_writereg24(struct si2165_state *state, const u16 reg, u32 val) +{ + u8 buf[3] = { val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff }; + + return si2165_write(state, reg, buf, 3); +} + +static int si2165_writereg32(struct si2165_state *state, const u16 reg, u32 val) +{ + u8 buf[4] = { + val & 0xff, + (val >> 8) & 0xff, + (val >> 16) & 0xff, + (val >> 24) & 0xff + }; + return si2165_write(state, reg, buf, 4); +} + +static int si2165_writereg_mask8(struct si2165_state *state, const u16 reg, + u8 val, u8 mask) +{ + int ret; + u8 tmp; + + if (mask != 0xff) { + ret = si2165_readreg8(state, reg, &tmp); + if (ret < 0) + goto err; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + ret = si2165_writereg8(state, reg, val); +err: + return ret; +} + +static int si2165_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 1000; + return 0; +} + +static int si2165_init_pll(struct si2165_state *state) +{ + u32 ref_freq_Hz = state->config.ref_freq_Hz; + u8 divr = 1; /* 1..7 */ + u8 divp = 1; /* only 1 or 4 */ + u8 divn = 56; /* 1..63 */ + u8 divm = 8; + u8 divl = 12; + u8 buf[4]; + + /* hardcoded values can be deleted if calculation is verified + * or it yields the same values as the windows driver */ + switch (ref_freq_Hz) { + case 16000000u: + divn = 56; + break; + case 24000000u: + divr = 2; + divp = 4; + divn = 19; + break; + default: + /* ref_freq / divr must be between 4 and 16 MHz */ + if (ref_freq_Hz > 16000000u) + divr = 2; + + /* now select divn and divp such that + * fvco is in 1624..1824 MHz */ + if (1624000000u * divr > ref_freq_Hz * 2u * 63u) + divp = 4; + + /* is this already correct regarding rounding? */ + divn = 1624000000u * divr / (ref_freq_Hz * 2u * divp); + break; + } + + /* adc_clk and sys_clk depend on xtal and pll settings */ + state->fvco_hz = ref_freq_Hz / divr + * 2u * divn * divp; + state->adc_clk = state->fvco_hz / (divm * 4u); + state->sys_clk = state->fvco_hz / (divl * 2u); + + /* write pll registers 0x00a0..0x00a3 at once */ + buf[0] = divl; + buf[1] = divm; + buf[2] = (divn & 0x3f) | ((divp == 1) ? 0x40 : 0x00) | 0x80; + buf[3] = divr; + return si2165_write(state, 0x00a0, buf, 4); +} + +static int si2165_adjust_pll_divl(struct si2165_state *state, u8 divl) +{ + state->sys_clk = state->fvco_hz / (divl * 2u); + return si2165_writereg8(state, 0x00a0, divl); /* pll_divl */ +} + +static u32 si2165_get_fe_clk(struct si2165_state *state) +{ + /* assume Oversampling mode Ovr4 is used */ + return state->adc_clk; +} + +static bool si2165_wait_init_done(struct si2165_state *state) +{ + int ret = -EINVAL; + u8 val = 0; + int i; + + for (i = 0; i < 3; ++i) { + si2165_readreg8(state, 0x0054, &val); + if (val == 0x01) + return 0; + usleep_range(1000, 50000); + } + dev_err(&state->i2c->dev, "%s: init_done was not set\n", + KBUILD_MODNAME); + return ret; +} + +static int si2165_upload_firmware_block(struct si2165_state *state, + const u8 *data, u32 len, u32 *poffset, u32 block_count) +{ + int ret; + u8 buf_ctrl[4] = { 0x00, 0x00, 0x00, 0xc0 }; + u8 wordcount; + u32 cur_block = 0; + u32 offset = poffset ? *poffset : 0; + + if (len < 4) + return -EINVAL; + if (len % 4 != 0) + return -EINVAL; + + deb_fw_load("si2165_upload_firmware_block called with len=0x%x offset=0x%x blockcount=0x%x\n", + len, offset, block_count); + while (offset+12 <= len && cur_block < block_count) { + deb_fw_load("si2165_upload_firmware_block in while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n", + len, offset, cur_block, block_count); + wordcount = data[offset]; + if (wordcount < 1 || data[offset+1] || + data[offset+2] || data[offset+3]) { + dev_warn(&state->i2c->dev, + "%s: bad fw data[0..3] = %*ph\n", + KBUILD_MODNAME, 4, data); + return -EINVAL; + } + + if (offset + 8 + wordcount * 4 > len) { + dev_warn(&state->i2c->dev, + "%s: len is too small for block len=%d, wordcount=%d\n", + KBUILD_MODNAME, len, wordcount); + return -EINVAL; + } + + buf_ctrl[0] = wordcount - 1; + + ret = si2165_write(state, 0x0364, buf_ctrl, 4); + if (ret < 0) + goto error; + ret = si2165_write(state, 0x0368, data+offset+4, 4); + if (ret < 0) + goto error; + + offset += 8; + + while (wordcount > 0) { + ret = si2165_write(state, 0x36c, data+offset, 4); + if (ret < 0) + goto error; + wordcount--; + offset += 4; + } + cur_block++; + } + + deb_fw_load("si2165_upload_firmware_block after while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n", + len, offset, cur_block, block_count); + + if (poffset) + *poffset = offset; + + deb_fw_load("si2165_upload_firmware_block returned offset=0x%x\n", + offset); + + return 0; +error: + return ret; +} + +static int si2165_upload_firmware(struct si2165_state *state) +{ + /* int ret; */ + u8 val[3]; + u16 val16; + int ret; + + const struct firmware *fw = NULL; + u8 *fw_file = SI2165_FIRMWARE; + const u8 *data; + u32 len; + u32 offset; + u8 patch_version; + u8 block_count; + u16 crc_expected; + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, state->i2c->dev.parent); + if (ret) { + dev_warn(&state->i2c->dev, "%s: firmare file '%s' not found\n", + KBUILD_MODNAME, fw_file); + goto error; + } + + data = fw->data; + len = fw->size; + + dev_info(&state->i2c->dev, "%s: downloading firmware from file '%s' size=%d\n", + KBUILD_MODNAME, fw_file, len); + + if (len % 4 != 0) { + dev_warn(&state->i2c->dev, "%s: firmware size is not multiple of 4\n", + KBUILD_MODNAME); + ret = -EINVAL; + goto error; + } + + /* check header (8 bytes) */ + if (len < 8) { + dev_warn(&state->i2c->dev, "%s: firmware header is missing\n", + KBUILD_MODNAME); + ret = -EINVAL; + goto error; + } + + if (data[0] != 1 || data[1] != 0) { + dev_warn(&state->i2c->dev, "%s: firmware file version is wrong\n", + KBUILD_MODNAME); + ret = -EINVAL; + goto error; + } + + patch_version = data[2]; + block_count = data[4]; + crc_expected = data[7] << 8 | data[6]; + + /* start uploading fw */ + /* boot/wdog status */ + ret = si2165_writereg8(state, 0x0341, 0x00); + if (ret < 0) + goto error; + /* reset */ + ret = si2165_writereg8(state, 0x00c0, 0x00); + if (ret < 0) + goto error; + /* boot/wdog status */ + ret = si2165_readreg8(state, 0x0341, val); + if (ret < 0) + goto error; + + /* enable reset on error */ + ret = si2165_readreg8(state, 0x035c, val); + if (ret < 0) + goto error; + ret = si2165_readreg8(state, 0x035c, val); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, 0x035c, 0x02); + if (ret < 0) + goto error; + + /* start right after the header */ + offset = 8; + + dev_info(&state->i2c->dev, "%s: si2165_upload_firmware extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x\n", + KBUILD_MODNAME, patch_version, block_count, crc_expected); + + ret = si2165_upload_firmware_block(state, data, len, &offset, 1); + if (ret < 0) + goto error; + + ret = si2165_writereg8(state, 0x0344, patch_version); + if (ret < 0) + goto error; + + /* reset crc */ + ret = si2165_writereg8(state, 0x0379, 0x01); + if (ret) + return ret; + + ret = si2165_upload_firmware_block(state, data, len, + &offset, block_count); + if (ret < 0) { + dev_err(&state->i2c->dev, + "%s: firmare could not be uploaded\n", + KBUILD_MODNAME); + goto error; + } + + /* read crc */ + ret = si2165_readreg16(state, 0x037a, &val16); + if (ret) + goto error; + + if (val16 != crc_expected) { + dev_err(&state->i2c->dev, + "%s: firmware crc mismatch %04x != %04x\n", + KBUILD_MODNAME, val16, crc_expected); + ret = -EINVAL; + goto error; + } + + ret = si2165_upload_firmware_block(state, data, len, &offset, 5); + if (ret) + goto error; + + if (len != offset) { + dev_err(&state->i2c->dev, + "%s: firmare len mismatch %04x != %04x\n", + KBUILD_MODNAME, len, offset); + ret = -EINVAL; + goto error; + } + + /* reset watchdog error register */ + ret = si2165_writereg_mask8(state, 0x0341, 0x02, 0x02); + if (ret < 0) + goto error; + + /* enable reset on error */ + ret = si2165_writereg_mask8(state, 0x035c, 0x01, 0x01); + if (ret < 0) + goto error; + + dev_info(&state->i2c->dev, "%s: fw load finished\n", KBUILD_MODNAME); + + ret = 0; + state->firmware_loaded = true; +error: + if (fw) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + +static int si2165_init(struct dvb_frontend *fe) +{ + int ret = 0; + struct si2165_state *state = fe->demodulator_priv; + u8 val; + u8 patch_version = 0x00; + + dprintk("%s: called\n", __func__); + + /* powerup */ + ret = si2165_writereg8(state, 0x0000, state->config.chip_mode); + if (ret < 0) + goto error; + /* dsp_clock_enable */ + ret = si2165_writereg8(state, 0x0104, 0x01); + if (ret < 0) + goto error; + ret = si2165_readreg8(state, 0x0000, &val); /* verify chip_mode */ + if (ret < 0) + goto error; + if (val != state->config.chip_mode) { + dev_err(&state->i2c->dev, "%s: could not set chip_mode\n", + KBUILD_MODNAME); + return -EINVAL; + } + + /* agc */ + ret = si2165_writereg8(state, 0x018b, 0x00); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, 0x0190, 0x01); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, 0x0170, 0x00); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, 0x0171, 0x07); + if (ret < 0) + goto error; + /* rssi pad */ + ret = si2165_writereg8(state, 0x0646, 0x00); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, 0x0641, 0x00); + if (ret < 0) + goto error; + + ret = si2165_init_pll(state); + if (ret < 0) + goto error; + + /* enable chip_init */ + ret = si2165_writereg8(state, 0x0050, 0x01); + if (ret < 0) + goto error; + /* set start_init */ + ret = si2165_writereg8(state, 0x0096, 0x01); + if (ret < 0) + goto error; + ret = si2165_wait_init_done(state); + if (ret < 0) + goto error; + + /* disable chip_init */ + ret = si2165_writereg8(state, 0x0050, 0x00); + if (ret < 0) + goto error; + + /* ber_pkt */ + ret = si2165_writereg16(state, 0x0470 , 0x7530); + if (ret < 0) + goto error; + + ret = si2165_readreg8(state, 0x0344, &patch_version); + if (ret < 0) + goto error; + + ret = si2165_writereg8(state, 0x00cb, 0x00); + if (ret < 0) + goto error; + + /* dsp_addr_jump */ + ret = si2165_writereg32(state, 0x0348, 0xf4000000); + if (ret < 0) + goto error; + /* boot/wdog status */ + ret = si2165_readreg8(state, 0x0341, &val); + if (ret < 0) + goto error; + + if (patch_version == 0x00) { + ret = si2165_upload_firmware(state); + if (ret < 0) + goto error; + } + + /* write adc values after each reset*/ + ret = si2165_writereg8(state, 0x012a, 0x46); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, 0x012c, 0x00); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, 0x012e, 0x0a); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, 0x012f, 0xff); + if (ret < 0) + goto error; + ret = si2165_writereg8(state, 0x0123, 0x70); + if (ret < 0) + goto error; + + return 0; +error: + return ret; +} + +static int si2165_sleep(struct dvb_frontend *fe) +{ + int ret; + struct si2165_state *state = fe->demodulator_priv; + + /* dsp clock disable */ + ret = si2165_writereg8(state, 0x0104, 0x00); + if (ret < 0) + return ret; + /* chip mode */ + ret = si2165_writereg8(state, 0x0000, SI2165_MODE_OFF); + if (ret < 0) + return ret; + return 0; +} + +static int si2165_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + int ret; + u8 fec_lock = 0; + struct si2165_state *state = fe->demodulator_priv; + + if (!state->has_dvbt) + return -EINVAL; + + /* check fec_lock */ + ret = si2165_readreg8(state, 0x4e0, &fec_lock); + if (ret < 0) + return ret; + *status = 0; + if (fec_lock & 0x01) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + } + + return 0; +} + +static int si2165_set_oversamp(struct si2165_state *state, u32 dvb_rate) +{ + u64 oversamp; + u32 reg_value; + + oversamp = si2165_get_fe_clk(state); + oversamp <<= 23; + do_div(oversamp, dvb_rate); + reg_value = oversamp & 0x3fffffff; + + /* oversamp, usbdump contained 0x03100000; */ + return si2165_writereg32(state, 0x00e4, reg_value); +} + +static int si2165_set_if_freq_shift(struct si2165_state *state, u32 IF) +{ + u64 if_freq_shift; + s32 reg_value = 0; + u32 fe_clk = si2165_get_fe_clk(state); + + if_freq_shift = IF; + if_freq_shift <<= 29; + + do_div(if_freq_shift, fe_clk); + reg_value = (s32)if_freq_shift; + + if (state->config.inversion) + reg_value = -reg_value; + + reg_value = reg_value & 0x1fffffff; + + /* if_freq_shift, usbdump contained 0x023ee08f; */ + return si2165_writereg32(state, 0x00e8, reg_value); +} + +static int si2165_set_parameters(struct dvb_frontend *fe) +{ + int ret; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct si2165_state *state = fe->demodulator_priv; + u8 val[3]; + u32 IF; + u32 dvb_rate = 0; + u16 bw10k; + + dprintk("%s: called\n", __func__); + + if (!fe->ops.tuner_ops.get_if_frequency) { + dev_err(&state->i2c->dev, + "%s: Error: get_if_frequency() not defined at tuner. Can't work without it!\n", + KBUILD_MODNAME); + return -EINVAL; + } + + if (!state->has_dvbt) + return -EINVAL; + + if (p->bandwidth_hz > 0) { + dvb_rate = p->bandwidth_hz * 8 / 7; + bw10k = p->bandwidth_hz / 10000; + } else { + dvb_rate = 8 * 8 / 7; + bw10k = 800; + } + + /* standard = DVB-T */ + ret = si2165_writereg8(state, 0x00ec, 0x01); + if (ret < 0) + return ret; + ret = si2165_adjust_pll_divl(state, 12); + if (ret < 0) + return ret; + + fe->ops.tuner_ops.get_if_frequency(fe, &IF); + ret = si2165_set_if_freq_shift(state, IF); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, 0x08f8, 0x00); + if (ret < 0) + return ret; + /* ts output config */ + ret = si2165_writereg8(state, 0x04e4, 0x20); + if (ret < 0) + return ret; + ret = si2165_writereg16(state, 0x04ef, 0x00fe); + if (ret < 0) + return ret; + ret = si2165_writereg24(state, 0x04f4, 0x555555); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, 0x04e5, 0x01); + if (ret < 0) + return ret; + /* bandwidth in 10KHz steps */ + ret = si2165_writereg16(state, 0x0308, bw10k); + if (ret < 0) + return ret; + ret = si2165_set_oversamp(state, dvb_rate); + if (ret < 0) + return ret; + /* impulsive_noise_remover */ + ret = si2165_writereg8(state, 0x031c, 0x01); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, 0x00cb, 0x00); + if (ret < 0) + return ret; + /* agc2 */ + ret = si2165_writereg8(state, 0x016e, 0x41); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, 0x016c, 0x0e); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, 0x016d, 0x10); + if (ret < 0) + return ret; + /* agc */ + ret = si2165_writereg8(state, 0x015b, 0x03); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, 0x0150, 0x78); + if (ret < 0) + return ret; + /* agc */ + ret = si2165_writereg8(state, 0x01a0, 0x78); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, 0x01c8, 0x68); + if (ret < 0) + return ret; + /* freq_sync_range */ + ret = si2165_writereg16(state, 0x030c, 0x0064); + if (ret < 0) + return ret; + /* gp_reg0 */ + ret = si2165_readreg8(state, 0x0387, val); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, 0x0387, 0x00); + if (ret < 0) + return ret; + /* dsp_addr_jump */ + ret = si2165_writereg32(state, 0x0348, 0xf4000000); + if (ret < 0) + return ret; + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + /* recalc if_freq_shift if IF might has changed */ + fe->ops.tuner_ops.get_if_frequency(fe, &IF); + ret = si2165_set_if_freq_shift(state, IF); + if (ret < 0) + return ret; + + /* boot/wdog status */ + ret = si2165_readreg8(state, 0x0341, val); + if (ret < 0) + return ret; + ret = si2165_writereg8(state, 0x0341, 0x00); + if (ret < 0) + return ret; + /* reset all */ + ret = si2165_writereg8(state, 0x00c0, 0x00); + if (ret < 0) + return ret; + /* gp_reg0 */ + ret = si2165_writereg32(state, 0x0384, 0x00000000); + if (ret < 0) + return ret; + /* start_synchro */ + ret = si2165_writereg8(state, 0x02e0, 0x01); + if (ret < 0) + return ret; + /* boot/wdog status */ + ret = si2165_readreg8(state, 0x0341, val); + if (ret < 0) + return ret; + + return 0; +} + +static void si2165_release(struct dvb_frontend *fe) +{ + struct si2165_state *state = fe->demodulator_priv; + + dprintk("%s: called\n", __func__); + kfree(state); +} + +static struct dvb_frontend_ops si2165_ops = { + .info = { + .name = "Silicon Labs Si2165", + .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_TRANSMISSION_MODE_AUTO | + FE_CAN_RECOVER + }, + + .get_tune_settings = si2165_get_tune_settings, + + .init = si2165_init, + .sleep = si2165_sleep, + + .set_frontend = si2165_set_parameters, + .read_status = si2165_read_status, + + .release = si2165_release, +}; + +struct dvb_frontend *si2165_attach(const struct si2165_config *config, + struct i2c_adapter *i2c) +{ + struct si2165_state *state = NULL; + int n; + int io_ret; + u8 val; + + if (config == NULL || i2c == NULL) + goto error; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct si2165_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + state->config = *config; + + if (state->config.ref_freq_Hz < 4000000 + || state->config.ref_freq_Hz > 27000000) { + dev_err(&state->i2c->dev, "%s: ref_freq of %d Hz not supported by this driver\n", + KBUILD_MODNAME, state->config.ref_freq_Hz); + goto error; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &si2165_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + /* powerup */ + io_ret = si2165_writereg8(state, 0x0000, state->config.chip_mode); + if (io_ret < 0) + goto error; + + io_ret = si2165_readreg8(state, 0x0000, &val); + if (io_ret < 0) + goto error; + if (val != state->config.chip_mode) + goto error; + + io_ret = si2165_readreg8(state, 0x0023 , &state->revcode); + if (io_ret < 0) + goto error; + + io_ret = si2165_readreg8(state, 0x0118, &state->chip_type); + if (io_ret < 0) + goto error; + + /* powerdown */ + io_ret = si2165_writereg8(state, 0x0000, SI2165_MODE_OFF); + if (io_ret < 0) + goto error; + + dev_info(&state->i2c->dev, "%s: hardware revision 0x%02x, chip type 0x%02x\n", + KBUILD_MODNAME, state->revcode, state->chip_type); + + /* It is a guess that register 0x0118 (chip type?) can be used to + * differ between si2161, si2163 and si2165 + * Only si2165 has been tested. + */ + if (state->revcode == 0x03 && state->chip_type == 0x07) { + state->has_dvbt = true; + state->has_dvbc = true; + } else { + dev_err(&state->i2c->dev, "%s: Unsupported chip.\n", + KBUILD_MODNAME); + goto error; + } + + n = 0; + if (state->has_dvbt) { + state->frontend.ops.delsys[n++] = SYS_DVBT; + strlcat(state->frontend.ops.info.name, " DVB-T", + sizeof(state->frontend.ops.info.name)); + } + if (state->has_dvbc) + dev_warn(&state->i2c->dev, "%s: DVB-C is not yet supported.\n", + KBUILD_MODNAME); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(si2165_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Silicon Labs Si2165 DVB-C/-T Demodulator driver"); +MODULE_AUTHOR("Matthias Schwarzott "); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(SI2165_FIRMWARE); diff --git a/drivers/media/dvb-frontends/si2165.h b/drivers/media/dvb-frontends/si2165.h new file mode 100644 index 0000000..7a6b59d --- /dev/null +++ b/drivers/media/dvb-frontends/si2165.h @@ -0,0 +1,64 @@ +/* + Driver for Silicon Labs SI2165 DVB-C/-T Demodulator + + Copyright (C) 2013-2014 Matthias Schwarzott + + 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. + + References: + http://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf +*/ + +#ifndef _DVB_SI2165_H +#define _DVB_SI2165_H + +#include + +#if IS_ENABLED(CONFIG_DVB_SI2165) + +enum { + SI2165_MODE_OFF = 0x00, + SI2165_MODE_PLL_EXT = 0x20, + SI2165_MODE_PLL_XTAL = 0x21 +}; + +struct si2165_config { + /* i2c addr + * possible values: 0x64,0x65,0x66,0x67 */ + u8 i2c_addr; + + /* external clock or XTAL */ + u8 chip_mode; + + /* frequency of external clock or xtal in Hz + * possible values: 4000000, 16000000, 20000000, 240000000, 27000000 + */ + u32 ref_freq_Hz; + + /* invert the spectrum */ + bool inversion; +}; + +/* Addresses: 0x64,0x65,0x66,0x67 */ +struct dvb_frontend *si2165_attach( + const struct si2165_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *si2165_attach( + const struct si2165_config *config, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_SI2165 */ + +#endif /* _DVB_SI2165_H */ diff --git a/drivers/media/dvb-frontends/si2165_priv.h b/drivers/media/dvb-frontends/si2165_priv.h new file mode 100644 index 0000000..d4cc93f --- /dev/null +++ b/drivers/media/dvb-frontends/si2165_priv.h @@ -0,0 +1,23 @@ +/* + Driver for Silicon Labs SI2165 DVB-C/-T Demodulator + + Copyright (C) 2013-2014 Matthias Schwarzott + + 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 _DVB_SI2165_PRIV +#define _DVB_SI2165_PRIV + +#define SI2165_FIRMWARE "dvb-demod-si2165.fw" + +#endif /* _DVB_SI2165_PRIV */ -- cgit v0.10.2 From 36efec48e2e6016e05364906720a0ec350a5d768 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 22 Jul 2014 17:12:13 -0300 Subject: [media] cx23885: Add si2165 support for HVR-5500 The same card entry is used for HVR-4400 and HVR-5500. Only HVR-5500 has been tested. Signed-off-by: Matthias Schwarzott Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index 5158133..e12c006 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -31,6 +31,7 @@ config VIDEO_CX23885 select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 1d1550a..c2b6080 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -619,7 +619,12 @@ struct cx23885_board cx23885_boards[] = { }, [CX23885_BOARD_HAUPPAUGE_HVR4400] = { .name = "Hauppauge WinTV-HVR4400", + .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, /* 0xc0 >> 1 */ + .tuner_bus = 1, }, [CX23885_BOARD_AVERMEDIA_HC81R] = { .name = "AVerTV Hybrid Express Slim HC81R", @@ -1486,13 +1491,16 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) break; case CX23885_BOARD_HAUPPAUGE_HVR4400: /* GPIO-8 tda10071 demod reset */ + /* GPIO-9 si2165 demod reset */ /* Put the parts into reset and back */ - cx23885_gpio_enable(dev, GPIO_8, 1); - cx23885_gpio_clear(dev, GPIO_8); + cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1); + + cx23885_gpio_clear(dev, GPIO_8 | GPIO_9); mdelay(100); - cx23885_gpio_set(dev, GPIO_8); + cx23885_gpio_set(dev, GPIO_8 | GPIO_9); mdelay(100); + break; case CX23885_BOARD_AVERMEDIA_HC81R: cx_clear(MC417_CTL, 1); @@ -1839,6 +1847,9 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 6968706..968fecc 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -72,6 +72,7 @@ #include "tda10071.h" #include "a8293.h" #include "mb86a20s.h" +#include "si2165.h" static unsigned int debug; @@ -303,6 +304,11 @@ static struct tda18271_config hauppauge_hvr1210_tuner_config = { .output_opt = TDA18271_OUTPUT_LT_OFF, }; +static struct tda18271_config hauppauge_hvr4400_tuner_config = { + .gate = TDA18271_GATE_DIGITAL, + .output_opt = TDA18271_OUTPUT_LT_OFF, +}; + static struct tda18271_std_map hauppauge_hvr127x_std_map = { .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, .if_lvl = 1, .rfagc_top = 0x58 }, @@ -703,6 +709,12 @@ static const struct a8293_config hauppauge_a8293_config = { .i2c_addr = 0x0b, }; +static const struct si2165_config hauppauge_hvr4400_si2165_config = { + .i2c_addr = 0x64, + .chip_mode = SI2165_MODE_PLL_XTAL, + .ref_freq_Hz = 16000000, +}; + static int netup_altera_fpga_rw(void *device, int flag, int data, int read) { struct cx23885_dev *dev = (struct cx23885_dev *)device; @@ -1459,13 +1471,34 @@ static int dvb_register(struct cx23885_tsport *port) break; case CX23885_BOARD_HAUPPAUGE_HVR4400: i2c_bus = &dev->i2c_bus[0]; - fe0->dvb.frontend = dvb_attach(tda10071_attach, + i2c_bus2 = &dev->i2c_bus[1]; + switch (port->nr) { + /* port b */ + case 1: + fe0->dvb.frontend = dvb_attach(tda10071_attach, &hauppauge_tda10071_config, &i2c_bus->i2c_adap); - if (fe0->dvb.frontend != NULL) { - dvb_attach(a8293_attach, fe0->dvb.frontend, - &i2c_bus->i2c_adap, - &hauppauge_a8293_config); + if (fe0->dvb.frontend != NULL) { + if (!dvb_attach(a8293_attach, fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &hauppauge_a8293_config)) + goto frontend_detach; + } + break; + /* port c */ + case 2: + fe0->dvb.frontend = dvb_attach(si2165_attach, + &hauppauge_hvr4400_si2165_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + fe0->dvb.frontend->ops.i2c_gate_ctrl = 0; + if (!dvb_attach(tda18271_attach, + fe0->dvb.frontend, + 0x60, &i2c_bus2->i2c_adap, + &hauppauge_hvr4400_tuner_config)) + goto frontend_detach; + } + break; } break; default: -- cgit v0.10.2 From 6d3debafc8e718d5f3b2d87fe0387019b29b3bfa Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 22 Jul 2014 17:12:14 -0300 Subject: [media] cx231xx: prepare for i2c_client attachment This is needed to support PCTV QuatroStick 522e which uses a si2157. The si2157 driver is written using i2c_client attachment. Signed-off-by: Matthias Schwarzott Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index 4504bc6..5c69be7 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -67,6 +67,7 @@ struct cx231xx_dvb { struct dmx_frontend fe_hw; struct dmx_frontend fe_mem; struct dvb_net net; + struct i2c_client *i2c_client_tuner; }; static struct s5h1432_config dvico_s5h1432_config = { @@ -549,11 +550,18 @@ fail_adapter: static void unregister_dvb(struct cx231xx_dvb *dvb) { + struct i2c_client *client; dvb_net_release(&dvb->net); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); dvb_dmxdev_release(&dvb->dmxdev); dvb_dmx_release(&dvb->demux); + client = dvb->i2c_client_tuner; + /* remove I2C tuner */ + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } dvb_unregister_frontend(dvb->frontend); dvb_frontend_detach(dvb->frontend); dvb_unregister_adapter(&dvb->adapter); -- cgit v0.10.2 From dd2e7dd20cf482bc2fd989bfbd0354476ae904c2 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 22 Jul 2014 17:12:15 -0300 Subject: [media] cx231xx: Add digital support for HVR 930c-HD model 1113xx Add support for: [2040:b130] Hauppauge WinTV 930C-HD (model 1113xx) After loading the driver the first open to dvb device node fails. Signed-off-by: Matthias Schwarzott Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index f14c5e8..036454e 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -47,6 +47,7 @@ config VIDEO_CX231XX_DVB select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c index 89de00b..a428c10 100644 --- a/drivers/media/usb/cx231xx/cx231xx-avcore.c +++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -352,6 +352,7 @@ int cx231xx_afe_update_power_control(struct cx231xx *dev, case CX231XX_BOARD_CNXT_RDU_253S: case CX231XX_BOARD_CNXT_VIDEO_GRABBER: case CX231XX_BOARD_HAUPPAUGE_EXETER: + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: case CX231XX_BOARD_HAUPPAUGE_USBLIVE2: case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index db69307..5dd185e 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -704,6 +704,45 @@ struct cx231xx_board cx231xx_boards[] = { } }, }, + [CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx] = { + .name = "Hauppauge WinTV 930C-HD (1113xx)", + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x0e, + .norm = V4L2_STD_PAL, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, }; const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); @@ -733,6 +772,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC}, {USB_DEVICE(0x2040, 0xb120), .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, + {USB_DEVICE(0x2040, 0xb130), + .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx}, {USB_DEVICE(0x2040, 0xb140), .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, {USB_DEVICE(0x2040, 0xc200), @@ -886,6 +927,43 @@ static void cx231xx_config_tuner(struct cx231xx *dev) } +static int read_eeprom(struct cx231xx *dev, u8 *eedata, int len) +{ + int ret = 0; + u8 addr = 0xa0 >> 1; + u8 start_offset = 0; + int len_todo = len; + u8 *eedata_cur = eedata; + int i; + struct i2c_msg msg_write = { .addr = addr, .flags = 0, + .buf = &start_offset, .len = 1 }; + struct i2c_msg msg_read = { .addr = addr, .flags = I2C_M_RD }; + + /* mutex_lock(&dev->i2c_lock); */ + cx231xx_enable_i2c_port_3(dev, false); + + /* start reading at offset 0 */ + ret = i2c_transfer(&dev->i2c_bus[1].i2c_adap, &msg_write, 1); + + while (len_todo > 0) { + msg_read.len = (len_todo > 64) ? 64 : len_todo; + msg_read.buf = eedata_cur; + + ret = i2c_transfer(&dev->i2c_bus[1].i2c_adap, &msg_read, 1); + + eedata_cur += msg_read.len; + len_todo -= msg_read.len; + } + + cx231xx_enable_i2c_port_3(dev, true); + /* mutex_unlock(&dev->i2c_lock); */ + + for (i = 0; i + 15 < len; i += 16) + cx231xx_info("i2c eeprom %02x: %*ph\n", i, 16, &eedata[i]); + + return 0; +} + void cx231xx_card_setup(struct cx231xx *dev) { @@ -917,6 +995,20 @@ void cx231xx_card_setup(struct cx231xx *dev) else cx231xx_config_tuner(dev); } + + switch (dev->model) { + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: + { + struct tveeprom tvee; + static u8 eeprom[256]; + + read_eeprom(dev, eeprom, sizeof(eeprom)); + tveeprom_hauppauge_analog(&dev->i2c_bus[1].i2c_client, + &tvee, eeprom + 0xc0); + break; + } + } + } /* diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 4ba3ce0..513194a 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -726,6 +726,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1); break; case CX231XX_BOARD_HAUPPAUGE_EXETER: + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: errCode = cx231xx_set_power_mode(dev, POLARIS_AVMODE_DIGITAL); break; @@ -744,6 +745,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) case CX231XX_BOARD_CNXT_RDE_253S: case CX231XX_BOARD_CNXT_RDU_253S: case CX231XX_BOARD_HAUPPAUGE_EXETER: + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: @@ -1379,6 +1381,7 @@ int cx231xx_dev_init(struct cx231xx *dev) case CX231XX_BOARD_CNXT_RDE_253S: case CX231XX_BOARD_CNXT_RDU_253S: case CX231XX_BOARD_HAUPPAUGE_EXETER: + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index 5c69be7..4ff6f7f 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -32,6 +32,7 @@ #include "tda18271.h" #include "s5h1411.h" #include "lgdt3305.h" +#include "si2165.h" #include "mb86a20s.h" MODULE_DESCRIPTION("driver for cx231xx based DVB cards"); @@ -152,6 +153,12 @@ static struct tda18271_config pv_tda18271_config = { .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, }; +static const struct si2165_config hauppauge_930C_HD_1113xx_si2165_config = { + .i2c_addr = 0x64, + .chip_mode = SI2165_MODE_PLL_XTAL, + .ref_freq_Hz = 16000000, +}; + static inline void print_err_status(struct cx231xx *dev, int packet, int status) { char *errmsg = "Unknown"; @@ -712,6 +719,33 @@ static int dvb_init(struct cx231xx *dev) &hcw_tda18271_config); break; + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: + + dev->dvb->frontend = dvb_attach(si2165_attach, + &hauppauge_930C_HD_1113xx_si2165_config, + &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap + ); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach SI2165 front end\n"); + result = -EINVAL; + goto out_free; + } + + dev->dvb->frontend->ops.i2c_gate_ctrl = 0; + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + dvb_attach(tda18271_attach, dev->dvb->frontend, + 0x60, + &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, + &hcw_tda18271_config); + + dev->cx231xx_reset_analog_tuner = NULL; + break; + case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID: diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index babca7f..a6373ba 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -73,6 +73,7 @@ #define CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2 16 #define CX231XX_BOARD_OTG102 17 #define CX231XX_BOARD_KWORLD_UB445_USB_HYBRID 18 +#define CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx 19 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 -- cgit v0.10.2 From 9e49f7c3d67c4db4911d3a62c39fb44b82feaaf7 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 22 Jul 2014 17:12:16 -0300 Subject: [media] cx231xx: Add digital support for HVR930C-HD model 1114xx Add support for: [2040:b131] Hauppauge WinTV 930C-HD (model 1114xx) Signed-off-by: Matthias Schwarzott Tested-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 036454e..569aa29 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -48,6 +48,7 @@ config VIDEO_CX231XX_DVB select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 5dd185e..3953a19 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -743,6 +743,45 @@ struct cx231xx_board cx231xx_boards[] = { .gpio = NULL, } }, }, + [CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx] = { + .name = "Hauppauge WinTV 930C-HD (1114xx)", + .tuner_type = TUNER_ABSENT, + .tuner_addr = 0x60, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x0e, + .norm = V4L2_STD_PAL, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, }; const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); @@ -774,6 +813,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, {USB_DEVICE(0x2040, 0xb130), .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx}, + {USB_DEVICE(0x2040, 0xb131), + .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx}, {USB_DEVICE(0x2040, 0xb140), .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, {USB_DEVICE(0x2040, 0xc200), @@ -998,6 +1039,7 @@ void cx231xx_card_setup(struct cx231xx *dev) switch (dev->model) { case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: { struct tveeprom tvee; static u8 eeprom[256]; diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index 4ff6f7f..1fa7974 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -34,6 +34,7 @@ #include "lgdt3305.h" #include "si2165.h" #include "mb86a20s.h" +#include "si2157.h" MODULE_DESCRIPTION("driver for cx231xx based DVB cards"); MODULE_AUTHOR("Srinivasa Deevi "); @@ -159,6 +160,12 @@ static const struct si2165_config hauppauge_930C_HD_1113xx_si2165_config = { .ref_freq_Hz = 16000000, }; +static const struct si2165_config pctv_quatro_stick_1114xx_si2165_config = { + .i2c_addr = 0x64, + .chip_mode = SI2165_MODE_PLL_EXT, + .ref_freq_Hz = 24000000, +}; + static inline void print_err_status(struct cx231xx *dev, int packet, int status) { char *errmsg = "Unknown"; @@ -746,6 +753,62 @@ static int dvb_init(struct cx231xx *dev) dev->cx231xx_reset_analog_tuner = NULL; break; + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: + { + struct i2c_client *client; + struct i2c_board_info info; + struct si2157_config si2157_config; + + memset(&info, 0, sizeof(struct i2c_board_info)); + + dev->dvb->frontend = dvb_attach(si2165_attach, + &pctv_quatro_stick_1114xx_si2165_config, + &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap + ); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach SI2165 front end\n"); + result = -EINVAL; + goto out_free; + } + + dev->dvb->frontend->ops.i2c_gate_ctrl = 0; + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = dev->dvb->frontend; + si2157_config.inversion = true; + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module("si2157"); + + client = i2c_new_device( + &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap, + &info); + if (client == NULL || client->dev.driver == NULL) { + dvb_frontend_detach(dev->dvb->frontend); + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + dvb_frontend_detach(dev->dvb->frontend); + result = -ENODEV; + goto out_free; + } + + dev->cx231xx_reset_analog_tuner = NULL; + + dev->dvb->i2c_client_tuner = client; + break; + } + case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID: diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index a6373ba..aeb1bf4 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -74,6 +74,7 @@ #define CX231XX_BOARD_OTG102 17 #define CX231XX_BOARD_KWORLD_UB445_USB_HYBRID 18 #define CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx 19 +#define CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx 20 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 -- cgit v0.10.2 From a141a887e07838972a47355b82713a21e84b1343 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 22 Jul 2014 17:12:17 -0300 Subject: [media] cx231xx: Add support for PCTV QuatroStick 521e Add support for: [2013:0259] PCTV QuatroStick 521e The hardware is identical to Hauppauge WinTV 930C-HD (model 1113xx) Signed-off-by: Matthias Schwarzott Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 3953a19..40fb645 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -705,7 +705,7 @@ struct cx231xx_board cx231xx_boards[] = { }, }, [CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx] = { - .name = "Hauppauge WinTV 930C-HD (1113xx)", + .name = "Hauppauge WinTV 930C-HD (1113xx) / PCTV QuatroStick 521e", .tuner_type = TUNER_NXP_TDA18271, .tuner_addr = 0x60, .tuner_gpio = RDE250_XCV_TUNER, @@ -819,6 +819,9 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, {USB_DEVICE(0x2040, 0xc200), .driver_info = CX231XX_BOARD_HAUPPAUGE_USBLIVE2}, + /* PCTV QuatroStick 521e */ + {USB_DEVICE(0x2013, 0x0259), + .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx}, {USB_DEVICE_VER(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD, 0x4000, 0x4001), .driver_info = CX231XX_BOARD_PV_PLAYTV_USB_HYBRID}, {USB_DEVICE(USB_VID_PIXELVIEW, 0x5014), -- cgit v0.10.2 From 140a7987dbe4c5bbb6bb623373216ee6122fe8bf Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 22 Jul 2014 17:12:18 -0300 Subject: [media] cx231xx: Add support for PCTV QuatroStick 522e Add support for: [2013:025e] PCTV QuatroStick 522e The hardware is identical to Hauppauge WinTV 930C-HD (model 1114xx) Signed-off-by: Matthias Schwarzott Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 40fb645..5812c63 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -744,7 +744,7 @@ struct cx231xx_board cx231xx_boards[] = { } }, }, [CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx] = { - .name = "Hauppauge WinTV 930C-HD (1114xx)", + .name = "Hauppauge WinTV 930C-HD (1114xx) / PCTV QuatroStick 522e", .tuner_type = TUNER_ABSENT, .tuner_addr = 0x60, .tuner_gpio = RDE250_XCV_TUNER, @@ -822,6 +822,9 @@ struct usb_device_id cx231xx_id_table[] = { /* PCTV QuatroStick 521e */ {USB_DEVICE(0x2013, 0x0259), .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx}, + /* PCTV QuatroStick 522e */ + {USB_DEVICE(0x2013, 0x025e), + .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx}, {USB_DEVICE_VER(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD, 0x4000, 0x4001), .driver_info = CX231XX_BOARD_PV_PLAYTV_USB_HYBRID}, {USB_DEVICE(USB_VID_PIXELVIEW, 0x5014), -- cgit v0.10.2 From 1f97947c15a8d2bc21917463eb72d7f8685e3a86 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 27 Jul 2014 16:27:30 -0300 Subject: [media] cx231xx: handle errors at read_eeprom() Fix the following warnings: drivers/media/usb/cx231xx/cx231xx-cards.c: In function 'read_eeprom': drivers/media/usb/cx231xx/cx231xx-cards.c:979:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 5812c63..8039b76 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -991,13 +991,20 @@ static int read_eeprom(struct cx231xx *dev, u8 *eedata, int len) /* start reading at offset 0 */ ret = i2c_transfer(&dev->i2c_bus[1].i2c_adap, &msg_write, 1); + if (ret < 0) { + cx231xx_err("Can't read eeprom\n"); + return ret; + } while (len_todo > 0) { msg_read.len = (len_todo > 64) ? 64 : len_todo; msg_read.buf = eedata_cur; ret = i2c_transfer(&dev->i2c_bus[1].i2c_adap, &msg_read, 1); - + if (ret < 0) { + cx231xx_err("Can't read eeprom\n"); + return ret; + } eedata_cur += msg_read.len; len_todo -= msg_read.len; } -- cgit v0.10.2 From 9683e01effc9ab3eb3c4584a39a6b87dadf5f391 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 27 Jul 2014 17:06:02 -0300 Subject: [media] mceusb: add support for newer cx231xx devices Add support for the si2165-based cx231xx devices: [2013:025e] PCTV QuatroStick 522e [2013:0259] PCTV QuatroStick 521e [2040:b131] Hauppauge WinTV 930C-HD (model 1114xx) They're similar to the already supported: [2040:b130] Hauppauge WinTV 930C-HD (model 1113xx) Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index b1be81f..48a6a08 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -187,6 +187,7 @@ #define VENDOR_CONEXANT 0x0572 #define VENDOR_TWISTEDMELON 0x2596 #define VENDOR_HAUPPAUGE 0x2040 +#define VENDOR_PCTV 0x2013 enum mceusb_model_type { MCE_GEN2 = 0, /* Most boards */ @@ -396,6 +397,13 @@ static struct usb_device_id mceusb_dev_table[] = { /* Hauppauge WINTV-HVR-HVR 930C-HD - based on cx231xx */ { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb130), .driver_info = HAUPPAUGE_CX_HYBRID_TV }, + { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb131), + .driver_info = HAUPPAUGE_CX_HYBRID_TV }, + { USB_DEVICE(VENDOR_PCTV, 0x0259), + .driver_info = HAUPPAUGE_CX_HYBRID_TV }, + { USB_DEVICE(VENDOR_PCTV, 0x025e), + .driver_info = HAUPPAUGE_CX_HYBRID_TV }, + /* Terminating entry */ { } }; -- cgit v0.10.2 From 5b8c8d41919ac4985dfacd31a860e4f00f52d3a6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 27 Jul 2014 17:28:48 -0300 Subject: [media] mceusb: select default keytable based on vendor Some vendors have their on keymap table that are used on all (or almost all) models for that vendor. So, instead of specifying the keymap table per USB ID, let's use the Vendor ID's table by default. At the end, this will mean less code to be added when newer devices for those vendors are added. Of course, if rc_map is specified per board, it takes precedence. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 48a6a08..45b0894 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -241,7 +241,6 @@ static const struct mceusb_model mceusb_model[] = { * remotes, but we should have something handy, * to allow testing it */ - .rc_map = RC_MAP_HAUPPAUGE, .name = "Conexant Hybrid TV (cx231xx) MCE IR", }, [CX_HYBRID_TV] = { @@ -249,7 +248,6 @@ static const struct mceusb_model mceusb_model[] = { .name = "Conexant Hybrid TV (cx231xx) MCE IR", }, [HAUPPAUGE_CX_HYBRID_TV] = { - .rc_map = RC_MAP_HAUPPAUGE, .no_tx = 1, /* eeprom says it has no tx */ .name = "Conexant Hybrid TV (cx231xx) MCE IR no TX", }, @@ -1200,8 +1198,10 @@ static void mceusb_flash_led(struct mceusb_dev *ir) mce_async_out(ir, FLASH_LED, sizeof(FLASH_LED)); } -static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) +static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir, + struct usb_interface *intf) { + struct usb_device *udev = usb_get_dev(interface_to_usbdev(intf)); struct device *dev = ir->dev; struct rc_dev *rc; int ret; @@ -1235,8 +1235,19 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) rc->tx_ir = mceusb_tx_ir; } rc->driver_name = DRIVER_NAME; - rc->map_name = mceusb_model[ir->model].rc_map ? - mceusb_model[ir->model].rc_map : RC_MAP_RC6_MCE; + + switch (le16_to_cpu(udev->descriptor.idVendor)) { + case VENDOR_HAUPPAUGE: + rc->map_name = RC_MAP_HAUPPAUGE; + break; + case VENDOR_PCTV: + rc->map_name = RC_MAP_PINNACLE_PCTV_HD; + break; + default: + rc->map_name = RC_MAP_RC6_MCE; + } + if (mceusb_model[ir->model].rc_map) + rc->map_name = mceusb_model[ir->model].rc_map; ret = rc_register_device(rc); if (ret < 0) { @@ -1351,7 +1362,7 @@ static int mceusb_dev_probe(struct usb_interface *intf, snprintf(name + strlen(name), sizeof(name) - strlen(name), " %s", buf); - ir->rc = mceusb_init_rc_dev(ir); + ir->rc = mceusb_init_rc_dev(ir, intf); if (!ir->rc) goto rc_dev_fail; -- cgit v0.10.2 From 9c9cb1fad865b3d9e9c4d7bbfbd20ca04cdc79b3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 26 Jul 2014 13:02:59 -0300 Subject: [media] v4l2-ctrls: fix rounding calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 958c7c7e65 ("[media] v4l2-ctrls: fix corner case in round-to-range code") broke controls that use a negative range. The cause was a s32/u32 mixup: ctrl->step is unsigned while all others are signed. So the result type of the expression '(ctrl)->maximum - ((ctrl)->step / 2)' became unsigned, making 'val >= (ctrl)->maximum - ((ctrl)->step / 2)' true, since '((u32)-128) > 128' (if val = -128, maximum = 128 and step = 1). So carefully cast (step / 2) to s32. There was one cast of step to s32 where it should have been u32 because both offset and step are unsigned, so casting to signed makes no sense there. You do need a cast to u32 there, because otherwise architectures that have no 64-bit division start complaining (step is a u64). Signed-off-by: Hans Verkuil Reported-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 2d8ced8..f030d6a 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1347,14 +1347,14 @@ static void std_log(const struct v4l2_ctrl *ctrl) ({ \ offset_type offset; \ if ((ctrl)->maximum >= 0 && \ - val >= (ctrl)->maximum - ((ctrl)->step / 2)) \ + val >= (ctrl)->maximum - (s32)((ctrl)->step / 2)) \ val = (ctrl)->maximum; \ else \ - val += (ctrl)->step / 2; \ + val += (s32)((ctrl)->step / 2); \ val = clamp_t(typeof(val), val, \ (ctrl)->minimum, (ctrl)->maximum); \ offset = (val) - (ctrl)->minimum; \ - offset = (ctrl)->step * (offset / (s32)(ctrl)->step); \ + offset = (ctrl)->step * (offset / (u32)(ctrl)->step); \ val = (ctrl)->minimum + offset; \ 0; \ }) @@ -1376,10 +1376,10 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, * the u64 divide that needs special care. */ val = ptr.p_s64[idx]; - if (ctrl->maximum >= 0 && val >= ctrl->maximum - ctrl->step / 2) + if (ctrl->maximum >= 0 && val >= ctrl->maximum - (s64)(ctrl->step / 2)) val = ctrl->maximum; else - val += ctrl->step / 2; + val += (s64)(ctrl->step / 2); val = clamp_t(s64, val, ctrl->minimum, ctrl->maximum); offset = val - ctrl->minimum; do_div(offset, ctrl->step); -- cgit v0.10.2 From 0a12830893e8b111189e9019848ead054b0f85b3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 25 Jul 2014 08:19:54 -0300 Subject: [media] solo6x10: fix potential null dereference drivers/media/pci/solo6x10/solo6x10-disp.c:221 solo_set_motion_block() error: potential null dereference 'buf'. (kzalloc returns null) Also propagate this error up the chain. Signed-off-by: Hans Verkuil Reported-by: kbuild test robot Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/solo6x10/solo6x10-disp.c b/drivers/media/pci/solo6x10/solo6x10-disp.c index ed88ab4..5ea9cac 100644 --- a/drivers/media/pci/solo6x10/solo6x10-disp.c +++ b/drivers/media/pci/solo6x10/solo6x10-disp.c @@ -216,6 +216,8 @@ int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, int ret = 0; buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; for (y = 0; y < SOLO_MOTION_SZ; y++) { for (x = 0; x < SOLO_MOTION_SZ; x++) buf[x] = cpu_to_le16(thresholds[y * SOLO_MOTION_SZ + x]); diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index 2e07b49..28023f9 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -1137,11 +1137,13 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl) solo_enc->motion_enabled = ctrl->val > V4L2_DETECT_MD_MODE_DISABLED; if (ctrl->val) { if (solo_enc->motion_global) - solo_set_motion_threshold(solo_dev, solo_enc->ch, + err = solo_set_motion_threshold(solo_dev, solo_enc->ch, solo_enc->motion_thresh); else - solo_set_motion_block(solo_dev, solo_enc->ch, + err = solo_set_motion_block(solo_dev, solo_enc->ch, solo_enc->md_thresholds->p_cur.p_u16); + if (err) + return err; } solo_motion_toggle(solo_enc, ctrl->val); return 0; @@ -1152,8 +1154,7 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_OSD_TEXT: strcpy(solo_enc->osd_text, ctrl->p_new.p_char); - err = solo_osd_print(solo_enc); - return err; + return solo_osd_print(solo_enc); default: return -EINVAL; } -- cgit v0.10.2 From 7f196789b3ffee243b681d3e7dab8890038db856 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 28 Jul 2014 10:34:15 -0300 Subject: si2135: Declare the structs even if frontend is not enabled As reported by Kbuildtest: In file included from drivers/media/usb/cx231xx/cx231xx-dvb.c:35:0: drivers/media/dvb-frontends/si2165.h:57:9: warning: 'struct si2165_config' declared inside parameter list [enabled by default] struct i2c_adapter *i2c) ^ drivers/media/dvb-frontends/si2165.h:57:9: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default] drivers/media/usb/cx231xx/cx231xx-dvb.c:157:21: error: variable 'hauppauge_930C_HD_1113xx_si2165_config' has initializer but incomplete type static const struct si2165_config hauppauge_930C_HD_1113xx_si2165_config = { ^ drivers/media/usb/cx231xx/cx231xx-dvb.c:158:2: error: unknown field 'i2c_addr' specified in initializer .i2c_addr = 0x64, ^ drivers/media/usb/cx231xx/cx231xx-dvb.c:158:2: warning: excess elements in struct initializer [enabled by default] drivers/media/usb/cx231xx/cx231xx-dvb.c:158:2: warning: (near initialization for 'hauppauge_930C_HD_1113xx_si2165_config') [enabled by default] drivers/media/usb/cx231xx/cx231xx-dvb.c:159:2: error: unknown field 'chip_mode' specified in initializer .chip_mode = SI2165_MODE_PLL_XTAL, ^ drivers/media/usb/cx231xx/cx231xx-dvb.c:159:15: error: 'SI2165_MODE_PLL_XTAL' undeclared here (not in a function) .chip_mode = SI2165_MODE_PLL_XTAL, ^ drivers/media/usb/cx231xx/cx231xx-dvb.c:159:2: warning: excess elements in struct initializer [enabled by default] .chip_mode = SI2165_MODE_PLL_XTAL, ^ drivers/media/usb/cx231xx/cx231xx-dvb.c:159:2: warning: (near initialization for 'hauppauge_930C_HD_1113xx_si2165_config') [enabled by default] drivers/media/usb/cx231xx/cx231xx-dvb.c:160:2: error: unknown field 'ref_freq_Hz' specified in initializer .ref_freq_Hz = 16000000, ^ drivers/media/usb/cx231xx/cx231xx-dvb.c:160:2: warning: excess elements in struct initializer [enabled by default] drivers/media/usb/cx231xx/cx231xx-dvb.c:160:2: warning: (near initialization for 'hauppauge_930C_HD_1113xx_si2165_config') [enabled by default] >> drivers/media/usb/cx231xx/cx231xx-dvb.c:163:21: error: variable 'pctv_quatro_stick_1114xx_si2165_config' has initializer but incomplete type static const struct si2165_config pctv_quatro_stick_1114xx_si2165_config = { ^ drivers/media/usb/cx231xx/cx231xx-dvb.c:164:2: error: unknown field 'i2c_addr' specified in initializer .i2c_addr = 0x64, ^ drivers/media/usb/cx231xx/cx231xx-dvb.c:164:2: warning: excess elements in struct initializer [enabled by default] >> drivers/media/usb/cx231xx/cx231xx-dvb.c:164:2: warning: (near initialization for 'pctv_quatro_stick_1114xx_si2165_config') [enabled by default] drivers/media/usb/cx231xx/cx231xx-dvb.c:165:2: error: unknown field 'chip_mode' specified in initializer .chip_mode = SI2165_MODE_PLL_EXT, ^ >> drivers/media/usb/cx231xx/cx231xx-dvb.c:165:15: error: 'SI2165_MODE_PLL_EXT' undeclared here (not in a function) .chip_mode = SI2165_MODE_PLL_EXT, ^ drivers/media/usb/cx231xx/cx231xx-dvb.c:165:2: warning: excess elements in struct initializer [enabled by default] .chip_mode = SI2165_MODE_PLL_EXT, ^ >> drivers/media/usb/cx231xx/cx231xx-dvb.c:165:2: warning: (near initialization for 'pctv_quatro_stick_1114xx_si2165_config') [enabled by default] drivers/media/usb/cx231xx/cx231xx-dvb.c:166:2: error: unknown field 'ref_freq_Hz' specified in initializer .ref_freq_Hz = 24000000, ^ drivers/media/usb/cx231xx/cx231xx-dvb.c:166:2: warning: excess elements in struct initializer [enabled by default] >> drivers/media/usb/cx231xx/cx231xx-dvb.c:166:2: warning: (near initialization for 'pctv_quatro_stick_1114xx_si2165_config') [enabled by default] drivers/media/usb/cx231xx/cx231xx-dvb.c: In function 'dvb_init': drivers/media/usb/cx231xx/cx231xx-dvb.c:731:3: warning: passing argument 1 of 'si2165_attach' from incompatible pointer type [enabled by default] dev->dvb->frontend = dvb_attach(si2165_attach, ^ In file included from drivers/media/usb/cx231xx/cx231xx-dvb.c:35:0: drivers/media/dvb-frontends/si2165.h:55:36: note: expected 'const struct si2165_config *' but argument is of type 'const struct si2165_config *' static inline struct dvb_frontend *si2165_attach( ^ drivers/media/usb/cx231xx/cx231xx-dvb.c:764:3: warning: passing argument 1 of 'si2165_attach' from incompatible pointer type [enabled by default] dev->dvb->frontend = dvb_attach(si2165_attach, ^ In file included from drivers/media/usb/cx231xx/cx231xx-dvb.c:35:0: drivers/media/dvb-frontends/si2165.h:55:36: note: expected 'const struct si2165_config *' but argument is of type 'const struct si2165_config *' static inline struct dvb_frontend *si2165_attach( ^ That happens because the frontend was disabled by .config, but the si2165_attach void stub require those structs, and also the drivers that call it. While here, remove the duplicated info about the possible I2C addresses. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/si2165.h b/drivers/media/dvb-frontends/si2165.h index 7a6b59d..efaa081 100644 --- a/drivers/media/dvb-frontends/si2165.h +++ b/drivers/media/dvb-frontends/si2165.h @@ -22,8 +22,6 @@ #include -#if IS_ENABLED(CONFIG_DVB_SI2165) - enum { SI2165_MODE_OFF = 0x00, SI2165_MODE_PLL_EXT = 0x20, @@ -47,7 +45,7 @@ struct si2165_config { bool inversion; }; -/* Addresses: 0x64,0x65,0x66,0x67 */ +#if IS_ENABLED(CONFIG_DVB_SI2165) struct dvb_frontend *si2165_attach( const struct si2165_config *config, struct i2c_adapter *i2c); -- cgit v0.10.2 From cfd0c77d0830d33288e9da17aa10db3a61de601a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 25 Jul 2014 14:02:14 -0300 Subject: [media] au0828: improve I2C speed Commits 21dc61d3c0a4 and 7a1dd50b89d4 reduced the board I2C speed to 20 MHz by default, due to a I2C stretch issue: while xc5000 uses i2c stretch when a command is sent to it, au0828 doesn't support this feature. However, this is needed only for Xceive tuners. The other I2C devices can work at the max speed. So, revert the workarounds at board level, handling it at I2C level, only when talking with xc5000. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 7fdadf9..3a79240 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -46,7 +46,7 @@ struct au0828_board au0828_boards[] = { .name = "Hauppauge HVR850", .tuner_type = TUNER_XC5000, .tuner_addr = 0x61, - .i2c_clk_divider = AU0828_I2C_CLK_20KHZ, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, .input = { { .type = AU0828_VMUX_TELEVISION, @@ -77,7 +77,7 @@ struct au0828_board au0828_boards[] = { stretch fits inside of a normal clock cycle, or else the au0828 fails to set the STOP bit. A 30 KHz clock puts the clock pulse width at 18us */ - .i2c_clk_divider = AU0828_I2C_CLK_20KHZ, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, .input = { { .type = AU0828_VMUX_TELEVISION, @@ -108,7 +108,7 @@ struct au0828_board au0828_boards[] = { .name = "DViCO FusionHDTV USB", .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .i2c_clk_divider = AU0828_I2C_CLK_20KHZ, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, }, [AU0828_BOARD_HAUPPAUGE_WOODBURY] = { .name = "Hauppauge Woodbury", diff --git a/drivers/media/usb/au0828/au0828-i2c.c b/drivers/media/usb/au0828/au0828-i2c.c index 17ec3651..daaeaf1 100644 --- a/drivers/media/usb/au0828/au0828-i2c.c +++ b/drivers/media/usb/au0828/au0828-i2c.c @@ -141,25 +141,27 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap, { int i, strobe = 0; struct au0828_dev *dev = i2c_adap->algo_data; + u8 i2c_speed = dev->board.i2c_clk_divider; dprintk(4, "%s()\n", __func__); au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01); - /* Set the I2C clock */ if (((dev->board.tuner_type == TUNER_XC5000) || (dev->board.tuner_type == TUNER_XC5000C)) && - (dev->board.tuner_addr == msg->addr) && - (msg->len == 64)) { - /* Hack to speed up firmware load. The xc5000 lets us do up - to 400 KHz when in firmware download mode */ - au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, - AU0828_I2C_CLK_250KHZ); - } else { - /* Use the i2c clock speed in the board configuration */ - au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, - dev->board.i2c_clk_divider); + (dev->board.tuner_addr == msg->addr)) { + /* + * Due to I2C clock stretch, we need to use a lower speed + * on xc5000 for commands. However, firmware transfer can + * speed up to 400 KHz. + */ + if (msg->len == 64) + i2c_speed = AU0828_I2C_CLK_250KHZ; + else + i2c_speed = AU0828_I2C_CLK_20KHZ; } + /* Set the I2C clock */ + au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, i2c_speed); /* Hardware needs 8 bit addresses */ au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1); @@ -228,15 +230,24 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap, const struct i2c_msg *msg, int joined) { struct au0828_dev *dev = i2c_adap->algo_data; + u8 i2c_speed = dev->board.i2c_clk_divider; int i; dprintk(4, "%s()\n", __func__); au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01); + /* + * Due to xc5000c clock stretch, we cannot use full speed at + * readings from xc5000, as otherwise they'll fail. + */ + if (((dev->board.tuner_type == TUNER_XC5000) || + (dev->board.tuner_type == TUNER_XC5000C)) && + (dev->board.tuner_addr == msg->addr)) + i2c_speed = AU0828_I2C_CLK_20KHZ; + /* Set the I2C clock */ - au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, - dev->board.i2c_clk_divider); + au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, i2c_speed); /* Hardware needs 8 bit addresses */ au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1); -- cgit v0.10.2 From d24b69fc6b0aca41a205b2b5abaf3f243ad7d48b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 28 Jul 2014 14:25:28 -0300 Subject: [media] rc-main: allow raw protocol drivers to restrict the allowed protos On some hardware (au0828/au8522), the hardware is broken with regards to the initial pulse detection. So, the driver needs to produce a fake start pulse. That limits the acceptable protocols, as it is not possible to produce a fake pulse that would cover all supported protocols. So, allow the driver to explicitly set the allowed protocols. If the driver doesn't specify, keep the old behavior. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index dfceeb4..a7991c7 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -859,10 +859,9 @@ static ssize_t show_protocols(struct device *device, if (fattr->type == RC_FILTER_NORMAL) { enabled = dev->enabled_protocols; - if (dev->raw) + allowed = dev->allowed_protocols; + if (dev->raw && !allowed) allowed = ir_raw_get_allowed_protocols(); - else - allowed = dev->allowed_protocols; } else { enabled = dev->enabled_wakeup_protocols; allowed = dev->allowed_wakeup_protocols; -- cgit v0.10.2 From 32570579cbde878580b621a846b18d04db856286 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 30 Jul 2014 15:49:16 -0300 Subject: [media] ir-rc5-decoder: print where decoding fails The IR dprintk message that prints a RC5 decoding failure is not much helpful, as it doesn't tell on what bit the error occurred. Add such information, in order to make the debug message more useful. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 93168da..2ef7639 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -178,8 +178,8 @@ again: } out: - IR_dprintk(1, "RC5(x/sz) decode failed at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + IR_dprintk(1, "RC5(x/sz) decode failed at state %i count %d (%uus %s)\n", + data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } -- cgit v0.10.2 From 2fcfd317f66c6e6cf0e05351e7641898d767dfa4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 24 Jul 2014 22:49:02 -0300 Subject: [media] au0828: add support for IR on HVR-950Q HVR-950Q uses an I2C remote controller at address 0x47 (7-bits notation). Add support for it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index fe48403..1d410ac 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -27,3 +27,10 @@ config VIDEO_AU0828_V4L2 Choose Y here to include support for v4l2 analog video capture within the au0828 driver. + +config VIDEO_AU0828_RC + bool "AU0828 Remote Controller support" + depends on RC_CORE + depends on VIDEO_AU0828 + ---help--- + Enables Remote Controller support on au0828 driver. diff --git a/drivers/media/usb/au0828/Makefile b/drivers/media/usb/au0828/Makefile index be3bdf6..3dc7539 100644 --- a/drivers/media/usb/au0828/Makefile +++ b/drivers/media/usb/au0828/Makefile @@ -4,6 +4,10 @@ ifeq ($(CONFIG_VIDEO_AU0828_V4L2),y) au0828-objs += au0828-video.o au0828-vbi.o endif +ifeq ($(CONFIG_VIDEO_AU0828_RC),y) + au0828-objs += au0828-input.o +endif + obj-$(CONFIG_VIDEO_AU0828) += au0828.o ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 3a79240..2c6b7da 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -71,6 +71,7 @@ struct au0828_board au0828_boards[] = { .name = "Hauppauge HVR950Q", .tuner_type = TUNER_XC5000, .tuner_addr = 0x61, + .has_ir_i2c = 1, /* The au0828 hardware i2c implementation does not properly support the xc5000's i2c clock stretching. So we need to lower the clock frequency enough where the 15us clock diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index ab45a6f..56025e6 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -32,10 +32,12 @@ * 2 = USB handling * 4 = I2C related * 8 = Bridge related + * 16 = IR related */ int au0828_debug; module_param_named(debug, au0828_debug, int, 0644); -MODULE_PARM_DESC(debug, "enable debug messages"); +MODULE_PARM_DESC(debug, + "set debug bitmask: 1=general, 2=USB, 4=I2C, 8=bridge, 16=IR"); static unsigned int disable_usb_speed_check; module_param(disable_usb_speed_check, int, 0444); @@ -151,6 +153,9 @@ static void au0828_usb_disconnect(struct usb_interface *interface) dprintk(1, "%s()\n", __func__); +#ifdef CONFIG_VIDEO_AU0828_RC + au0828_rc_unregister(dev); +#endif /* Digital TV */ au0828_dvb_unregister(dev); @@ -261,9 +266,15 @@ static int au0828_usb_probe(struct usb_interface *interface, pr_err("%s() au0282_dev_register failed\n", __func__); +#ifdef CONFIG_VIDEO_AU0828_RC + /* Remote controller */ + au0828_rc_register(dev); +#endif - /* Store the pointer to the au0828_dev so it can be accessed in - au0828_usb_disconnect */ + /* + * Store the pointer to the au0828_dev so it can be accessed in + * au0828_usb_disconnect + */ usb_set_intfdata(interface, dev); printk(KERN_INFO "Registered device AU0828 [%s]\n", @@ -279,6 +290,8 @@ static struct usb_driver au0828_usb_driver = { .probe = au0828_usb_probe, .disconnect = au0828_usb_disconnect, .id_table = au0828_usb_id_table, + + /* FIXME: Add suspend and resume functions */ }; static int __init au0828_init(void) @@ -298,6 +311,10 @@ static int __init au0828_init(void) printk(KERN_INFO "%s() Bridge Debugging is enabled\n", __func__); + if (au0828_debug & 16) + printk(KERN_INFO "%s() IR Debugging is enabled\n", + __func__); + printk(KERN_INFO "au0828 driver loaded\n"); ret = usb_register(&au0828_usb_driver); @@ -318,4 +335,4 @@ module_exit(au0828_exit); MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products"); MODULE_AUTHOR("Steven Toth "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.2"); +MODULE_VERSION("0.0.3"); diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c new file mode 100644 index 0000000..fd0d3a90 --- /dev/null +++ b/drivers/media/usb/au0828/au0828-input.c @@ -0,0 +1,386 @@ +/* + handle au0828 IR remotes via linux kernel input layer. + + Copyright (C) 2014 Mauro Carvalho Chehab + Copyright (c) 2014 Samsung Electronics Co., Ltd. + + Based on em28xx-input.c. + + 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 +#include +#include +#include +#include +#include +#include + +#include "au0828.h" + +struct au0828_rc { + struct au0828_dev *dev; + struct rc_dev *rc; + char name[32]; + char phys[32]; + + /* poll decoder */ + int polling; + struct delayed_work work; + + /* i2c slave address of external device (if used) */ + u16 i2c_dev_addr; + + int (*get_key_i2c)(struct au0828_rc *ir); +}; + +/* + * AU8522 has a builtin IR receiver. Add functions to get IR from it + */ + +static int au8522_rc_write(struct au0828_rc *ir, u16 reg, u8 data) +{ + int rc; + char buf[] = { (reg >> 8) | 0x80, reg & 0xff, data }; + struct i2c_msg msg = { .addr = ir->i2c_dev_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + + rc = i2c_transfer(ir->dev->i2c_client.adapter, &msg, 1); + + if (rc < 0) + return rc; + + return (rc == 1) ? 0 : -EIO; +} + +static int au8522_rc_read(struct au0828_rc *ir, u16 reg, int val, + char *buf, int size) +{ + int rc; + char obuf[3]; + struct i2c_msg msg[2] = { { .addr = ir->i2c_dev_addr, .flags = 0, + .buf = obuf, .len = 2 }, + { .addr = ir->i2c_dev_addr, .flags = I2C_M_RD, + .buf = buf, .len = size } }; + + obuf[0] = 0x40 | reg >> 8; + obuf[1] = reg & 0xff; + if (val >= 0) { + obuf[2] = val; + msg[0].len++; + } + + rc = i2c_transfer(ir->dev->i2c_client.adapter, msg, 2); + + if (rc < 0) + return rc; + + return (rc == 2) ? 0 : -EIO; +} + +static int au8522_rc_andor(struct au0828_rc *ir, u16 reg, u8 mask, u8 value) +{ + int rc; + char buf; + + rc = au8522_rc_read(ir, reg, -1, &buf, 1); + if (rc < 0) + return rc; + + buf = (buf & ~mask) | (value & mask); + + return au8522_rc_write(ir, reg, buf); +} + +#define au8522_rc_set(ir, reg, bit) au8522_rc_andor(ir, (reg), (bit), (bit)) +#define au8522_rc_clear(ir, reg, bit) au8522_rc_andor(ir, (reg), (bit), 0) + +/* Remote Controller time units */ + +#define AU8522_UNIT 200000 /* ns */ +#define NEC_START_SPACE (4500000 / AU8522_UNIT) +#define NEC_START_PULSE (562500 * 16) +#define RC5_START_SPACE (4 * AU8522_UNIT) +#define RC5_START_PULSE 888888 + +static int au0828_get_key_au8522(struct au0828_rc *ir) +{ + unsigned char buf[40]; + DEFINE_IR_RAW_EVENT(rawir); + int i, j, rc; + int prv_bit, bit, width; + bool first = true; + + /* Check IR int */ + rc = au8522_rc_read(ir, 0xe1, -1, buf, 1); + if (rc < 0 || !(buf[0] & (1 << 4))) + return 0; + + /* Something arrived. Get the data */ + rc = au8522_rc_read(ir, 0xe3, 0x11, buf, sizeof(buf)); + + + if (rc < 0) + return rc; + + /* Disable IR */ + au8522_rc_clear(ir, 0xe0, 1 << 4); + + usleep_range(45000, 46000); + + /* Enable IR */ + au8522_rc_set(ir, 0xe0, 1 << 4); + + dprintk(16, "RC data received: %*ph\n", 40, buf); + + prv_bit = (buf[0] >> 7) & 0x01; + width = 0; + for (i = 0; i < sizeof(buf); i++) { + for (j = 7; j >= 0; j--) { + bit = (buf[i] >> j) & 0x01; + if (bit == prv_bit) { + width++; + continue; + } + + /* + * Fix an au8522 bug: the first pulse event + * is lost. So, we need to fake it, based on the + * protocol. That means that not all raw decoders + * will work, as we need to add a hack for each + * protocol, based on the first space. + * So, we only support RC5 and NEC. + */ + + if (first) { + first = false; + + init_ir_raw_event(&rawir); + rawir.pulse = true; + if (width > NEC_START_SPACE - 2 && + width < NEC_START_SPACE + 2) { + /* NEC protocol */ + rawir.duration = NEC_START_PULSE; + dprintk(16, "Storing NEC start %s with duration %d", + rawir.pulse ? "pulse" : "space", + rawir.duration); + } else { + /* RC5 protocol */ + rawir.duration = RC5_START_PULSE; + dprintk(16, "Storing RC5 start %s with duration %d", + rawir.pulse ? "pulse" : "space", + rawir.duration); + } + ir_raw_event_store(ir->rc, &rawir); + } + + init_ir_raw_event(&rawir); + rawir.pulse = prv_bit ? false : true; + rawir.duration = AU8522_UNIT * width; + dprintk(16, "Storing %s with duration %d", + rawir.pulse ? "pulse" : "space", + rawir.duration); + ir_raw_event_store(ir->rc, &rawir); + + width = 1; + prv_bit = bit; + } + } + + init_ir_raw_event(&rawir); + rawir.pulse = prv_bit ? false : true; + rawir.duration = AU8522_UNIT * width; + dprintk(16, "Storing end %s with duration %d", + rawir.pulse ? "pulse" : "space", + rawir.duration); + ir_raw_event_store(ir->rc, &rawir); + + ir_raw_event_handle(ir->rc); + + return 1; +} + +/* + * Generic IR code + */ + +static void au0828_rc_work(struct work_struct *work) +{ + struct au0828_rc *ir = container_of(work, struct au0828_rc, work.work); + int rc; + + rc = ir->get_key_i2c(ir); + if (rc < 0) + pr_info("Error while getting RC scancode\n"); + + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); +} + +static int au0828_rc_start(struct rc_dev *rc) +{ + struct au0828_rc *ir = rc->priv; + + INIT_DELAYED_WORK(&ir->work, au0828_rc_work); + + /* Enable IR */ + au8522_rc_set(ir, 0xe0, 1 << 4); + + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); + + return 0; +} + +static void au0828_rc_stop(struct rc_dev *rc) +{ + struct au0828_rc *ir = rc->priv; + + /* Disable IR */ + au8522_rc_clear(ir, 0xe0, 1 << 4); + + cancel_delayed_work_sync(&ir->work); +} + +static int au0828_probe_i2c_ir(struct au0828_dev *dev) +{ + int i = 0; + const unsigned short addr_list[] = { + 0x47, I2C_CLIENT_END + }; + + while (addr_list[i] != I2C_CLIENT_END) { + if (i2c_probe_func_quick_read(dev->i2c_client.adapter, + addr_list[i]) == 1) + return addr_list[i]; + i++; + } + + return -ENODEV; +} + +int au0828_rc_register(struct au0828_dev *dev) +{ + struct au0828_rc *ir; + struct rc_dev *rc; + int err = -ENOMEM; + u16 i2c_rc_dev_addr = 0; + + if (!dev->board.has_ir_i2c) + return 0; + + i2c_rc_dev_addr = au0828_probe_i2c_ir(dev); + if (!i2c_rc_dev_addr) + return -ENODEV; + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + rc = rc_allocate_device(); + if (!ir || !rc) + goto error; + + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + ir->rc = rc; + + rc->priv = ir; + rc->open = au0828_rc_start; + rc->close = au0828_rc_stop; + + if (dev->board.has_ir_i2c) { /* external i2c device */ + switch (dev->boardnr) { + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + rc->map_name = RC_MAP_HAUPPAUGE; + ir->get_key_i2c = au0828_get_key_au8522; + break; + default: + err = -ENODEV; + goto error; + } + + ir->i2c_dev_addr = i2c_rc_dev_addr; + } + + /* This is how often we ask the chip for IR information */ + ir->polling = 100; /* ms */ + + /* init input device */ + snprintf(ir->name, sizeof(ir->name), "au0828 IR (%s)", + dev->board.name); + + usb_make_path(dev->usbdev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + + rc->input_name = ir->name; + rc->input_phys = ir->phys; + rc->input_id.bustype = BUS_USB; + rc->input_id.version = 1; + rc->input_id.vendor = le16_to_cpu(dev->usbdev->descriptor.idVendor); + rc->input_id.product = le16_to_cpu(dev->usbdev->descriptor.idProduct); + rc->dev.parent = &dev->usbdev->dev; + rc->driver_name = "au0828-input"; + rc->driver_type = RC_DRIVER_IR_RAW; + rc->allowed_protocols = RC_BIT_NEC | RC_BIT_RC5; + + /* all done */ + err = rc_register_device(rc); + if (err) + goto error; + + pr_info("Remote controller %s initalized\n", ir->name); + + return 0; + +error: + dev->ir = NULL; + rc_free_device(rc); + kfree(ir); + return err; +} + +void au0828_rc_unregister(struct au0828_dev *dev) +{ + struct au0828_rc *ir = dev->ir; + + /* skip detach on non attached boards */ + if (!ir) + return; + + if (ir->rc) + rc_unregister_device(ir->rc); + + /* done */ + kfree(ir); + dev->ir = NULL; +} + +int au0828_rc_suspend(struct au0828_dev *dev) +{ + struct au0828_rc *ir = dev->ir; + + if (!ir) + return 0; + + cancel_delayed_work_sync(&ir->work); + + return 0; +} + +int au0828_rc_resume(struct au0828_dev *dev) +{ + struct au0828_rc *ir = dev->ir; + + if (!ir) + return 0; + + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); + + return 0; +} diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 7112b9d..96bec05 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -88,6 +88,7 @@ struct au0828_board { unsigned int tuner_type; unsigned char tuner_addr; unsigned char i2c_clk_divider; + unsigned char has_ir_i2c:1; struct au0828_input input[AU0828_MAX_INPUT]; }; @@ -213,6 +214,10 @@ struct au0828_dev { struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler v4l2_ctrl_hdl; #endif +#ifdef CONFIG_VIDEO_AU0828_RC + struct au0828_rc *ir; +#endif + int users; unsigned int resources; /* resources in use */ struct video_device *vdev; @@ -319,3 +324,9 @@ extern struct videobuf_queue_ops au0828_vbi_qops; do { if (au0828_debug & level)\ printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\ } while (0) + +/* au0828-input.c */ +int au0828_rc_register(struct au0828_dev *dev); +void au0828_rc_unregister(struct au0828_dev *dev); +int au0828_rc_suspend(struct au0828_dev *dev); +int au0828_rc_resume(struct au0828_dev *dev); -- cgit v0.10.2 From 2621c0b3224633393ef9e24c3ac9de2770b3750c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 30 Jul 2014 10:36:32 -0300 Subject: [media] xc5000: Don't try forever to load the firmware With the current code, if something bad happens during the firmware init process, the device will keep trying forever, and removing it would cause an OOPS. Instead, try only a limited amount of time. If not, fails. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index ab7d444..460df44 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -1101,16 +1101,17 @@ static int xc5000_get_status(struct dvb_frontend *fe, u32 *status) static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) { struct xc5000_priv *priv = fe->tuner_priv; - int ret = 0; + int ret, i; u16 pll_lock_status; u16 fw_ck; cancel_delayed_work(&priv->timer_sleep); - if (force || xc5000_is_firmware_loaded(fe) != 0) { - -fw_retry: + if (!force && xc5000_is_firmware_loaded(fe) == 0) + return 0; + /* Try up to 5 times to load firmware */ + for (i = 0; i < 5; i++) { ret = xc5000_fwupload(fe); if (ret != 0) return ret; @@ -1118,25 +1119,25 @@ fw_retry: msleep(20); if (priv->fw_checksum_supported) { - if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck) - != 0) { + if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck)) { dprintk(1, "%s() FW checksum reading failed.\n", __func__); - goto fw_retry; + continue; } - if (fw_ck == 0) { + if (!fw_ck) { dprintk(1, "%s() FW checksum failed = 0x%04x\n", __func__, fw_ck); - goto fw_retry; + continue; } } /* Start the tuner self-calibration process */ - ret |= xc_initialize(priv); - - if (ret != 0) - goto fw_retry; + ret = xc_initialize(priv); + if (ret) { + dprintk(1, "Can't request Self-callibration. Reloading firmware\n"); + continue; + } /* Wait for calibration to complete. * We could continue but XC5000 will clock stretch subsequent @@ -1146,15 +1147,15 @@ fw_retry: msleep(100); if (priv->init_status_supported) { - if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck) != 0) { + if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck)) { dprintk(1, "%s() FW failed reading init status.\n", __func__); - goto fw_retry; + continue; } - if (fw_ck == 0) { + if (!fw_ck) { dprintk(1, "%s() FW init status failed = 0x%04x\n", __func__, fw_ck); - goto fw_retry; + continue; } } @@ -1164,12 +1165,13 @@ fw_retry: if (pll_lock_status > 63) { /* PLL is unlocked, force reload of the firmware */ printk(KERN_ERR "xc5000: PLL not running after fwload.\n"); - goto fw_retry; + continue; } } /* Default to "CABLE" mode */ - ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE); + ret = xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE); + break; } return ret; -- cgit v0.10.2 From 8604f3552b1c995186acc8306acf25ee73fd9d5e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 30 Jul 2014 11:09:15 -0300 Subject: [media] xc5000: optimize firmware retry logic Currently, firmware retry logic keeps reading from FS every time during the retry logic. This is not needed. Instead, only release the firmware read after success. While here, make the non-debug messages less verbose, as it only matters to the user if the firmware was successfully loaded, or if some error happened. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index 460df44..39f9fb1 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -625,48 +625,30 @@ static int xc_set_xtal(struct dvb_frontend *fe) return ret; } -static int xc5000_fwupload(struct dvb_frontend *fe) +static int xc5000_fwupload(struct dvb_frontend *fe, + const struct xc5000_fw_cfg *desired_fw, + const struct firmware *fw) { struct xc5000_priv *priv = fe->tuner_priv; - const struct firmware *fw; int ret; - const struct xc5000_fw_cfg *desired_fw = - xc5000_assign_firmware(priv->chip_id); - priv->pll_register_no = desired_fw->pll_reg; - priv->init_status_supported = desired_fw->init_status_supported; - priv->fw_checksum_supported = desired_fw->fw_checksum_supported; /* request the firmware, this will block and timeout */ - printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n", + dprintk(1, "waiting for firmware upload (%s)...\n", desired_fw->name); - ret = request_firmware(&fw, desired_fw->name, - priv->i2c_props.adap->dev.parent); - if (ret) { - printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n"); - goto out; - } else { - printk(KERN_DEBUG "xc5000: firmware read %Zu bytes.\n", - fw->size); - ret = 0; - } + priv->pll_register_no = desired_fw->pll_reg; + priv->init_status_supported = desired_fw->init_status_supported; + priv->fw_checksum_supported = desired_fw->fw_checksum_supported; - if (fw->size != desired_fw->size) { - printk(KERN_ERR "xc5000: firmware incorrect size\n"); - ret = -EINVAL; - } else { - printk(KERN_INFO "xc5000: firmware uploading...\n"); - ret = xc_load_i2c_sequence(fe, fw->data); - if (0 == ret) - ret = xc_set_xtal(fe); - if (0 == ret) - printk(KERN_INFO "xc5000: firmware upload complete...\n"); - else - printk(KERN_ERR "xc5000: firmware upload failed...\n"); - } -out: - release_firmware(fw); + dprintk(1, "firmware uploading...\n"); + ret = xc_load_i2c_sequence(fe, fw->data); + if (!ret) { + ret = xc_set_xtal(fe); + dprintk(1, "Firmware upload complete...\n"); + } else + printk(KERN_ERR "xc5000: firmware upload failed...\n"); + return ret; } @@ -1101,6 +1083,8 @@ static int xc5000_get_status(struct dvb_frontend *fe, u32 *status) static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) { struct xc5000_priv *priv = fe->tuner_priv; + const struct xc5000_fw_cfg *desired_fw = xc5000_assign_firmware(priv->chip_id); + const struct firmware *fw; int ret, i; u16 pll_lock_status; u16 fw_ck; @@ -1110,11 +1094,26 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) if (!force && xc5000_is_firmware_loaded(fe) == 0) return 0; + ret = request_firmware(&fw, desired_fw->name, + priv->i2c_props.adap->dev.parent); + if (ret) { + printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n"); + return ret; + } + + dprintk(1, "firmware read %Zu bytes.\n", fw->size); + + if (fw->size != desired_fw->size) { + printk(KERN_ERR "xc5000: firmware file with incorrect size\n"); + ret = -EINVAL; + goto err; + } + /* Try up to 5 times to load firmware */ for (i = 0; i < 5; i++) { - ret = xc5000_fwupload(fe); + ret = xc5000_fwupload(fe, desired_fw, fw); if (ret != 0) - return ret; + goto err; msleep(20); @@ -1171,9 +1170,13 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) /* Default to "CABLE" mode */ ret = xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE); + printk(KERN_INFO "xc5000: Firmware %s loaded and running.\n", + desired_fw->name); break; } +err: + release_firmware(fw); return ret; } -- cgit v0.10.2 From ee67674a7758dddc6abad16182b832a6a13b9ef9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 30 Jul 2014 11:17:46 -0300 Subject: [media] xc5000: always write at dmesg when it fails to upload firmware On a normal condition, no errors should happen at xc5000 firmware upload is done. So, print at the dmesg if something bad happens. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index 39f9fb1..e135760 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -1104,13 +1104,16 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) dprintk(1, "firmware read %Zu bytes.\n", fw->size); if (fw->size != desired_fw->size) { - printk(KERN_ERR "xc5000: firmware file with incorrect size\n"); + printk(KERN_ERR "xc5000: Firmware file with incorrect size\n"); ret = -EINVAL; goto err; } /* Try up to 5 times to load firmware */ for (i = 0; i < 5; i++) { + if (i) + printk(KERN_CONT " - retrying to upload firmware.\n"); + ret = xc5000_fwupload(fe, desired_fw, fw); if (ret != 0) goto err; @@ -1119,14 +1122,15 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) if (priv->fw_checksum_supported) { if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck)) { - dprintk(1, "%s() FW checksum reading failed.\n", - __func__); + printk(KERN_ERR + "xc5000: FW checksum reading failed."); continue; } if (!fw_ck) { - dprintk(1, "%s() FW checksum failed = 0x%04x\n", - __func__, fw_ck); + printk(KERN_ERR + "xc5000: FW checksum failed = 0x%04x.", + fw_ck); continue; } } @@ -1134,7 +1138,8 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) /* Start the tuner self-calibration process */ ret = xc_initialize(priv); if (ret) { - dprintk(1, "Can't request Self-callibration. Reloading firmware\n"); + printk(KERN_ERR + "xc5000: Can't request Self-callibration."); continue; } @@ -1147,13 +1152,15 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) if (priv->init_status_supported) { if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck)) { - dprintk(1, "%s() FW failed reading init status.\n", - __func__); + printk(KERN_ERR + "xc5000: FW failed reading init status."); continue; } if (!fw_ck) { - dprintk(1, "%s() FW init status failed = 0x%04x\n", __func__, fw_ck); + printk(KERN_ERR + "xc5000: FW init status failed = 0x%04x.", + fw_ck); continue; } } @@ -1163,19 +1170,26 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) &pll_lock_status); if (pll_lock_status > 63) { /* PLL is unlocked, force reload of the firmware */ - printk(KERN_ERR "xc5000: PLL not running after fwload.\n"); + printk(KERN_ERR + "xc5000: PLL not running after fwload."); continue; } } /* Default to "CABLE" mode */ ret = xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE); - printk(KERN_INFO "xc5000: Firmware %s loaded and running.\n", - desired_fw->name); - break; + if (!ret) + break; + printk(KERN_ERR "xc5000: can't set to cable mode."); } err: + if (!ret) + printk(KERN_INFO "xc5000: Firmware %s loaded and running.\n", + desired_fw->name); + else + printk(KERN_CONT " - too many retries. Giving up\n"); + release_firmware(fw); return ret; } -- cgit v0.10.2 From 1774afe7d98d8cfcb57327d2ced2d95c48de8c72 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 11 Jul 2014 12:19:49 -0300 Subject: [media] s5p-jpeg: Document sclk-jpeg clock for Exynos3250 SoC JPEG IP on Exynos3250 SoC requires enabling two clock gates for its operation. This patch documents this requirement. Signed-off-by: Jacek Anaszewski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt b/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt index 937b755..bf52ed4 100644 --- a/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt +++ b/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt @@ -3,9 +3,13 @@ Samsung S5P/EXYNOS SoC series JPEG codec Required properties: - compatible : should be one of: - "samsung,s5pv210-jpeg", "samsung,exynos4210-jpeg"; + "samsung,s5pv210-jpeg", "samsung,exynos4210-jpeg", + "samsung,exynos3250-jpeg"; - reg : address and length of the JPEG codec IP register set; - interrupts : specifies the JPEG codec IP interrupt; -- clocks : should contain the JPEG codec IP gate clock specifier, from the - common clock bindings; -- clock-names : should contain "jpeg" entry. +- clock-names : should contain: + - "jpeg" for the core gate clock, + - "sclk" for the special clock (optional). +- clocks : should contain the clock specifier and clock ID list + matching entries in the clock-names property; from + the common clock bindings. -- cgit v0.10.2 From 3246fdaa0ac2d936b1838589b76fd5aaff02e46d Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 11 Jul 2014 12:19:42 -0300 Subject: [media] s5p-jpeg: Add support for Exynos3250 SoC This patch adds support for jpeg codec on Exynos3250 SoC to the s5p-jpeg driver. Supported raw formats are: YUYV, YVYU, UYVY, VYUY, RGB565, RGB565X, RGB32, NV12, NV21. The support includes also scaling and cropping features. Signed-off-by: Jacek Anaszewski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 433f0bf..6d86646 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -167,12 +167,13 @@ config VIDEO_SAMSUNG_S5P_G2D 2d graphics accelerator. config VIDEO_SAMSUNG_S5P_JPEG - tristate "Samsung S5P/Exynos4 JPEG codec driver" + tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver" depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS) select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV ---help--- - This is a v4l2 driver for Samsung S5P and EXYNOS4 JPEG codec + This is a v4l2 driver for Samsung S5P, EXYNOS3250 + and EXYNOS4 JPEG codec config VIDEO_SAMSUNG_S5P_MFC tristate "Samsung S5P MFC Video Codec" diff --git a/drivers/media/platform/s5p-jpeg/Makefile b/drivers/media/platform/s5p-jpeg/Makefile index a1a9169..9e5f214 100644 --- a/drivers/media/platform/s5p-jpeg/Makefile +++ b/drivers/media/platform/s5p-jpeg/Makefile @@ -1,2 +1,2 @@ -s5p-jpeg-objs := jpeg-core.o jpeg-hw-exynos4.o jpeg-hw-s5p.o +s5p-jpeg-objs := jpeg-core.o jpeg-hw-exynos3250.o jpeg-hw-exynos4.o jpeg-hw-s5p.o obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg.o diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 0dcb796..126199e 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1,6 +1,6 @@ /* linux/drivers/media/platform/s5p-jpeg/jpeg-core.c * - * Copyright (c) 2011-2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Author: Andrzej Pietrasiewicz @@ -32,6 +32,7 @@ #include "jpeg-core.h" #include "jpeg-hw-s5p.h" #include "jpeg-hw-exynos4.h" +#include "jpeg-hw-exynos3250.h" #include "jpeg-regs.h" static struct s5p_jpeg_fmt sjpeg_formats[] = { @@ -41,6 +42,7 @@ static struct s5p_jpeg_fmt sjpeg_formats[] = { .flags = SJPEG_FMT_FLAG_ENC_CAPTURE | SJPEG_FMT_FLAG_DEC_OUTPUT | SJPEG_FMT_FLAG_S5P | + SJPEG_FMT_FLAG_EXYNOS3250 | SJPEG_FMT_FLAG_EXYNOS4, }, { @@ -70,6 +72,19 @@ static struct s5p_jpeg_fmt sjpeg_formats[] = { .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, }, { + .name = "YUV 4:2:2 packed, YCbYCr", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { .name = "YUV 4:2:2 packed, YCrYCb", .fourcc = V4L2_PIX_FMT_YVYU, .depth = 16, @@ -83,6 +98,45 @@ static struct s5p_jpeg_fmt sjpeg_formats[] = { .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, }, { + .name = "YUV 4:2:2 packed, YCrYCb", + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = 16, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .name = "YUV 4:2:2 packed, YCrYCb", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { + .name = "YUV 4:2:2 packed, YCrYCb", + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = 16, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + }, + { .name = "RGB565", .fourcc = V4L2_PIX_FMT_RGB565, .depth = 16, @@ -100,6 +154,32 @@ static struct s5p_jpeg_fmt sjpeg_formats[] = { .fourcc = V4L2_PIX_FMT_RGB565, .depth = 16, .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .name = "RGB565X", + .fourcc = V4L2_PIX_FMT_RGB565X, + .depth = 16, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { + .name = "RGB565", + .fourcc = V4L2_PIX_FMT_RGB565, + .depth = 16, + .colplanes = 1, .h_align = 0, .v_align = 0, .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | @@ -121,6 +201,19 @@ static struct s5p_jpeg_fmt sjpeg_formats[] = { .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, }, { + .name = "ARGB8888, 32 bpp", + .fourcc = V4L2_PIX_FMT_RGB32, + .depth = 32, + .colplanes = 1, + .h_align = 2, + .v_align = 0, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444, + }, + { .name = "YUV 4:4:4 planar, Y/CbCr", .fourcc = V4L2_PIX_FMT_NV24, .depth = 24, @@ -190,9 +283,23 @@ static struct s5p_jpeg_fmt sjpeg_formats[] = { .fourcc = V4L2_PIX_FMT_NV12, .depth = 12, .colplanes = 2, + .h_align = 3, + .v_align = 3, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { + .name = "YUV 4:2:0 planar, Y/CbCr", + .fourcc = V4L2_PIX_FMT_NV12, + .depth = 12, + .colplanes = 2, .h_align = 4, .v_align = 4, - .flags = SJPEG_FMT_FLAG_DEC_CAPTURE | + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | SJPEG_FMT_FLAG_S5P | SJPEG_FMT_NON_RGB, .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, @@ -202,10 +309,24 @@ static struct s5p_jpeg_fmt sjpeg_formats[] = { .fourcc = V4L2_PIX_FMT_NV21, .depth = 12, .colplanes = 2, + .h_align = 3, + .v_align = 3, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { + .name = "YUV 4:2:0 planar, Y/CrCb", + .fourcc = V4L2_PIX_FMT_NV21, + .depth = 12, + .colplanes = 2, .h_align = 1, .v_align = 1, .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | SJPEG_FMT_FLAG_EXYNOS4 | SJPEG_FMT_NON_RGB, .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, @@ -224,6 +345,19 @@ static struct s5p_jpeg_fmt sjpeg_formats[] = { .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, }, { + .name = "YUV 4:2:0 contiguous 3-planar, Y/Cb/Cr", + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = 12, + .colplanes = 3, + .h_align = 4, + .v_align = 4, + .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | + SJPEG_FMT_FLAG_DEC_CAPTURE | + SJPEG_FMT_FLAG_EXYNOS3250 | + SJPEG_FMT_NON_RGB, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + }, + { .name = "Gray", .fourcc = V4L2_PIX_FMT_GREY, .depth = 8, @@ -457,6 +591,16 @@ static int exynos4x12_decoded_subsampling[] = { V4L2_JPEG_CHROMA_SUBSAMPLING_420, }; +static int exynos3250_decoded_subsampling[] = { + V4L2_JPEG_CHROMA_SUBSAMPLING_444, + V4L2_JPEG_CHROMA_SUBSAMPLING_422, + V4L2_JPEG_CHROMA_SUBSAMPLING_420, + V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, + -1, + -1, + V4L2_JPEG_CHROMA_SUBSAMPLING_411, +}; + static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c) { return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler); @@ -471,14 +615,21 @@ static int s5p_jpeg_to_user_subsampling(struct s5p_jpeg_ctx *ctx) { WARN_ON(ctx->subsampling > 3); - if (ctx->jpeg->variant->version == SJPEG_S5P) { + switch (ctx->jpeg->variant->version) { + case SJPEG_S5P: if (ctx->subsampling > 2) return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; return ctx->subsampling; - } else { + case SJPEG_EXYNOS3250: + if (ctx->subsampling > 3) + return V4L2_JPEG_CHROMA_SUBSAMPLING_411; + return exynos3250_decoded_subsampling[ctx->subsampling]; + case SJPEG_EXYNOS4: if (ctx->subsampling > 2) return V4L2_JPEG_CHROMA_SUBSAMPLING_420; return exynos4x12_decoded_subsampling[ctx->subsampling]; + default: + return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; } } @@ -646,6 +797,7 @@ static int s5p_jpeg_open(struct file *file) FMT_TYPE_OUTPUT); cap_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_YUYV, FMT_TYPE_CAPTURE); + ctx->scale_factor = EXYNOS3250_DEC_SCALE_FACTOR_8_8; } ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init); @@ -1229,6 +1381,101 @@ static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv, return s5p_jpeg_s_fmt(fh_to_ctx(priv), f); } +static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, + struct v4l2_rect *r) +{ + int w_ratio, h_ratio, scale_factor, cur_ratio, i; + + w_ratio = ctx->out_q.w / r->width; + h_ratio = ctx->out_q.h / r->height; + + scale_factor = w_ratio > h_ratio ? w_ratio : h_ratio; + scale_factor = clamp_val(scale_factor, 1, 8); + + /* Align scale ratio to the nearest power of 2 */ + for (i = 0; i <= 3; ++i) { + cur_ratio = 1 << i; + if (scale_factor <= cur_ratio) { + ctx->scale_factor = cur_ratio; + break; + } + } + + r->width = round_down(ctx->out_q.w / ctx->scale_factor, 2); + r->height = round_down(ctx->out_q.h / ctx->scale_factor, 2); + + ctx->crop_rect.width = r->width; + ctx->crop_rect.height = r->height; + ctx->crop_rect.left = 0; + ctx->crop_rect.top = 0; + + ctx->crop_altered = true; + + return 0; +} + +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ +static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) +{ + if (a->left < b->left || a->top < b->top) + return 0; + if (a->left + a->width > b->left + b->width) + return 0; + if (a->top + a->height > b->top + b->height) + return 0; + + return 1; +} + +static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, + struct v4l2_rect *r) +{ + struct v4l2_rect base_rect; + int w_step, h_step; + + switch (ctx->cap_q.fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + w_step = 1; + h_step = 2; + break; + case V4L2_PIX_FMT_YUV420: + w_step = 2; + h_step = 2; + break; + default: + w_step = 1; + h_step = 1; + break; + } + + base_rect.top = 0; + base_rect.left = 0; + base_rect.width = ctx->out_q.w; + base_rect.height = ctx->out_q.h; + + r->width = round_down(r->width, w_step); + r->height = round_down(r->height, h_step); + r->left = round_down(r->left, 2); + r->top = round_down(r->top, 2); + + if (!enclosed_rectangle(r, &base_rect)) + return -EINVAL; + + ctx->crop_rect.left = r->left; + ctx->crop_rect.top = r->top; + ctx->crop_rect.width = r->width; + ctx->crop_rect.height = r->height; + + ctx->crop_altered = true; + + return 0; +} + +/* + * V4L2 controls + */ + static int s5p_jpeg_g_selection(struct file *file, void *priv, struct v4l2_selection *s) { @@ -1264,6 +1511,30 @@ static int s5p_jpeg_g_selection(struct file *file, void *priv, /* * V4L2 controls */ +static int s5p_jpeg_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); + struct v4l2_rect *rect = &s->r; + int ret = -EINVAL; + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (s->target == V4L2_SEL_TGT_COMPOSE) { + if (ctx->mode != S5P_JPEG_DECODE) + return -EINVAL; + if (ctx->jpeg->variant->version == SJPEG_EXYNOS3250) + ret = exynos3250_jpeg_try_downscale(ctx, rect); + } else if (s->target == V4L2_SEL_TGT_CROP) { + if (ctx->mode != S5P_JPEG_ENCODE) + return -EINVAL; + if (ctx->jpeg->variant->version == SJPEG_EXYNOS3250) + ret = exynos3250_jpeg_try_crop(ctx, rect); + } + + return ret; +} static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { @@ -1414,6 +1685,7 @@ static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = { .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, .vidioc_g_selection = s5p_jpeg_g_selection, + .vidioc_s_selection = s5p_jpeg_s_selection, }; /* @@ -1604,6 +1876,135 @@ static void exynos4_jpeg_device_run(void *priv) spin_unlock_irqrestore(&ctx->jpeg->slock, flags); } +static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct s5p_jpeg_fmt *fmt; + struct vb2_buffer *vb; + struct s5p_jpeg_addr jpeg_addr; + u32 pix_size; + + pix_size = ctx->cap_q.w * ctx->cap_q.h; + + if (ctx->mode == S5P_JPEG_ENCODE) { + vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + fmt = ctx->out_q.fmt; + } else { + vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + fmt = ctx->cap_q.fmt; + } + + jpeg_addr.y = vb2_dma_contig_plane_dma_addr(vb, 0); + + if (fmt->colplanes == 2) { + jpeg_addr.cb = jpeg_addr.y + pix_size; + } else if (fmt->colplanes == 3) { + jpeg_addr.cb = jpeg_addr.y + pix_size; + if (fmt->fourcc == V4L2_PIX_FMT_YUV420) + jpeg_addr.cr = jpeg_addr.cb + pix_size / 4; + else + jpeg_addr.cr = jpeg_addr.cb + pix_size / 2; + } + + exynos3250_jpeg_imgadr(jpeg->regs, &jpeg_addr); +} + +static void exynos3250_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_buffer *vb; + unsigned int jpeg_addr = 0; + + if (ctx->mode == S5P_JPEG_ENCODE) + vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + else + vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + + jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + exynos3250_jpeg_jpgadr(jpeg->regs, jpeg_addr); +} + +static void exynos3250_jpeg_device_run(void *priv) +{ + struct s5p_jpeg_ctx *ctx = priv; + struct s5p_jpeg *jpeg = ctx->jpeg; + unsigned long flags; + + spin_lock_irqsave(&ctx->jpeg->slock, flags); + + exynos3250_jpeg_reset(jpeg->regs); + exynos3250_jpeg_set_dma_num(jpeg->regs); + exynos3250_jpeg_poweron(jpeg->regs); + exynos3250_jpeg_clk_set(jpeg->regs); + exynos3250_jpeg_proc_mode(jpeg->regs, ctx->mode); + + if (ctx->mode == S5P_JPEG_ENCODE) { + exynos3250_jpeg_input_raw_fmt(jpeg->regs, + ctx->out_q.fmt->fourcc); + exynos3250_jpeg_dri(jpeg->regs, ctx->restart_interval); + + /* + * JPEG IP allows storing 4 quantization tables + * We fill table 0 for luma and table 1 for chroma + */ + s5p_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); + s5p_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); + /* use table 0 for Y */ + exynos3250_jpeg_qtbl(jpeg->regs, 1, 0); + /* use table 1 for Cb and Cr*/ + exynos3250_jpeg_qtbl(jpeg->regs, 2, 1); + exynos3250_jpeg_qtbl(jpeg->regs, 3, 1); + + /* Y, Cb, Cr use Huffman table 0 */ + exynos3250_jpeg_htbl_ac(jpeg->regs, 1); + exynos3250_jpeg_htbl_dc(jpeg->regs, 1); + exynos3250_jpeg_htbl_ac(jpeg->regs, 2); + exynos3250_jpeg_htbl_dc(jpeg->regs, 2); + exynos3250_jpeg_htbl_ac(jpeg->regs, 3); + exynos3250_jpeg_htbl_dc(jpeg->regs, 3); + + exynos3250_jpeg_set_x(jpeg->regs, ctx->crop_rect.width); + exynos3250_jpeg_set_y(jpeg->regs, ctx->crop_rect.height); + exynos3250_jpeg_stride(jpeg->regs, ctx->out_q.fmt->fourcc, + ctx->out_q.w); + exynos3250_jpeg_offset(jpeg->regs, ctx->crop_rect.left, + ctx->crop_rect.top); + exynos3250_jpeg_set_img_addr(ctx); + exynos3250_jpeg_set_jpeg_addr(ctx); + exynos3250_jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); + + /* ultimately comes from sizeimage from userspace */ + exynos3250_jpeg_enc_stream_bound(jpeg->regs, ctx->cap_q.size); + + if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565 || + ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565X || + ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB32) + exynos3250_jpeg_set_y16(jpeg->regs, true); + } else { + exynos3250_jpeg_set_img_addr(ctx); + exynos3250_jpeg_set_jpeg_addr(ctx); + exynos3250_jpeg_stride(jpeg->regs, ctx->cap_q.fmt->fourcc, + ctx->cap_q.w); + exynos3250_jpeg_offset(jpeg->regs, 0, 0); + exynos3250_jpeg_dec_scaling_ratio(jpeg->regs, + ctx->scale_factor); + exynos3250_jpeg_dec_stream_size(jpeg->regs, ctx->out_q.size); + exynos3250_jpeg_output_raw_fmt(jpeg->regs, + ctx->cap_q.fmt->fourcc); + } + + exynos3250_jpeg_interrupts_enable(jpeg->regs); + + /* JPEG RGB to YCbCr conversion matrix */ + exynos3250_jpeg_coef(jpeg->regs, ctx->mode); + + exynos3250_jpeg_set_timer(jpeg->regs, EXYNOS3250_IRQ_TIMEOUT); + jpeg->irq_status = 0; + exynos3250_jpeg_start(jpeg->regs); + + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); +} + static int s5p_jpeg_job_ready(void *priv) { struct s5p_jpeg_ctx *ctx = priv; @@ -1621,8 +2022,14 @@ static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = { .device_run = s5p_jpeg_device_run, .job_ready = s5p_jpeg_job_ready, .job_abort = s5p_jpeg_job_abort, -} -; +}; + +static struct v4l2_m2m_ops exynos3250_jpeg_m2m_ops = { + .device_run = exynos3250_jpeg_device_run, + .job_ready = s5p_jpeg_job_ready, + .job_abort = s5p_jpeg_job_abort, +}; + static struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = { .device_run = exynos4_jpeg_device_run, .job_ready = s5p_jpeg_job_ready, @@ -1895,6 +2302,70 @@ static irqreturn_t exynos4_jpeg_irq(int irq, void *priv) return IRQ_HANDLED; } +static irqreturn_t exynos3250_jpeg_irq(int irq, void *dev_id) +{ + struct s5p_jpeg *jpeg = dev_id; + struct s5p_jpeg_ctx *curr_ctx; + struct vb2_buffer *src_buf, *dst_buf; + unsigned long payload_size = 0; + enum vb2_buffer_state state = VB2_BUF_STATE_DONE; + bool interrupt_timeout = false; + u32 irq_status; + + spin_lock(&jpeg->slock); + + irq_status = exynos3250_jpeg_get_timer_status(jpeg->regs); + if (irq_status & EXYNOS3250_TIMER_INT_STAT) { + exynos3250_jpeg_clear_timer_status(jpeg->regs); + interrupt_timeout = true; + dev_err(jpeg->dev, "Interrupt timeout occurred.\n"); + } + + irq_status = exynos3250_jpeg_get_int_status(jpeg->regs); + exynos3250_jpeg_clear_int_status(jpeg->regs, irq_status); + + jpeg->irq_status |= irq_status; + + curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); + + if (!curr_ctx) + goto exit_unlock; + + if ((irq_status & EXYNOS3250_HEADER_STAT) && + (curr_ctx->mode == S5P_JPEG_DECODE)) { + exynos3250_jpeg_rstart(jpeg->regs); + goto exit_unlock; + } + + if (jpeg->irq_status & (EXYNOS3250_JPEG_DONE | + EXYNOS3250_WDMA_DONE | + EXYNOS3250_RDMA_DONE | + EXYNOS3250_RESULT_STAT)) + payload_size = exynos3250_jpeg_compressed_size(jpeg->regs); + else if (interrupt_timeout) + state = VB2_BUF_STATE_ERROR; + else + goto exit_unlock; + + src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); + + dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; + dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; + + v4l2_m2m_buf_done(src_buf, state); + if (curr_ctx->mode == S5P_JPEG_ENCODE) + vb2_set_plane_payload(dst_buf, 0, payload_size); + v4l2_m2m_buf_done(dst_buf, state); + v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); + + curr_ctx->subsampling = + exynos3250_jpeg_get_subsampling_mode(jpeg->regs); +exit_unlock: + spin_unlock(&jpeg->slock); + return IRQ_HANDLED; +} + static void *jpeg_get_drv_data(struct device *dev); /* @@ -1950,6 +2421,10 @@ static int s5p_jpeg_probe(struct platform_device *pdev) } dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk); + jpeg->sclk = clk_get(&pdev->dev, "sclk"); + if (IS_ERR(jpeg->sclk)) + dev_info(&pdev->dev, "sclk clock not available\n"); + /* v4l2 device */ ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev); if (ret) { @@ -2057,6 +2532,8 @@ device_register_rollback: clk_get_rollback: clk_put(jpeg->clk); + if (!IS_ERR(jpeg->sclk)) + clk_put(jpeg->sclk); return ret; } @@ -2075,10 +2552,15 @@ static int s5p_jpeg_remove(struct platform_device *pdev) v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); - if (!pm_runtime_status_suspended(&pdev->dev)) + if (!pm_runtime_status_suspended(&pdev->dev)) { clk_disable_unprepare(jpeg->clk); + if (!IS_ERR(jpeg->sclk)) + clk_disable_unprepare(jpeg->sclk); + } clk_put(jpeg->clk); + if (!IS_ERR(jpeg->sclk)) + clk_put(jpeg->sclk); return 0; } @@ -2088,6 +2570,8 @@ static int s5p_jpeg_runtime_suspend(struct device *dev) struct s5p_jpeg *jpeg = dev_get_drvdata(dev); clk_disable_unprepare(jpeg->clk); + if (!IS_ERR(jpeg->sclk)) + clk_disable_unprepare(jpeg->sclk); return 0; } @@ -2102,15 +2586,24 @@ static int s5p_jpeg_runtime_resume(struct device *dev) if (ret < 0) return ret; + if (!IS_ERR(jpeg->sclk)) { + ret = clk_prepare_enable(jpeg->sclk); + if (ret < 0) + return ret; + } + spin_lock_irqsave(&jpeg->slock, flags); /* - * JPEG IP allows storing two Huffman tables for each component + * JPEG IP allows storing two Huffman tables for each component. * We fill table 0 for each component and do this here only - * for S5PC210 device as Exynos4x12 requires programming its - * Huffman tables each time the encoding process is initialized. + * for S5PC210 and Exynos3250 SoCs. Exynos4x12 SoC requires + * programming its Huffman tables each time the encoding process + * is initialized, and thus it is accomplished in the device_run + * callback of m2m_ops. */ - if (jpeg->variant->version == SJPEG_S5P) { + if (jpeg->variant->version == SJPEG_S5P || + jpeg->variant->version == SJPEG_EXYNOS3250) { s5p_jpeg_set_hdctbl(jpeg->regs); s5p_jpeg_set_hdctblg(jpeg->regs); s5p_jpeg_set_hactbl(jpeg->regs); @@ -2150,6 +2643,13 @@ static struct s5p_jpeg_variant s5p_jpeg_drvdata = { .fmt_ver_flag = SJPEG_FMT_FLAG_S5P, }; +static struct s5p_jpeg_variant exynos3250_jpeg_drvdata = { + .version = SJPEG_EXYNOS3250, + .jpeg_irq = exynos3250_jpeg_irq, + .m2m_ops = &exynos3250_jpeg_m2m_ops, + .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS3250, +}; + static struct s5p_jpeg_variant exynos4_jpeg_drvdata = { .version = SJPEG_EXYNOS4, .jpeg_irq = exynos4_jpeg_irq, @@ -2162,8 +2662,11 @@ static const struct of_device_id samsung_jpeg_match[] = { .compatible = "samsung,s5pv210-jpeg", .data = &s5p_jpeg_drvdata, }, { + .compatible = "samsung,exynos3250-jpeg", + .data = &exynos3250_jpeg_drvdata, + }, { .compatible = "samsung,exynos4210-jpeg", - .data = &s5p_jpeg_drvdata, + .data = &exynos4_jpeg_drvdata, }, { .compatible = "samsung,exynos4212-jpeg", .data = &exynos4_jpeg_drvdata, diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index 3e47863..764b32d 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -35,6 +35,8 @@ #define S5P_JPEG_COEF32 0x6e #define S5P_JPEG_COEF33 0x13 +#define EXYNOS3250_IRQ_TIMEOUT 0x10000000 + /* a selection of JPEG markers */ #define TEM 0x01 #define SOF0 0xc0 @@ -49,9 +51,10 @@ #define SJPEG_FMT_FLAG_DEC_CAPTURE (1 << 2) #define SJPEG_FMT_FLAG_DEC_OUTPUT (1 << 3) #define SJPEG_FMT_FLAG_S5P (1 << 4) -#define SJPEG_FMT_FLAG_EXYNOS4 (1 << 5) -#define SJPEG_FMT_RGB (1 << 6) -#define SJPEG_FMT_NON_RGB (1 << 7) +#define SJPEG_FMT_FLAG_EXYNOS3250 (1 << 5) +#define SJPEG_FMT_FLAG_EXYNOS4 (1 << 6) +#define SJPEG_FMT_RGB (1 << 7) +#define SJPEG_FMT_NON_RGB (1 << 8) #define S5P_JPEG_ENCODE 0 #define S5P_JPEG_DECODE 1 @@ -65,8 +68,9 @@ /* Version numbers */ -#define SJPEG_S5P 1 -#define SJPEG_EXYNOS4 2 +#define SJPEG_S5P 1 +#define SJPEG_EXYNOS3250 2 +#define SJPEG_EXYNOS4 3 enum exynos4_jpeg_result { OK_ENC_OR_DEC, @@ -95,8 +99,13 @@ enum exynos4_jpeg_img_quality_level { * @regs: JPEG IP registers mapping * @irq: JPEG IP irq * @clk: JPEG IP clock + * @sclk: Exynos3250 JPEG IP special clock * @dev: JPEG IP struct device * @alloc_ctx: videobuf2 memory allocator's context + * @variant: driver variant to be used + * @irq_status interrupt flags set during single encode/decode + operation + */ struct s5p_jpeg { struct mutex lock; @@ -111,9 +120,11 @@ struct s5p_jpeg { unsigned int irq; enum exynos4_jpeg_result irq_ret; struct clk *clk; + struct clk *sclk; struct device *dev; void *alloc_ctx; struct s5p_jpeg_variant *variant; + u32 irq_status; }; struct s5p_jpeg_variant { @@ -164,9 +175,15 @@ struct s5p_jpeg_q_data { * @jpeg: JPEG IP device for this context * @mode: compression (encode) operation or decompression (decode) * @compr_quality: destination image quality in compression (encode) mode + * @restart_interval: JPEG restart interval for JPEG encoding + * @subsampling: subsampling of a raw format or a JPEG * @out_q: source (output) queue information - * @cap_fmt: destination (capture) queue queue information + * @cap_q: destination (capture) queue queue information + * @scale_factor: scale factor for JPEG decoding + * @crop_rect: a rectangle representing crop area of the output buffer + * @fh: V4L2 file handle * @hdr_parsed: set if header has been parsed during decompression + * @crop_altered: set if crop rectangle has been altered by the user space * @ctrl_handler: controls handler */ struct s5p_jpeg_ctx { @@ -177,8 +194,11 @@ struct s5p_jpeg_ctx { unsigned short subsampling; struct s5p_jpeg_q_data out_q; struct s5p_jpeg_q_data cap_q; + unsigned int scale_factor; + struct v4l2_rect crop_rect; struct v4l2_fh fh; bool hdr_parsed; + bool crop_altered; struct v4l2_ctrl_handler ctrl_handler; }; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c new file mode 100644 index 0000000..d26e1f8 --- /dev/null +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c @@ -0,0 +1,487 @@ +/* linux/drivers/media/platform/exynos3250-jpeg/jpeg-hw.h + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jacek Anaszewski + * + * 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 +#include +#include + +#include "jpeg-core.h" +#include "jpeg-regs.h" +#include "jpeg-hw-exynos3250.h" + +void exynos3250_jpeg_reset(void __iomem *regs) +{ + u32 reg = 0; + int count = 1000; + + writel(1, regs + EXYNOS3250_SW_RESET); + /* no other way but polling for when JPEG IP becomes operational */ + while (reg != 0 && --count > 0) { + udelay(1); + cpu_relax(); + reg = readl(regs + EXYNOS3250_SW_RESET); + } + + reg = 0; + count = 1000; + + while (reg != 1 && --count > 0) { + writel(1, regs + EXYNOS3250_JPGDRI); + udelay(1); + cpu_relax(); + reg = readl(regs + EXYNOS3250_JPGDRI); + } + + writel(0, regs + EXYNOS3250_JPGDRI); +} + +void exynos3250_jpeg_poweron(void __iomem *regs) +{ + writel(EXYNOS3250_POWER_ON, regs + EXYNOS3250_JPGCLKCON); +} + +void exynos3250_jpeg_set_dma_num(void __iomem *regs) +{ + writel(((EXYNOS3250_DMA_MO_COUNT << EXYNOS3250_WDMA_ISSUE_NUM_SHIFT) & + EXYNOS3250_WDMA_ISSUE_NUM_MASK) | + ((EXYNOS3250_DMA_MO_COUNT << EXYNOS3250_RDMA_ISSUE_NUM_SHIFT) & + EXYNOS3250_RDMA_ISSUE_NUM_MASK) | + ((EXYNOS3250_DMA_MO_COUNT << EXYNOS3250_ISSUE_GATHER_NUM_SHIFT) & + EXYNOS3250_ISSUE_GATHER_NUM_MASK), + regs + EXYNOS3250_DMA_ISSUE_NUM); +} + +void exynos3250_jpeg_clk_set(void __iomem *base) +{ + u32 reg; + + reg = readl(base + EXYNOS3250_JPGCMOD) & ~EXYNOS3250_HALF_EN_MASK; + + writel(reg | EXYNOS3250_HALF_EN, base + EXYNOS3250_JPGCMOD); +} + +void exynos3250_jpeg_input_raw_fmt(void __iomem *regs, unsigned int fmt) +{ + u32 reg; + + reg = readl(regs + EXYNOS3250_JPGCMOD) & + EXYNOS3250_MODE_Y16_MASK; + + switch (fmt) { + case V4L2_PIX_FMT_RGB32: + reg |= EXYNOS3250_MODE_SEL_ARGB8888; + break; + case V4L2_PIX_FMT_BGR32: + reg |= EXYNOS3250_MODE_SEL_ARGB8888 | EXYNOS3250_SRC_SWAP_RGB; + break; + case V4L2_PIX_FMT_RGB565: + reg |= EXYNOS3250_MODE_SEL_RGB565; + break; + case V4L2_PIX_FMT_RGB565X: + reg |= EXYNOS3250_MODE_SEL_RGB565 | EXYNOS3250_SRC_SWAP_RGB; + break; + case V4L2_PIX_FMT_YUYV: + reg |= EXYNOS3250_MODE_SEL_422_1P_LUM_CHR; + break; + case V4L2_PIX_FMT_YVYU: + reg |= EXYNOS3250_MODE_SEL_422_1P_LUM_CHR | + EXYNOS3250_SRC_SWAP_UV; + break; + case V4L2_PIX_FMT_UYVY: + reg |= EXYNOS3250_MODE_SEL_422_1P_CHR_LUM; + break; + case V4L2_PIX_FMT_VYUY: + reg |= EXYNOS3250_MODE_SEL_422_1P_CHR_LUM | + EXYNOS3250_SRC_SWAP_UV; + break; + case V4L2_PIX_FMT_NV12: + reg |= EXYNOS3250_MODE_SEL_420_2P | EXYNOS3250_SRC_NV12; + break; + case V4L2_PIX_FMT_NV21: + reg |= EXYNOS3250_MODE_SEL_420_2P | EXYNOS3250_SRC_NV21; + break; + case V4L2_PIX_FMT_YUV420: + reg |= EXYNOS3250_MODE_SEL_420_3P; + break; + default: + break; + + } + + writel(reg, regs + EXYNOS3250_JPGCMOD); +} + +void exynos3250_jpeg_set_y16(void __iomem *regs, bool y16) +{ + u32 reg; + + reg = readl(regs + EXYNOS3250_JPGCMOD); + if (y16) + reg |= EXYNOS3250_MODE_Y16; + else + reg &= ~EXYNOS3250_MODE_Y16_MASK; + writel(reg, regs + EXYNOS3250_JPGCMOD); +} + +void exynos3250_jpeg_proc_mode(void __iomem *regs, unsigned int mode) +{ + u32 reg, m; + + if (mode == S5P_JPEG_ENCODE) + m = EXYNOS3250_PROC_MODE_COMPR; + else + m = EXYNOS3250_PROC_MODE_DECOMPR; + reg = readl(regs + EXYNOS3250_JPGMOD); + reg &= ~EXYNOS3250_PROC_MODE_MASK; + reg |= m; + writel(reg, regs + EXYNOS3250_JPGMOD); +} + +void exynos3250_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) +{ + u32 reg, m = 0; + + switch (mode) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_444: + m = EXYNOS3250_SUBSAMPLING_MODE_444; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + m = EXYNOS3250_SUBSAMPLING_MODE_422; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + m = EXYNOS3250_SUBSAMPLING_MODE_420; + break; + } + + reg = readl(regs + EXYNOS3250_JPGMOD); + reg &= ~EXYNOS3250_SUBSAMPLING_MODE_MASK; + reg |= m; + writel(reg, regs + EXYNOS3250_JPGMOD); +} + +unsigned int exynos3250_jpeg_get_subsampling_mode(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_JPGMOD) & + EXYNOS3250_SUBSAMPLING_MODE_MASK; +} + +void exynos3250_jpeg_dri(void __iomem *regs, unsigned int dri) +{ + u32 reg; + + reg = dri & EXYNOS3250_JPGDRI_MASK; + writel(reg, regs + EXYNOS3250_JPGDRI); +} + +void exynos3250_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n) +{ + unsigned long reg; + + reg = readl(regs + EXYNOS3250_QHTBL); + reg &= ~EXYNOS3250_QT_NUM_MASK(t); + reg |= (n << EXYNOS3250_QT_NUM_SHIFT(t)) & + EXYNOS3250_QT_NUM_MASK(t); + writel(reg, regs + EXYNOS3250_QHTBL); +} + +void exynos3250_jpeg_htbl_ac(void __iomem *regs, unsigned int t) +{ + unsigned long reg; + + reg = readl(regs + EXYNOS3250_QHTBL); + reg &= ~EXYNOS3250_HT_NUM_AC_MASK(t); + /* this driver uses table 0 for all color components */ + reg |= (0 << EXYNOS3250_HT_NUM_AC_SHIFT(t)) & + EXYNOS3250_HT_NUM_AC_MASK(t); + writel(reg, regs + EXYNOS3250_QHTBL); +} + +void exynos3250_jpeg_htbl_dc(void __iomem *regs, unsigned int t) +{ + unsigned long reg; + + reg = readl(regs + EXYNOS3250_QHTBL); + reg &= ~EXYNOS3250_HT_NUM_DC_MASK(t); + /* this driver uses table 0 for all color components */ + reg |= (0 << EXYNOS3250_HT_NUM_DC_SHIFT(t)) & + EXYNOS3250_HT_NUM_DC_MASK(t); + writel(reg, regs + EXYNOS3250_QHTBL); +} + +void exynos3250_jpeg_set_y(void __iomem *regs, unsigned int y) +{ + u32 reg; + + reg = y & EXYNOS3250_JPGY_MASK; + writel(reg, regs + EXYNOS3250_JPGY); +} + +void exynos3250_jpeg_set_x(void __iomem *regs, unsigned int x) +{ + u32 reg; + + reg = x & EXYNOS3250_JPGX_MASK; + writel(reg, regs + EXYNOS3250_JPGX); +} + +unsigned int exynos3250_jpeg_get_y(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_JPGY); +} + +unsigned int exynos3250_jpeg_get_x(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_JPGX); +} + +void exynos3250_jpeg_interrupts_enable(void __iomem *regs) +{ + u32 reg; + + reg = readl(regs + EXYNOS3250_JPGINTSE); + reg |= (EXYNOS3250_JPEG_DONE_EN | + EXYNOS3250_WDMA_DONE_EN | + EXYNOS3250_RDMA_DONE_EN | + EXYNOS3250_ENC_STREAM_INT_EN | + EXYNOS3250_CORE_DONE_EN | + EXYNOS3250_ERR_INT_EN | + EXYNOS3250_HEAD_INT_EN); + writel(reg, regs + EXYNOS3250_JPGINTSE); +} + +void exynos3250_jpeg_enc_stream_bound(void __iomem *regs, unsigned int size) +{ + u32 reg; + + reg = size & EXYNOS3250_ENC_STREAM_BOUND_MASK; + writel(reg, regs + EXYNOS3250_ENC_STREAM_BOUND); +} + +void exynos3250_jpeg_output_raw_fmt(void __iomem *regs, unsigned int fmt) +{ + u32 reg; + + switch (fmt) { + case V4L2_PIX_FMT_RGB32: + reg = EXYNOS3250_OUT_FMT_ARGB8888; + break; + case V4L2_PIX_FMT_BGR32: + reg = EXYNOS3250_OUT_FMT_ARGB8888 | EXYNOS3250_OUT_SWAP_RGB; + break; + case V4L2_PIX_FMT_RGB565: + reg = EXYNOS3250_OUT_FMT_RGB565; + break; + case V4L2_PIX_FMT_RGB565X: + reg = EXYNOS3250_OUT_FMT_RGB565 | EXYNOS3250_OUT_SWAP_RGB; + break; + case V4L2_PIX_FMT_YUYV: + reg = EXYNOS3250_OUT_FMT_422_1P_LUM_CHR; + break; + case V4L2_PIX_FMT_YVYU: + reg = EXYNOS3250_OUT_FMT_422_1P_LUM_CHR | + EXYNOS3250_OUT_SWAP_UV; + break; + case V4L2_PIX_FMT_UYVY: + reg = EXYNOS3250_OUT_FMT_422_1P_CHR_LUM; + break; + case V4L2_PIX_FMT_VYUY: + reg = EXYNOS3250_OUT_FMT_422_1P_CHR_LUM | + EXYNOS3250_OUT_SWAP_UV; + break; + case V4L2_PIX_FMT_NV12: + reg = EXYNOS3250_OUT_FMT_420_2P | EXYNOS3250_OUT_NV12; + break; + case V4L2_PIX_FMT_NV21: + reg = EXYNOS3250_OUT_FMT_420_2P | EXYNOS3250_OUT_NV21; + break; + case V4L2_PIX_FMT_YUV420: + reg = EXYNOS3250_OUT_FMT_420_3P; + break; + default: + reg = 0; + break; + } + + writel(reg, regs + EXYNOS3250_OUTFORM); +} + +void exynos3250_jpeg_jpgadr(void __iomem *regs, unsigned int addr) +{ + writel(addr, regs + EXYNOS3250_JPG_JPGADR); +} + +void exynos3250_jpeg_imgadr(void __iomem *regs, struct s5p_jpeg_addr *img_addr) +{ + writel(img_addr->y, regs + EXYNOS3250_LUMA_BASE); + writel(img_addr->cb, regs + EXYNOS3250_CHROMA_BASE); + writel(img_addr->cr, regs + EXYNOS3250_CHROMA_CR_BASE); +} + +void exynos3250_jpeg_stride(void __iomem *regs, unsigned int img_fmt, + unsigned int width) +{ + u32 reg_luma = 0, reg_cr = 0, reg_cb = 0; + + switch (img_fmt) { + case V4L2_PIX_FMT_RGB32: + reg_luma = 4 * width; + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + reg_luma = 2 * width; + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + reg_luma = width; + reg_cb = reg_luma; + break; + case V4L2_PIX_FMT_YUV420: + reg_luma = width; + reg_cb = reg_cr = reg_luma / 2; + break; + default: + break; + } + + writel(reg_luma, regs + EXYNOS3250_LUMA_STRIDE); + writel(reg_cb, regs + EXYNOS3250_CHROMA_STRIDE); + writel(reg_cr, regs + EXYNOS3250_CHROMA_CR_STRIDE); +} + +void exynos3250_jpeg_offset(void __iomem *regs, unsigned int x_offset, + unsigned int y_offset) +{ + u32 reg; + + reg = (y_offset << EXYNOS3250_LUMA_YY_OFFSET_SHIFT) & + EXYNOS3250_LUMA_YY_OFFSET_MASK; + reg |= (x_offset << EXYNOS3250_LUMA_YX_OFFSET_SHIFT) & + EXYNOS3250_LUMA_YX_OFFSET_MASK; + + writel(reg, regs + EXYNOS3250_LUMA_XY_OFFSET); + + reg = (y_offset << EXYNOS3250_CHROMA_YY_OFFSET_SHIFT) & + EXYNOS3250_CHROMA_YY_OFFSET_MASK; + reg |= (x_offset << EXYNOS3250_CHROMA_YX_OFFSET_SHIFT) & + EXYNOS3250_CHROMA_YX_OFFSET_MASK; + + writel(reg, regs + EXYNOS3250_CHROMA_XY_OFFSET); + + reg = (y_offset << EXYNOS3250_CHROMA_CR_YY_OFFSET_SHIFT) & + EXYNOS3250_CHROMA_CR_YY_OFFSET_MASK; + reg |= (x_offset << EXYNOS3250_CHROMA_CR_YX_OFFSET_SHIFT) & + EXYNOS3250_CHROMA_CR_YX_OFFSET_MASK; + + writel(reg, regs + EXYNOS3250_CHROMA_CR_XY_OFFSET); +} + +void exynos3250_jpeg_coef(void __iomem *base, unsigned int mode) +{ + if (mode == S5P_JPEG_ENCODE) { + writel(EXYNOS3250_JPEG_ENC_COEF1, + base + EXYNOS3250_JPG_COEF(1)); + writel(EXYNOS3250_JPEG_ENC_COEF2, + base + EXYNOS3250_JPG_COEF(2)); + writel(EXYNOS3250_JPEG_ENC_COEF3, + base + EXYNOS3250_JPG_COEF(3)); + } else { + writel(EXYNOS3250_JPEG_DEC_COEF1, + base + EXYNOS3250_JPG_COEF(1)); + writel(EXYNOS3250_JPEG_DEC_COEF2, + base + EXYNOS3250_JPG_COEF(2)); + writel(EXYNOS3250_JPEG_DEC_COEF3, + base + EXYNOS3250_JPG_COEF(3)); + } +} + +void exynos3250_jpeg_start(void __iomem *regs) +{ + writel(1, regs + EXYNOS3250_JSTART); +} + +void exynos3250_jpeg_rstart(void __iomem *regs) +{ + writel(1, regs + EXYNOS3250_JRSTART); +} + +unsigned int exynos3250_jpeg_get_int_status(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_JPGINTST); +} + +void exynos3250_jpeg_clear_int_status(void __iomem *regs, + unsigned int value) +{ + return writel(value, regs + EXYNOS3250_JPGINTST); +} + +unsigned int exynos3250_jpeg_operating(void __iomem *regs) +{ + return readl(regs + S5P_JPGOPR) & EXYNOS3250_JPGOPR_MASK; +} + +unsigned int exynos3250_jpeg_compressed_size(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_JPGCNT) & EXYNOS3250_JPGCNT_MASK; +} + +void exynos3250_jpeg_dec_stream_size(void __iomem *regs, + unsigned int size) +{ + writel(size & EXYNOS3250_DEC_STREAM_MASK, + regs + EXYNOS3250_DEC_STREAM_SIZE); +} + +void exynos3250_jpeg_dec_scaling_ratio(void __iomem *regs, + unsigned int sratio) +{ + switch (sratio) { + case 1: + default: + sratio = EXYNOS3250_DEC_SCALE_FACTOR_8_8; + break; + case 2: + sratio = EXYNOS3250_DEC_SCALE_FACTOR_4_8; + break; + case 4: + sratio = EXYNOS3250_DEC_SCALE_FACTOR_2_8; + break; + case 8: + sratio = EXYNOS3250_DEC_SCALE_FACTOR_1_8; + break; + } + + writel(sratio & EXYNOS3250_DEC_SCALE_FACTOR_MASK, + regs + EXYNOS3250_DEC_SCALING_RATIO); +} + +void exynos3250_jpeg_set_timer(void __iomem *regs, unsigned int time_value) +{ + time_value &= EXYNOS3250_TIMER_INIT_MASK; + + writel(EXYNOS3250_TIMER_INT_STAT | time_value, + regs + EXYNOS3250_TIMER_SE); +} + +unsigned int exynos3250_jpeg_get_timer_status(void __iomem *regs) +{ + return readl(regs + EXYNOS3250_TIMER_ST); +} + +void exynos3250_jpeg_clear_timer_status(void __iomem *regs) +{ + writel(EXYNOS3250_TIMER_INT_STAT, regs + EXYNOS3250_TIMER_ST); +} diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h new file mode 100644 index 0000000..b6e3be8 --- /dev/null +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h @@ -0,0 +1,60 @@ +/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jacek Anaszewski + * + * 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. + */ +#ifndef JPEG_HW_EXYNOS3250_H_ +#define JPEG_HW_EXYNOS3250_H_ + +#include +#include + +#include "jpeg-regs.h" + +void exynos3250_jpeg_reset(void __iomem *regs); +void exynos3250_jpeg_poweron(void __iomem *regs); +void exynos3250_jpeg_set_dma_num(void __iomem *regs); +void exynos3250_jpeg_clk_set(void __iomem *base); +void exynos3250_jpeg_input_raw_fmt(void __iomem *regs, unsigned int fmt); +void exynos3250_jpeg_output_raw_fmt(void __iomem *regs, unsigned int fmt); +void exynos3250_jpeg_set_y16(void __iomem *regs, bool y16); +void exynos3250_jpeg_proc_mode(void __iomem *regs, unsigned int mode); +void exynos3250_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode); +unsigned int exynos3250_jpeg_get_subsampling_mode(void __iomem *regs); +void exynos3250_jpeg_dri(void __iomem *regs, unsigned int dri); +void exynos3250_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n); +void exynos3250_jpeg_htbl_ac(void __iomem *regs, unsigned int t); +void exynos3250_jpeg_htbl_dc(void __iomem *regs, unsigned int t); +void exynos3250_jpeg_set_y(void __iomem *regs, unsigned int y); +void exynos3250_jpeg_set_x(void __iomem *regs, unsigned int x); +void exynos3250_jpeg_interrupts_enable(void __iomem *regs); +void exynos3250_jpeg_enc_stream_bound(void __iomem *regs, unsigned int size); +void exynos3250_jpeg_outform_raw(void __iomem *regs, unsigned long format); +void exynos3250_jpeg_jpgadr(void __iomem *regs, unsigned int addr); +void exynos3250_jpeg_imgadr(void __iomem *regs, struct s5p_jpeg_addr *img_addr); +void exynos3250_jpeg_stride(void __iomem *regs, unsigned int img_fmt, + unsigned int width); +void exynos3250_jpeg_offset(void __iomem *regs, unsigned int x_offset, + unsigned int y_offset); +void exynos3250_jpeg_coef(void __iomem *base, unsigned int mode); +void exynos3250_jpeg_start(void __iomem *regs); +void exynos3250_jpeg_rstart(void __iomem *regs); +unsigned int exynos3250_jpeg_get_int_status(void __iomem *regs); +void exynos3250_jpeg_clear_int_status(void __iomem *regs, + unsigned int value); +unsigned int exynos3250_jpeg_operating(void __iomem *regs); +unsigned int exynos3250_jpeg_compressed_size(void __iomem *regs); +void exynos3250_jpeg_dec_stream_size(void __iomem *regs, unsigned int size); +void exynos3250_jpeg_dec_scaling_ratio(void __iomem *regs, unsigned int sratio); +void exynos3250_jpeg_set_timer(void __iomem *regs, unsigned int time_value); +unsigned int exynos3250_jpeg_get_timer_status(void __iomem *regs); +void exynos3250_jpeg_set_timer_status(void __iomem *regs); +void exynos3250_jpeg_clear_timer_status(void __iomem *regs); + +#endif /* JPEG_HW_EXYNOS3250_H_ */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h index 57fb05b..050fc44 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-regs.h @@ -2,7 +2,7 @@ * * Register definition file for Samsung JPEG codec driver * - * Copyright (c) 2011-2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Author: Andrzej Pietrasiewicz @@ -373,5 +373,250 @@ /* JPEG AC chrominance (values) Huffman table register */ #define EXYNOS4_HUFF_TBL_HACCV 0x310 +/* Register and bit definitions for Exynos 3250 */ + +/* JPEG mode register */ +#define EXYNOS3250_JPGMOD 0x00 +#define EXYNOS3250_PROC_MODE_MASK (0x1 << 3) +#define EXYNOS3250_PROC_MODE_DECOMPR (0x1 << 3) +#define EXYNOS3250_PROC_MODE_COMPR (0x0 << 3) +#define EXYNOS3250_SUBSAMPLING_MODE_MASK (0x7 << 0) +#define EXYNOS3250_SUBSAMPLING_MODE_444 (0x0 << 0) +#define EXYNOS3250_SUBSAMPLING_MODE_422 (0x1 << 0) +#define EXYNOS3250_SUBSAMPLING_MODE_420 (0x2 << 0) +#define EXYNOS3250_SUBSAMPLING_MODE_411 (0x6 << 0) +#define EXYNOS3250_SUBSAMPLING_MODE_GRAY (0x3 << 0) + +/* JPEG operation status register */ +#define EXYNOS3250_JPGOPR 0x04 +#define EXYNOS3250_JPGOPR_MASK 0x01 + +/* Quantization and Huffman tables register */ +#define EXYNOS3250_QHTBL 0x08 +#define EXYNOS3250_QT_NUM_SHIFT(t) ((((t) - 1) << 1) + 8) +#define EXYNOS3250_QT_NUM_MASK(t) (0x3 << EXYNOS3250_QT_NUM_SHIFT(t)) + +/* Huffman tables */ +#define EXYNOS3250_HT_NUM_AC_SHIFT(t) (((t) << 1) - 1) +#define EXYNOS3250_HT_NUM_AC_MASK(t) (0x1 << EXYNOS3250_HT_NUM_AC_SHIFT(t)) + +#define EXYNOS3250_HT_NUM_DC_SHIFT(t) (((t) - 1) << 1) +#define EXYNOS3250_HT_NUM_DC_MASK(t) (0x1 << EXYNOS3250_HT_NUM_DC_SHIFT(t)) + +/* JPEG restart interval register */ +#define EXYNOS3250_JPGDRI 0x0c +#define EXYNOS3250_JPGDRI_MASK 0xffff + +/* JPEG vertical resolution register */ +#define EXYNOS3250_JPGY 0x10 +#define EXYNOS3250_JPGY_MASK 0xffff + +/* JPEG horizontal resolution register */ +#define EXYNOS3250_JPGX 0x14 +#define EXYNOS3250_JPGX_MASK 0xffff + +/* JPEG byte count register */ +#define EXYNOS3250_JPGCNT 0x18 +#define EXYNOS3250_JPGCNT_MASK 0xffffff + +/* JPEG interrupt mask register */ +#define EXYNOS3250_JPGINTSE 0x1c +#define EXYNOS3250_JPEG_DONE_EN (1 << 11) +#define EXYNOS3250_WDMA_DONE_EN (1 << 10) +#define EXYNOS3250_RDMA_DONE_EN (1 << 9) +#define EXYNOS3250_ENC_STREAM_INT_EN (1 << 8) +#define EXYNOS3250_CORE_DONE_EN (1 << 5) +#define EXYNOS3250_ERR_INT_EN (1 << 4) +#define EXYNOS3250_HEAD_INT_EN (1 << 3) + +/* JPEG interrupt status register */ +#define EXYNOS3250_JPGINTST 0x20 +#define EXYNOS3250_JPEG_DONE (1 << 11) +#define EXYNOS3250_WDMA_DONE (1 << 10) +#define EXYNOS3250_RDMA_DONE (1 << 9) +#define EXYNOS3250_ENC_STREAM_STAT (1 << 8) +#define EXYNOS3250_RESULT_STAT (1 << 5) +#define EXYNOS3250_STREAM_STAT (1 << 4) +#define EXYNOS3250_HEADER_STAT (1 << 3) + +/* + * Base address of the luma component DMA buffer + * of the raw input or output image. + */ +#define EXYNOS3250_LUMA_BASE 0x100 +#define EXYNOS3250_SRC_TILE_EN_MASK 0x100 + +/* Stride of source or destination luma raw image buffer */ +#define EXYNOS3250_LUMA_STRIDE 0x104 + +/* Horizontal/vertical offset of active region in luma raw image buffer */ +#define EXYNOS3250_LUMA_XY_OFFSET 0x108 +#define EXYNOS3250_LUMA_YY_OFFSET_SHIFT 18 +#define EXYNOS3250_LUMA_YY_OFFSET_MASK (0x1fff << EXYNOS3250_LUMA_YY_OFFSET_SHIFT) +#define EXYNOS3250_LUMA_YX_OFFSET_SHIFT 2 +#define EXYNOS3250_LUMA_YX_OFFSET_MASK (0x1fff << EXYNOS3250_LUMA_YX_OFFSET_SHIFT) + +/* + * Base address of the chroma(Cb) component DMA buffer + * of the raw input or output image. + */ +#define EXYNOS3250_CHROMA_BASE 0x10c + +/* Stride of source or destination chroma(Cb) raw image buffer */ +#define EXYNOS3250_CHROMA_STRIDE 0x110 + +/* Horizontal/vertical offset of active region in chroma(Cb) raw image buffer */ +#define EXYNOS3250_CHROMA_XY_OFFSET 0x114 +#define EXYNOS3250_CHROMA_YY_OFFSET_SHIFT 18 +#define EXYNOS3250_CHROMA_YY_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_YY_OFFSET_SHIFT) +#define EXYNOS3250_CHROMA_YX_OFFSET_SHIFT 2 +#define EXYNOS3250_CHROMA_YX_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_YX_OFFSET_SHIFT) + +/* + * Base address of the chroma(Cr) component DMA buffer + * of the raw input or output image. + */ +#define EXYNOS3250_CHROMA_CR_BASE 0x118 + +/* Stride of source or destination chroma(Cr) raw image buffer */ +#define EXYNOS3250_CHROMA_CR_STRIDE 0x11c + +/* Horizontal/vertical offset of active region in chroma(Cb) raw image buffer */ +#define EXYNOS3250_CHROMA_CR_XY_OFFSET 0x120 +#define EXYNOS3250_CHROMA_CR_YY_OFFSET_SHIFT 18 +#define EXYNOS3250_CHROMA_CR_YY_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_CR_YY_OFFSET_SHIFT) +#define EXYNOS3250_CHROMA_CR_YX_OFFSET_SHIFT 2 +#define EXYNOS3250_CHROMA_CR_YX_OFFSET_MASK (0x1fff << EXYNOS3250_CHROMA_CR_YX_OFFSET_SHIFT) + +/* Raw image data r/w address register */ +#define EXYNOS3250_JPG_IMGADR 0x50 + +/* Source or destination JPEG file DMA buffer address */ +#define EXYNOS3250_JPG_JPGADR 0x124 + +/* Coefficients for RGB-to-YCbCr converter register */ +#define EXYNOS3250_JPG_COEF(n) (0x128 + (((n) - 1) << 2)) +#define EXYNOS3250_COEF_SHIFT(j) ((3 - (j)) << 3) +#define EXYNOS3250_COEF_MASK(j) (0xff << EXYNOS3250_COEF_SHIFT(j)) + +/* Raw input format setting */ +#define EXYNOS3250_JPGCMOD 0x134 +#define EXYNOS3250_SRC_TILE_EN (0x1 << 10) +#define EXYNOS3250_SRC_NV_MASK (0x1 << 9) +#define EXYNOS3250_SRC_NV12 (0x0 << 9) +#define EXYNOS3250_SRC_NV21 (0x1 << 9) +#define EXYNOS3250_SRC_BIG_ENDIAN_MASK (0x1 << 8) +#define EXYNOS3250_SRC_BIG_ENDIAN (0x1 << 8) +#define EXYNOS3250_MODE_SEL_MASK (0x7 << 5) +#define EXYNOS3250_MODE_SEL_420_2P (0x0 << 5) +#define EXYNOS3250_MODE_SEL_422_1P_LUM_CHR (0x1 << 5) +#define EXYNOS3250_MODE_SEL_RGB565 (0x2 << 5) +#define EXYNOS3250_MODE_SEL_422_1P_CHR_LUM (0x3 << 5) +#define EXYNOS3250_MODE_SEL_ARGB8888 (0x4 << 5) +#define EXYNOS3250_MODE_SEL_420_3P (0x5 << 5) +#define EXYNOS3250_SRC_SWAP_RGB (0x1 << 3) +#define EXYNOS3250_SRC_SWAP_UV (0x1 << 2) +#define EXYNOS3250_MODE_Y16_MASK (0x1 << 1) +#define EXYNOS3250_MODE_Y16 (0x1 << 1) +#define EXYNOS3250_HALF_EN_MASK (0x1 << 0) +#define EXYNOS3250_HALF_EN (0x1 << 0) + +/* Power on/off and clock down control */ +#define EXYNOS3250_JPGCLKCON 0x138 +#define EXYNOS3250_CLK_DOWN_READY (0x1 << 1) +#define EXYNOS3250_POWER_ON (0x1 << 0) + +/* Start compression or decompression */ +#define EXYNOS3250_JSTART 0x13c + +/* Restart decompression after header analysis */ +#define EXYNOS3250_JRSTART 0x140 + +/* JPEG SW reset register */ +#define EXYNOS3250_SW_RESET 0x144 + +/* JPEG timer setting register */ +#define EXYNOS3250_TIMER_SE 0x148 +#define EXYNOS3250_TIMER_INT_EN_SHIFT 31 +#define EXYNOS3250_TIMER_INT_EN (1 << EXYNOS3250_TIMER_INT_EN_SHIFT) +#define EXYNOS3250_TIMER_INIT_MASK 0x7fffffff + +/* JPEG timer status register */ +#define EXYNOS3250_TIMER_ST 0x14c +#define EXYNOS3250_TIMER_INT_STAT_SHIFT 31 +#define EXYNOS3250_TIMER_INT_STAT (1 << EXYNOS3250_TIMER_INT_STAT_SHIFT) +#define EXYNOS3250_TIMER_CNT_SHIFT 0 +#define EXYNOS3250_TIMER_CNT_MASK 0x7fffffff + +/* Command status register */ +#define EXYNOS3250_COMSTAT 0x150 +#define EXYNOS3250_CUR_PROC_MODE (0x1 << 1) +#define EXYNOS3250_CUR_COM_MODE (0x1 << 0) + +/* JPEG decompression output format register */ +#define EXYNOS3250_OUTFORM 0x154 +#define EXYNOS3250_OUT_ALPHA_MASK (0xff << 24) +#define EXYNOS3250_OUT_TILE_EN (0x1 << 10) +#define EXYNOS3250_OUT_NV_MASK (0x1 << 9) +#define EXYNOS3250_OUT_NV12 (0x0 << 9) +#define EXYNOS3250_OUT_NV21 (0x1 << 9) +#define EXYNOS3250_OUT_BIG_ENDIAN_MASK (0x1 << 8) +#define EXYNOS3250_OUT_BIG_ENDIAN (0x1 << 8) +#define EXYNOS3250_OUT_SWAP_RGB (0x1 << 7) +#define EXYNOS3250_OUT_SWAP_UV (0x1 << 6) +#define EXYNOS3250_OUT_FMT_MASK (0x7 << 0) +#define EXYNOS3250_OUT_FMT_420_2P (0x0 << 0) +#define EXYNOS3250_OUT_FMT_422_1P_LUM_CHR (0x1 << 0) +#define EXYNOS3250_OUT_FMT_422_1P_CHR_LUM (0x3 << 0) +#define EXYNOS3250_OUT_FMT_420_3P (0x4 << 0) +#define EXYNOS3250_OUT_FMT_RGB565 (0x5 << 0) +#define EXYNOS3250_OUT_FMT_ARGB8888 (0x6 << 0) + +/* Input JPEG stream byte size for decompression */ +#define EXYNOS3250_DEC_STREAM_SIZE 0x158 +#define EXYNOS3250_DEC_STREAM_MASK 0x1fffffff + +/* The upper bound of the byte size of output compressed stream */ +#define EXYNOS3250_ENC_STREAM_BOUND 0x15c +#define EXYNOS3250_ENC_STREAM_BOUND_MASK 0xffffc0 + +/* Scale-down ratio when decoding */ +#define EXYNOS3250_DEC_SCALING_RATIO 0x160 +#define EXYNOS3250_DEC_SCALE_FACTOR_MASK 0x3 +#define EXYNOS3250_DEC_SCALE_FACTOR_8_8 0x0 +#define EXYNOS3250_DEC_SCALE_FACTOR_4_8 0x1 +#define EXYNOS3250_DEC_SCALE_FACTOR_2_8 0x2 +#define EXYNOS3250_DEC_SCALE_FACTOR_1_8 0x3 + +/* Error check */ +#define EXYNOS3250_CRC_RESULT 0x164 + +/* RDMA and WDMA operation status register */ +#define EXYNOS3250_DMA_OPER_STATUS 0x168 +#define EXYNOS3250_WDMA_OPER_STATUS (0x1 << 1) +#define EXYNOS3250_RDMA_OPER_STATUS (0x1 << 0) + +/* DMA issue gathering number and issue number settings */ +#define EXYNOS3250_DMA_ISSUE_NUM 0x16c +#define EXYNOS3250_WDMA_ISSUE_NUM_SHIFT 16 +#define EXYNOS3250_WDMA_ISSUE_NUM_MASK (0x7 << EXYNOS3250_WDMA_ISSUE_NUM_SHIFT) +#define EXYNOS3250_RDMA_ISSUE_NUM_SHIFT 8 +#define EXYNOS3250_RDMA_ISSUE_NUM_MASK (0x7 << EXYNOS3250_RDMA_ISSUE_NUM_SHIFT) +#define EXYNOS3250_ISSUE_GATHER_NUM_SHIFT 0 +#define EXYNOS3250_ISSUE_GATHER_NUM_MASK (0x7 << EXYNOS3250_ISSUE_GATHER_NUM_SHIFT) +#define EXYNOS3250_DMA_MO_COUNT 0x7 + +/* Version register */ +#define EXYNOS3250_VERSION 0x1fc + +/* RGB <-> YUV conversion coefficients */ +#define EXYNOS3250_JPEG_ENC_COEF1 0x01352e1e +#define EXYNOS3250_JPEG_ENC_COEF2 0x00b0ae83 +#define EXYNOS3250_JPEG_ENC_COEF3 0x020cdc13 + +#define EXYNOS3250_JPEG_DEC_COEF1 0x04a80199 +#define EXYNOS3250_JPEG_DEC_COEF2 0x04a9a064 +#define EXYNOS3250_JPEG_DEC_COEF3 0x04a80102 + #endif /* JPEG_REGS_H_ */ -- cgit v0.10.2 From a35f60031976d8b9930a2d32ebfdecd82d538066 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 11 Jul 2014 12:19:43 -0300 Subject: [media] s5p-jpeg: return error immediately after get_byte fails When parsing JPEG header s5p_jpeg_parse_hdr function should return immediately in case there was an error while reading a byte. Signed-off-by: Jacek Anaszewski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 126199e..a3f8862 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -906,14 +906,14 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, while (notfound) { c = get_byte(&jpeg_buffer); if (c == -1) - break; + return false; if (c != 0xff) continue; do c = get_byte(&jpeg_buffer); while (c == 0xff); if (c == -1) - break; + return false; if (c == 0) continue; length = 0; -- cgit v0.10.2 From e9e7dfe5ef577d07d20925f7d1680e246f0f8949 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 11 Jul 2014 12:19:44 -0300 Subject: [media] s5p-jpeg: Adjust jpeg_bound_align_image to Exynos3250 needs The jpeg_bound_align_image function needs to know the context in which it is called, as it needs to align image dimensions in a slight different manner for Exynos3250, which crops pixels for specific values in case the format is RGB. Signed-off-by: Jacek Anaszewski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index a3f8862..5ef7f5b 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1133,7 +1133,8 @@ static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, return NULL; } -static void jpeg_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, +static void jpeg_bound_align_image(struct s5p_jpeg_ctx *ctx, + u32 *w, unsigned int wmin, unsigned int wmax, unsigned int walign, u32 *h, unsigned int hmin, unsigned int hmax, unsigned int halign) @@ -1145,13 +1146,27 @@ static void jpeg_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, w_step = 1 << walign; h_step = 1 << halign; + + if (ctx->jpeg->variant->version == SJPEG_EXYNOS3250) { + /* + * Rightmost and bottommost pixels are cropped by the + * Exynos3250 JPEG IP for RGB formats, for the specific + * width and height values respectively. This assignment + * will result in v4l_bound_align_image returning dimensions + * reduced by 1 for the aforementioned cases. + */ + if (w_step == 4 && ((width & 3) == 1)) { + wmax = width; + hmax = height; + } + } + v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, 0); if (*w < width && (*w + w_step) < wmax) *w += w_step; if (*h < height && (*h + h_step) < hmax) *h += h_step; - } static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt, @@ -1167,12 +1182,12 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt, /* V4L2 specification suggests the driver corrects the format struct * if any of the dimensions is unsupported */ if (q_type == FMT_TYPE_OUTPUT) - jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH, + jpeg_bound_align_image(ctx, &pix->width, S5P_JPEG_MIN_WIDTH, S5P_JPEG_MAX_WIDTH, 0, &pix->height, S5P_JPEG_MIN_HEIGHT, S5P_JPEG_MAX_HEIGHT, 0); else - jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH, + jpeg_bound_align_image(ctx, &pix->width, S5P_JPEG_MIN_WIDTH, S5P_JPEG_MAX_WIDTH, fmt->h_align, &pix->height, S5P_JPEG_MIN_HEIGHT, S5P_JPEG_MAX_HEIGHT, fmt->v_align); @@ -1294,7 +1309,7 @@ static int exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx, else wh_align = 1; - jpeg_bound_align_image(&w, S5P_JPEG_MIN_WIDTH, + jpeg_bound_align_image(ctx, &w, S5P_JPEG_MIN_WIDTH, S5P_JPEG_MAX_WIDTH, wh_align, &h, S5P_JPEG_MIN_HEIGHT, S5P_JPEG_MAX_HEIGHT, wh_align); -- cgit v0.10.2 From fac4d961263d054658f42328ca7b7384d11a0487 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 11 Jul 2014 12:19:45 -0300 Subject: [media] s5p-jpeg: fix g_selection op V4L2_SEL_TGT_COMPOSE_DEFAULT switch case should select whole available area of the image and V4L2_SEL_TGT_COMPOSE should apply user settings. Signed-off-by: Jacek Anaszewski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 5ef7f5b..d11357f 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1505,21 +1505,23 @@ static int s5p_jpeg_g_selection(struct file *file, void *priv, case V4L2_SEL_TGT_CROP: case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_DEFAULT: s->r.width = ctx->out_q.w; s->r.height = ctx->out_q.h; + s->r.left = 0; + s->r.top = 0; break; + case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_BOUNDS: case V4L2_SEL_TGT_COMPOSE_PADDED: - s->r.width = ctx->cap_q.w; - s->r.height = ctx->cap_q.h; + s->r.width = ctx->crop_rect.width; + s->r.height = ctx->crop_rect.height; + s->r.left = ctx->crop_rect.left; + s->r.top = ctx->crop_rect.top; break; default: return -EINVAL; } - s->r.left = 0; - s->r.top = 0; return 0; } -- cgit v0.10.2 From dfd96900b29601f44410c9e85de8309ce8eadd02 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 11 Jul 2014 12:19:46 -0300 Subject: [media] s5p-jpeg: Assure proper crop rectangle initialization Assure proper crop_rect initialization in case the user space doesn't call S_SELECTION ioctl. Signed-off-by: Jacek Anaszewski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index d11357f..3e3d94d 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1367,6 +1367,21 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) V4L2_CID_JPEG_CHROMA_SUBSAMPLING); if (ctrl_subs) v4l2_ctrl_s_ctrl(ctrl_subs, q_data->fmt->subsampling); + ct->crop_altered = false; + } + + /* + * For decoding init crop_rect with capture buffer dimmensions which + * contain aligned dimensions of the input JPEG image and do it only + * if crop rectangle hasn't been altered by the user space e.g. with + * S_SELECTION ioctl. For encoding assign output buffer dimensions. + */ + if (!ct->crop_altered && + ((ct->mode == S5P_JPEG_DECODE && f_type == FMT_TYPE_CAPTURE) || + (ct->mode == S5P_JPEG_ENCODE && f_type == FMT_TYPE_OUTPUT))) { + ct->crop_rect.width = pix->width; + ct->crop_rect.height = pix->height; + } } return 0; -- cgit v0.10.2 From 5a71671af3727502386ac9b5497d6436286ed8a9 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 11 Jul 2014 12:19:47 -0300 Subject: [media] s5p-jpeg: Prevent erroneous downscaling for Exynos3250 SoC JPEG codec on Exynos3250 SoC produces broken raw image if a JPEG image is decoded to YUV420 format and downscaled by a factor greater than 2. Prevent this by asserting downscale ratio to 2. Signed-off-by: Jacek Anaszewski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 3e3d94d..eb13fdf 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1317,12 +1317,16 @@ static int exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx, return w * h * fmt_depth >> 3; } +static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, + struct v4l2_rect *r); + static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) { struct vb2_queue *vq; struct s5p_jpeg_q_data *q_data = NULL; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_ctrl *ctrl_subs; + struct v4l2_rect scale_rect; unsigned int f_type; vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); @@ -1382,6 +1386,20 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) ct->crop_rect.width = pix->width; ct->crop_rect.height = pix->height; } + + /* + * Prevent downscaling to YUV420 format by more than 2 + * for Exynos3250 SoC as it produces broken raw image + * in such cases. + */ + if (ct->mode == S5P_JPEG_DECODE && + f_type == FMT_TYPE_CAPTURE && + ct->jpeg->variant->version == SJPEG_EXYNOS3250 && + pix->pixelformat == V4L2_PIX_FMT_YUV420 && + ct->scale_factor > 2) { + scale_rect.width = ct->out_q.w / 2; + scale_rect.height = ct->out_q.h / 2; + exynos3250_jpeg_try_downscale(ct, &scale_rect); } return 0; -- cgit v0.10.2 From 39c344c4487a23c1399c384b78399b5947a47b50 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 11 Jul 2014 12:19:48 -0300 Subject: [media] s5p-jpeg: add chroma subsampling adjustment for Exynos3250 Take into account limitations specific to the Exynos3250 SoC, regarding setting the chroma subsampling control's value. Signed-off-by: Jacek Anaszewski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index eb13fdf..e66acbc 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1603,36 +1603,53 @@ static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl) return 0; } -static int s5p_jpeg_try_ctrl(struct v4l2_ctrl *ctrl) +static int s5p_jpeg_adjust_subs_ctrl(struct s5p_jpeg_ctx *ctx, int *ctrl_val) { - struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&ctx->jpeg->slock, flags); - - if (ctrl->id == V4L2_CID_JPEG_CHROMA_SUBSAMPLING) { - if (ctx->jpeg->variant->version == SJPEG_S5P) - goto error_free; + switch (ctx->jpeg->variant->version) { + case SJPEG_S5P: + return 0; + case SJPEG_EXYNOS3250: + /* + * The exynos3250 device can produce JPEG image only + * of 4:4:4 subsampling when given RGB32 source image. + */ + if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB32) + *ctrl_val = 0; + break; + case SJPEG_EXYNOS4: /* * The exynos4x12 device requires input raw image fourcc * to be V4L2_PIX_FMT_GREY if gray jpeg format * is to be set. */ if (ctx->out_q.fmt->fourcc != V4L2_PIX_FMT_GREY && - ctrl->val == V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) { - ret = -EINVAL; - goto error_free; - } - /* - * The exynos4x12 device requires resulting jpeg subsampling - * not to be lower than the input raw image subsampling. - */ - if (ctx->out_q.fmt->subsampling > ctrl->val) - ctrl->val = ctx->out_q.fmt->subsampling; + *ctrl_val == V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) + return -EINVAL; + break; } -error_free: + /* + * The exynos4x12 and exynos3250 devices require resulting + * jpeg subsampling not to be lower than the input raw image + * subsampling. + */ + if (ctx->out_q.fmt->subsampling > *ctrl_val) + *ctrl_val = ctx->out_q.fmt->subsampling; + + return 0; +} + +static int s5p_jpeg_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ctx->jpeg->slock, flags); + + if (ctrl->id == V4L2_CID_JPEG_CHROMA_SUBSAMPLING) + ret = s5p_jpeg_adjust_subs_ctrl(ctx, &ctrl->val); + spin_unlock_irqrestore(&ctx->jpeg->slock, flags); return ret; } -- cgit v0.10.2 From 55647a99361603957a9d2a1b59450290dada5fae Mon Sep 17 00:00:00 2001 From: Zhaowei Yuan Date: Wed, 23 Jul 2014 01:06:12 -0300 Subject: [media] s5p-mfc: remove unnecessary calling to function video_devdata() Since we have get vdev by calling video_devdata() at the beginning of s5p_mfc_open(), we should just use vdev instead of calling video_devdata() again in the following code. Signed-off-by: Zhaowei Yuan Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index e3f0b54..d35b041 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -724,7 +724,7 @@ static int s5p_mfc_open(struct file *file) ret = -ENOMEM; goto err_alloc; } - v4l2_fh_init(&ctx->fh, video_devdata(file)); + v4l2_fh_init(&ctx->fh, vdev); file->private_data = &ctx->fh; v4l2_fh_add(&ctx->fh); ctx->dev = dev; -- cgit v0.10.2 From b0787de1cb0d9c748fc63b6626a5e58fa2f14147 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 21 Jun 2014 19:19:55 -0300 Subject: [media] media: mt9m111: add device-tree documentation Add device-tree bindings documentation for the Micron mt9m111 image sensor. Signed-off-by: Robert Jarzmik Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/i2c/mt9m111.txt b/Documentation/devicetree/bindings/media/i2c/mt9m111.txt new file mode 100644 index 0000000..ed5a334 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/mt9m111.txt @@ -0,0 +1,28 @@ +Micron 1.3Mp CMOS Digital Image Sensor + +The Micron MT9M111 is a CMOS active pixel digital image sensor with an active +array size of 1280H x 1024V. It is programmable through a simple two-wire serial +interface. + +Required Properties: +- compatible: value should be "micron,mt9m111" + +For further reading on port node refer to +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + + i2c_master { + mt9m111@5d { + compatible = "micron,mt9m111"; + reg = <0x5d>; + + remote = <&pxa_camera>; + port { + mt9m111_1: endpoint { + bus-width = <8>; + remote-endpoint = <&pxa_camera>; + }; + }; + }; + }; -- cgit v0.10.2 From ff493eef396b2480c270c1174264418739021e57 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 29 Jun 2014 11:20:00 -0300 Subject: [media] media: soc_camera: pxa_camera documentation device-tree support Add device-tree bindings documentation for pxa_camera driver. Signed-off-by: Robert Jarzmik Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/pxa-camera.txt b/Documentation/devicetree/bindings/media/pxa-camera.txt new file mode 100644 index 0000000..11f5b5d --- /dev/null +++ b/Documentation/devicetree/bindings/media/pxa-camera.txt @@ -0,0 +1,43 @@ +Marvell PXA camera host interface + +Required properties: + - compatible: Should be "marvell,pxa270-qci" + - reg: register base and size + - interrupts: the interrupt number + - any required generic properties defined in video-interfaces.txt + +Optional properties: + - clocks: input clock (see clock-bindings.txt) + - clock-output-names: should contain the name of the clock driving the + sensor master clock MCLK + - clock-frequency: host interface is driving MCLK, and MCLK rate is this rate + +Example: + + pxa_camera: pxa_camera@50000000 { + compatible = "marvell,pxa270-qci"; + reg = <0x50000000 0x1000>; + interrupts = <33>; + + clocks = <&pxa2xx_clks 24>; + clock-names = "ciclk"; + clock-frequency = <50000000>; + clock-output-names = "qci_mclk"; + + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + /* Parallel bus endpoint */ + qci: endpoint@0 { + reg = <0>; /* Local endpoint # */ + remote-endpoint = <&mt9m111_1>; + bus-width = <8>; /* Used data lines */ + hsync-active = <0>; /* Active low */ + vsync-active = <0>; /* Active low */ + pclk-sample = <1>; /* Rising */ + }; + }; + }; -- cgit v0.10.2 From 1ddc6a6caa94e1e19e8c657a9a6cbbecd39e63bd Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Sat, 5 Jul 2014 19:26:23 -0300 Subject: [media] soc_camera: add support for dt binding soc_camera drivers Add initial support for OF based soc-camera devices that may be used by any of the soc-camera drivers. The driver itself will need converting to use OF. These changes allow the soc-camera driver to do the connecting of any async capable v4l2 device to the soc-camera driver. This has currently been tested on the Renesas Lager board. It currently only supports one input device per driver as this seems to be the standard connection for these devices. Signed-off-by: Ben Dooks [g.liakhovetski@gmx.de add check for multiple subdevices] Tested-by: Robert Jarzmik Tested-by: Josh Wu Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index dc626b9..f4308fe 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -1585,6 +1586,130 @@ static void scan_async_host(struct soc_camera_host *ici) #define scan_async_host(ici) do {} while (0) #endif +#ifdef CONFIG_OF + +struct soc_of_info { + struct soc_camera_async_subdev sasd; + struct soc_camera_async_client sasc; + struct v4l2_async_subdev *subdev; +}; + +static int soc_of_bind(struct soc_camera_host *ici, + struct device_node *ep, + struct device_node *remote) +{ + struct soc_camera_device *icd; + struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; + struct soc_camera_async_client *sasc; + struct soc_of_info *info; + struct i2c_client *client; + char clk_name[V4L2_SUBDEV_NAME_SIZE]; + int ret; + + /* allocate a new subdev and add match info to it */ + info = devm_kzalloc(ici->v4l2_dev.dev, sizeof(struct soc_of_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->sasd.asd.match.of.node = remote; + info->sasd.asd.match_type = V4L2_ASYNC_MATCH_OF; + info->subdev = &info->sasd.asd; + + /* Or shall this be managed by the soc-camera device? */ + sasc = &info->sasc; + + /* HACK: just need a != NULL */ + sdesc.host_desc.board_info = ERR_PTR(-ENODATA); + + ret = soc_camera_dyn_pdev(&sdesc, sasc); + if (ret < 0) + goto eallocpdev; + + sasc->sensor = &info->sasd.asd; + + icd = soc_camera_add_pdev(sasc); + if (!icd) { + ret = -ENOMEM; + goto eaddpdev; + } + + sasc->notifier.subdevs = &info->subdev; + sasc->notifier.num_subdevs = 1; + sasc->notifier.bound = soc_camera_async_bound; + sasc->notifier.unbind = soc_camera_async_unbind; + sasc->notifier.complete = soc_camera_async_complete; + + icd->sasc = sasc; + icd->parent = ici->v4l2_dev.dev; + + client = of_find_i2c_device_by_node(remote); + + if (client) + snprintf(clk_name, sizeof(clk_name), "%d-%04x", + client->adapter->nr, client->addr); + else + snprintf(clk_name, sizeof(clk_name), "of-%s", + of_node_full_name(remote)); + + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd); + if (IS_ERR(icd->clk)) { + ret = PTR_ERR(icd->clk); + goto eclkreg; + } + + ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); + if (!ret) + return 0; +eclkreg: + icd->clk = NULL; + platform_device_del(sasc->pdev); +eaddpdev: + platform_device_put(sasc->pdev); +eallocpdev: + devm_kfree(ici->v4l2_dev.dev, sasc); + dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); + + return ret; +} + +static void scan_of_host(struct soc_camera_host *ici) +{ + struct device *dev = ici->v4l2_dev.dev; + struct device_node *np = dev->of_node; + struct device_node *epn = NULL, *ren; + unsigned int i; + + for (i = 0; ; i++) { + epn = of_graph_get_next_endpoint(np, epn); + if (!epn) + break; + + ren = of_graph_get_remote_port(epn); + if (!ren) { + dev_notice(dev, "no remote for %s\n", + of_node_full_name(epn)); + continue; + } + + /* so we now have a remote node to connect */ + if (!i) + soc_of_bind(ici, epn, ren->parent); + + of_node_put(epn); + of_node_put(ren); + + if (i) { + dev_err(dev, "multiple subdevices aren't supported yet!\n"); + break; + } + } +} + +#else +static inline void scan_of_host(struct soc_camera_host *ici) { } +#endif + /* Called during host-driver probe */ static int soc_camera_probe(struct soc_camera_host *ici, struct soc_camera_device *icd) @@ -1836,7 +1961,9 @@ int soc_camera_host_register(struct soc_camera_host *ici) mutex_init(&ici->host_lock); mutex_init(&ici->clk_lock); - if (ici->asd_sizes) + if (ici->v4l2_dev.dev->of_node) + scan_of_host(ici); + else if (ici->asd_sizes) /* * No OF, host with a list of subdevices. Don't try to mix * modes by initialising some groups statically and some -- cgit v0.10.2 From c5f176dc19870a424d6089317030f7b59ea1bafc Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 21 Jun 2014 19:19:54 -0300 Subject: [media] media: mt9m111: add device-tree suppport Add device-tree support for mt9m111 camera sensor. Signed-off-by: Robert Jarzmik Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index ccf5940..b51e856 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -931,6 +931,12 @@ static int mt9m111_probe(struct i2c_client *client, struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; + if (client->dev.of_node) { + ssdd = devm_kzalloc(&client->dev, sizeof(*ssdd), GFP_KERNEL); + if (!ssdd) + return -ENOMEM; + client->dev.platform_data = ssdd; + } if (!ssdd) { dev_err(&client->dev, "mt9m111: driver needs platform data\n"); return -EINVAL; @@ -1015,6 +1021,11 @@ static int mt9m111_remove(struct i2c_client *client) return 0; } +static const struct of_device_id mt9m111_of_match[] = { + { .compatible = "micron,mt9m111", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mt9m111_of_match); static const struct i2c_device_id mt9m111_id[] = { { "mt9m111", 0 }, @@ -1025,6 +1036,7 @@ MODULE_DEVICE_TABLE(i2c, mt9m111_id); static struct i2c_driver mt9m111_i2c_driver = { .driver = { .name = "mt9m111", + .of_match_table = of_match_ptr(mt9m111_of_match), }, .probe = mt9m111_probe, .remove = mt9m111_remove, -- cgit v0.10.2 From e9a1d94fa85542d4f3046ac82d234a3c8349c948 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 29 Jun 2014 11:19:59 -0300 Subject: [media] media: pxa_camera device-tree support Add device-tree support to pxa_camera host driver. Signed-off-by: Robert Jarzmik [g.liakhovetski@gmx.de: added of_node_put()] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index d4df305..64dc80c 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -1650,6 +1651,68 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .set_bus_param = pxa_camera_set_bus_param, }; +static int pxa_camera_pdata_from_dt(struct device *dev, + struct pxa_camera_dev *pcdev) +{ + u32 mclk_rate; + struct device_node *np = dev->of_node; + struct v4l2_of_endpoint ep; + int err = of_property_read_u32(np, "clock-frequency", + &mclk_rate); + if (!err) { + pcdev->platform_flags |= PXA_CAMERA_MCLK_EN; + pcdev->mclk = mclk_rate; + } + + np = of_graph_get_next_endpoint(np, NULL); + if (!np) { + dev_err(dev, "could not find endpoint\n"); + return -EINVAL; + } + + err = v4l2_of_parse_endpoint(np, &ep); + if (err) { + dev_err(dev, "could not parse endpoint\n"); + goto out; + } + + switch (ep.bus.parallel.bus_width) { + case 4: + pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_4; + break; + case 5: + pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_5; + break; + case 8: + pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_8; + break; + case 9: + pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_9; + break; + case 10: + pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10; + break; + default: + break; + }; + + if (ep.bus.parallel.flags & V4L2_MBUS_MASTER) + pcdev->platform_flags |= PXA_CAMERA_MASTER; + if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + pcdev->platform_flags |= PXA_CAMERA_HSP; + if (ep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + pcdev->platform_flags |= PXA_CAMERA_VSP; + if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + pcdev->platform_flags |= PXA_CAMERA_PCLK_EN | PXA_CAMERA_PCP; + if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + pcdev->platform_flags |= PXA_CAMERA_PCLK_EN; + +out: + of_node_put(np); + + return err; +} + static int pxa_camera_probe(struct platform_device *pdev) { struct pxa_camera_dev *pcdev; @@ -1676,7 +1739,15 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->res = res; pcdev->pdata = pdev->dev.platform_data; - pcdev->platform_flags = pcdev->pdata->flags; + if (&pdev->dev.of_node && !pcdev->pdata) { + err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev); + } else { + pcdev->platform_flags = pcdev->pdata->flags; + pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; + } + if (err < 0) + return err; + if (!(pcdev->platform_flags & (PXA_CAMERA_DATAWIDTH_8 | PXA_CAMERA_DATAWIDTH_9 | PXA_CAMERA_DATAWIDTH_10))) { /* @@ -1693,7 +1764,6 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->width_flags |= 1 << 8; if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10) pcdev->width_flags |= 1 << 9; - pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; if (!pcdev->mclk) { dev_warn(&pdev->dev, "mclk == 0! Please, fix your platform data. " @@ -1799,10 +1869,17 @@ static const struct dev_pm_ops pxa_camera_pm = { .resume = pxa_camera_resume, }; +static const struct of_device_id pxa_camera_of_match[] = { + { .compatible = "marvell,pxa270-qci", }, + {}, +}; +MODULE_DEVICE_TABLE(of, pxa_camera_of_match); + static struct platform_driver pxa_camera_driver = { .driver = { .name = PXA_CAM_DRV_NAME, .pm = &pxa_camera_pm, + .of_match_table = of_match_ptr(pxa_camera_of_match), }, .probe = pxa_camera_probe, .remove = pxa_camera_remove, -- cgit v0.10.2 From 47c71bd61b772cd72159dff4aae43734257a485a Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Sat, 5 Jul 2014 19:26:22 -0300 Subject: [media] rcar_vin: add devicetree support Add support for devicetree probe for the rcar-vin driver. Signed-off-by: Ben Dooks [g.liakhovetski@gmx.de fix a typo, sort headers alphabetically] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt new file mode 100644 index 0000000..ba61782 --- /dev/null +++ b/Documentation/devicetree/bindings/media/rcar_vin.txt @@ -0,0 +1,86 @@ +Renesas RCar Video Input driver (rcar_vin) +------------------------------------------ + +The rcar_vin device provides video input capabilities for the Renesas R-Car +family of devices. The current blocks are always slaves and suppot one input +channel which can be either RGB, YUYV or BT656. + + - compatible: Must be one of the following + - "renesas,vin-r8a7791" for the R8A7791 device + - "renesas,vin-r8a7790" for the R8A7790 device + - "renesas,vin-r8a7779" for the R8A7779 device + - "renesas,vin-r8a7778" for the R8A7778 device + - reg: the register base and size for the device registers + - interrupts: the interrupt for the device + - clocks: Reference to the parent clock + +Additionally, an alias named vinX will need to be created to specify +which video input device this is. + +The per-board settings: + - port sub-node describing a single endpoint connected to the vin + as described in video-interfaces.txt[1]. Only the first one will + be considered as each vin interface has one input port. + + These settings are used to work out video input format and widths + into the system. + + +Device node example +------------------- + + aliases { + vin0 = &vin0; + }; + + vin0: vin@0xe6ef0000 { + compatible = "renesas,vin-r8a7790"; + clocks = <&mstp8_clks R8A7790_CLK_VIN0>; + reg = <0 0xe6ef0000 0 0x1000>; + interrupts = <0 188 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + +Board setup example (vin1 composite video input) +------------------------------------------------ + +&i2c2 { + status = "ok"; + pinctrl-0 = <&i2c2_pins>; + pinctrl-names = "default"; + + adv7180@20 { + compatible = "adi,adv7180"; + reg = <0x20>; + remote = <&vin1>; + + port { + adv7180: endpoint { + bus-width = <8>; + remote-endpoint = <&vin1ep0>; + }; + }; + }; +}; + +/* composite video input */ +&vin1 { + pinctrl-0 = <&vin1_pins>; + pinctrl-names = "default"; + + status = "ok"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + vin1ep0: endpoint { + remote-endpoint = <&adv7180>; + bus-width = <8>; + }; + }; +}; + + + +[1] video-interfaces.txt common video media interface diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 7c4299d..85d579f 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -31,6 +33,7 @@ #include #include #include +#include #include #include @@ -1390,6 +1393,17 @@ static struct soc_camera_host_ops rcar_vin_host_ops = { .init_videobuf2 = rcar_vin_init_videobuf2, }; +#ifdef CONFIG_OF +static struct of_device_id rcar_vin_of_table[] = { + { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 }, + { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 }, + { }, +}; +MODULE_DEVICE_TABLE(of, rcar_vin_of_table); +#endif + static struct platform_device_id rcar_vin_id_table[] = { { "r8a7791-vin", RCAR_GEN2 }, { "r8a7790-vin", RCAR_GEN2 }, @@ -1402,15 +1416,52 @@ MODULE_DEVICE_TABLE(platform, rcar_vin_id_table); static int rcar_vin_probe(struct platform_device *pdev) { + const struct of_device_id *match = NULL; struct rcar_vin_priv *priv; struct resource *mem; struct rcar_vin_platform_data *pdata; + unsigned int pdata_flags; int irq, ret; - pdata = pdev->dev.platform_data; - if (!pdata || !pdata->flags) { - dev_err(&pdev->dev, "platform data not set\n"); - return -EINVAL; + if (pdev->dev.of_node) { + struct v4l2_of_endpoint ep; + struct device_node *np; + + match = of_match_device(of_match_ptr(rcar_vin_of_table), + &pdev->dev); + + np = of_graph_get_next_endpoint(pdev->dev.of_node, NULL); + if (!np) { + dev_err(&pdev->dev, "could not find endpoint\n"); + return -EINVAL; + } + + ret = v4l2_of_parse_endpoint(np, &ep); + if (ret) { + dev_err(&pdev->dev, "could not parse endpoint\n"); + return ret; + } + + if (ep.bus_type == V4L2_MBUS_BT656) + pdata_flags = RCAR_VIN_BT656; + else { + pdata_flags = 0; + if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + pdata_flags |= RCAR_VIN_HSYNC_ACTIVE_LOW; + if (ep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + pdata_flags |= RCAR_VIN_VSYNC_ACTIVE_LOW; + } + + of_node_put(np); + + dev_dbg(&pdev->dev, "pdata_flags = %08x\n", pdata_flags); + } else { + pdata = pdev->dev.platform_data; + if (!pdata || !pdata->flags) { + dev_err(&pdev->dev, "platform data not set\n"); + return -EINVAL; + } + pdata_flags = pdata->flags; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1441,12 +1492,18 @@ static int rcar_vin_probe(struct platform_device *pdev) priv->ici.priv = priv; priv->ici.v4l2_dev.dev = &pdev->dev; - priv->ici.nr = pdev->id; priv->ici.drv_name = dev_name(&pdev->dev); priv->ici.ops = &rcar_vin_host_ops; - priv->pdata_flags = pdata->flags; - priv->chip = pdev->id_entry->driver_data; + priv->pdata_flags = pdata_flags; + if (!match) { + priv->ici.nr = pdev->id; + priv->chip = pdev->id_entry->driver_data; + } else { + priv->ici.nr = of_alias_get_id(pdev->dev.of_node, "vin"); + priv->chip = (enum chip_id)match->data; + }; + spin_lock_init(&priv->lock); INIT_LIST_HEAD(&priv->capture); @@ -1487,6 +1544,7 @@ static struct platform_driver rcar_vin_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rcar_vin_of_table), }, .id_table = rcar_vin_id_table, }; -- cgit v0.10.2 From 3900623bfaffde4eeb93ca0ba0475598a1b0cef5 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Fri, 25 Jul 2014 07:13:38 -0300 Subject: [media] media: atmel-isi: add v4l2 async probe support Signed-off-by: Josh Wu Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 38c723a..c0d4612 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -980,6 +980,11 @@ static int atmel_isi_probe(struct platform_device *pdev) soc_host->v4l2_dev.dev = &pdev->dev; soc_host->nr = pdev->id; + if (isi->pdata.asd_sizes) { + soc_host->asd = isi->pdata.asd; + soc_host->asd_sizes = isi->pdata.asd_sizes; + } + ret = soc_camera_host_register(soc_host); if (ret) { dev_err(&pdev->dev, "Unable to register soc camera host\n"); diff --git a/include/media/atmel-isi.h b/include/media/atmel-isi.h index 2b02347..c2e5703 100644 --- a/include/media/atmel-isi.h +++ b/include/media/atmel-isi.h @@ -106,6 +106,8 @@ #define ISI_DATAWIDTH_8 0x01 #define ISI_DATAWIDTH_10 0x02 +struct v4l2_async_subdev; + struct isi_platform_data { u8 has_emb_sync; u8 emb_crc_sync; @@ -118,6 +120,8 @@ struct isi_platform_data { u32 frate; /* Using for ISI_MCK */ u32 mck_hz; + struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ + int *asd_sizes; /* 0-terminated array of asd group sizes */ }; #endif /* __ATMEL_ISI_H__ */ -- cgit v0.10.2 From 833e1063266ebbf75934f1171c6546cdf33e9848 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Fri, 25 Jul 2014 07:13:39 -0300 Subject: [media] media: atmel-isi: convert the pdata from pointer to structure Now the platform data is initialized by allocation of isi structure. In the future, we use pdata to store the dt parameters. Signed-off-by: Josh Wu Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index c0d4612..d32c533 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -84,7 +84,7 @@ struct atmel_isi { struct clk *mck; unsigned int irq; - struct isi_platform_data *pdata; + struct isi_platform_data pdata; u16 width_flags; /* max 12 bits */ struct list_head video_buffer_list; @@ -350,7 +350,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) cfg1 &= ~ISI_CFG1_FRATE_DIV_MASK; /* Enable linked list */ - cfg1 |= isi->pdata->frate | ISI_CFG1_DISCR; + cfg1 |= isi->pdata.frate | ISI_CFG1_DISCR; /* Enable codec path and ISI */ ctrl = ISI_CTRL_CDC | ISI_CTRL_EN; @@ -795,7 +795,7 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd) /* Make choises, based on platform preferences */ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (isi->pdata->hsync_act_low) + if (isi->pdata.hsync_act_low) common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; else common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; @@ -803,7 +803,7 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd) if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { - if (isi->pdata->vsync_act_low) + if (isi->pdata.vsync_act_low) common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; else common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; @@ -811,7 +811,7 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd) if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (isi->pdata->pclk_act_falling) + if (isi->pdata.pclk_act_falling) common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; else common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; @@ -833,9 +833,9 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd) if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING; - if (isi->pdata->has_emb_sync) + if (isi->pdata.has_emb_sync) cfg1 |= ISI_CFG1_EMB_SYNC; - if (isi->pdata->full_mode) + if (isi->pdata.full_mode) cfg1 |= ISI_CFG1_FULL_MODE; isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); @@ -903,7 +903,7 @@ static int atmel_isi_probe(struct platform_device *pdev) if (IS_ERR(isi->pclk)) return PTR_ERR(isi->pclk); - isi->pdata = pdata; + memcpy(&isi->pdata, pdata, sizeof(isi->pdata)); isi->active = NULL; spin_lock_init(&isi->lock); INIT_LIST_HEAD(&isi->video_buffer_list); @@ -919,7 +919,7 @@ static int atmel_isi_probe(struct platform_device *pdev) /* Set ISI_MCK's frequency, it should be faster than pixel * clock. */ - ret = clk_set_rate(isi->mck, pdata->mck_hz); + ret = clk_set_rate(isi->mck, isi->pdata.mck_hz); if (ret < 0) return ret; } @@ -953,9 +953,9 @@ static int atmel_isi_probe(struct platform_device *pdev) goto err_ioremap; } - if (pdata->data_width_flags & ISI_DATAWIDTH_8) + if (isi->pdata.data_width_flags & ISI_DATAWIDTH_8) isi->width_flags = 1 << 7; - if (pdata->data_width_flags & ISI_DATAWIDTH_10) + if (isi->pdata.data_width_flags & ISI_DATAWIDTH_10) isi->width_flags |= 1 << 9; isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); -- cgit v0.10.2 From 8ff19bc437f8d42d8a03dafe2bb28c5b3fc1bff1 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Mon, 28 Jul 2014 04:25:17 -0300 Subject: [media] media: atmel-isi: add primary DT support This patch add the DT support for Atmel ISI driver. It use the same v4l2 DT interface that defined in video-interfaces.txt. Signed-off-by: Josh Wu Cc: devicetree@vger.kernel.org Reviewed-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/atmel-isi.txt b/Documentation/devicetree/bindings/media/atmel-isi.txt new file mode 100644 index 0000000..17e71b7 --- /dev/null +++ b/Documentation/devicetree/bindings/media/atmel-isi.txt @@ -0,0 +1,51 @@ +Atmel Image Sensor Interface (ISI) SoC Camera Subsystem +---------------------------------------------- + +Required properties: +- compatible: must be "atmel,at91sam9g45-isi" +- reg: physical base address and length of the registers set for the device; +- interrupts: should contain IRQ line for the ISI; +- clocks: list of clock specifiers, corresponding to entries in + the clock-names property; +- clock-names: must contain "isi_clk", which is the isi peripherial clock. + +ISI supports a single port node with parallel bus. It should contain one +'port' child node with child 'endpoint' node. Please refer to the bindings +defined in Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + isi: isi@f0034000 { + compatible = "atmel,at91sam9g45-isi"; + reg = <0xf0034000 0x4000>; + interrupts = <37 IRQ_TYPE_LEVEL_HIGH 5>; + + clocks = <&isi_clk>; + clock-names = "isi_clk"; + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_isi>; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isi_0: endpoint { + remote-endpoint = <&ov2640_0>; + bus-width = <8>; + }; + }; + }; + + i2c1: i2c@f0018000 { + ov2640: camera@0x30 { + compatible = "omnivision,ov2640"; + reg = <0x30>; + + port { + ov2640_0: endpoint { + remote-endpoint = <&isi_0>; + bus-width = <8>; + }; + }; + }; + }; diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index d32c533..3408b04 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #define MAX_BUFFER_NUM 32 @@ -33,6 +34,7 @@ #define VID_LIMIT_BYTES (16 * 1024 * 1024) #define MIN_FRAME_RATE 15 #define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE) +#define ISI_DEFAULT_MCLK_FREQ 25000000 /* Frame buffer descriptor */ struct fbd { @@ -876,6 +878,51 @@ static int atmel_isi_remove(struct platform_device *pdev) return 0; } +static int atmel_isi_probe_dt(struct atmel_isi *isi, + struct platform_device *pdev) +{ + struct device_node *np= pdev->dev.of_node; + struct v4l2_of_endpoint ep; + int err; + + /* Default settings for ISI */ + isi->pdata.full_mode = 1; + isi->pdata.mck_hz = ISI_DEFAULT_MCLK_FREQ; + isi->pdata.frate = ISI_CFG1_FRATE_CAPTURE_ALL; + + np = of_graph_get_next_endpoint(np, NULL); + if (!np) { + dev_err(&pdev->dev, "Could not find the endpoint\n"); + return -EINVAL; + } + + err = v4l2_of_parse_endpoint(np, &ep); + if (err) { + dev_err(&pdev->dev, "Could not parse the endpoint\n"); + goto err_probe_dt; + } + + switch (ep.bus.parallel.bus_width) { + case 8: + isi->pdata.data_width_flags = ISI_DATAWIDTH_8; + break; + case 10: + isi->pdata.data_width_flags = + ISI_DATAWIDTH_8 | ISI_DATAWIDTH_10; + break; + default: + dev_err(&pdev->dev, "Unsupported bus width: %d\n", + ep.bus.parallel.bus_width); + err = -EINVAL; + goto err_probe_dt; + } + +err_probe_dt: + of_node_put(np); + + return err; +} + static int atmel_isi_probe(struct platform_device *pdev) { unsigned int irq; @@ -887,7 +934,7 @@ static int atmel_isi_probe(struct platform_device *pdev) struct isi_platform_data *pdata; pdata = dev->platform_data; - if (!pdata || !pdata->data_width_flags) { + if ((!pdata || !pdata->data_width_flags) && !pdev->dev.of_node) { dev_err(&pdev->dev, "No config available for Atmel ISI\n"); return -EINVAL; @@ -903,7 +950,14 @@ static int atmel_isi_probe(struct platform_device *pdev) if (IS_ERR(isi->pclk)) return PTR_ERR(isi->pclk); - memcpy(&isi->pdata, pdata, sizeof(isi->pdata)); + if (pdata) { + memcpy(&isi->pdata, pdata, sizeof(isi->pdata)); + } else { + ret = atmel_isi_probe_dt(isi, pdev); + if (ret) + return ret; + } + isi->active = NULL; spin_lock_init(&isi->lock); INIT_LIST_HEAD(&isi->video_buffer_list); @@ -1005,11 +1059,18 @@ err_alloc_ctx: return ret; } +static const struct of_device_id atmel_isi_of_match[] = { + { .compatible = "atmel,at91sam9g45-isi" }, + { } +}; +MODULE_DEVICE_TABLE(of, atmel_isi_of_match); + static struct platform_driver atmel_isi_driver = { .remove = atmel_isi_remove, .driver = { .name = "atmel_isi", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_isi_of_match), }, }; -- cgit v0.10.2 From e3994db108918d9abe4cfc5b98eed23dbcc3d590 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 30 Jul 2014 00:38:42 -0300 Subject: [media] MAINTAINERS: Update solo6x10 patterns commit 28cae868cd24 ("[media] solo6x10: move out of staging into drivers/media/pci") moved the files, update the patterns. Signed-off-by: Joe Perches cc: Ismael Luceno Acked-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 8982036..efd3e4a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8354,6 +8354,12 @@ M: Chris Boot S: Maintained F: drivers/leds/leds-net48xx.c +SOFTLOGIC 6x10 MPEG CODEC +M: Ismael Luceno +L: linux-media@vger.kernel.org +S: Supported +F: drivers/media/pci/solo6x10/ + SOFTWARE RAID (Multiple Disks) SUPPORT M: Neil Brown L: linux-raid@vger.kernel.org @@ -8657,11 +8663,6 @@ M: Christopher Harrer S: Odd Fixes F: drivers/staging/slicoss/ -STAGING - SOFTLOGIC 6x10 MPEG CODEC -M: Ismael Luceno -S: Supported -F: drivers/staging/media/solo6x10/ - STAGING - SPEAKUP CONSOLE SPEECH DRIVER M: William Hubbs M: Chris Brannon -- cgit v0.10.2 From 7eea35fe3df80e1a008203e59487185d5d04a5a8 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 30 Jul 2014 05:09:23 -0300 Subject: [media] MAINTAINERS: Update go7007 pattern Commit 7955f03d18d1 ("[media] go7007: move out of staging into drivers/media/usb") moved the files, update the pattern. Signed-off-by: Joe Perches Acked-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index efd3e4a..4cdf24c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3986,6 +3986,12 @@ F: Documentation/isdn/README.gigaset F: drivers/isdn/gigaset/ F: include/uapi/linux/gigaset_dev.h +GO7007 MPEG CODEC +M: Hans Verkuil +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/usb/go7007/ + GPIO SUBSYSTEM M: Linus Walleij M: Alexandre Courbot @@ -8596,11 +8602,6 @@ M: David Täht S: Odd Fixes F: drivers/staging/frontier/ -STAGING - GO7007 MPEG CODEC -M: Hans Verkuil -S: Maintained -F: drivers/staging/media/go7007/ - STAGING - INDUSTRIAL IO M: Jonathan Cameron L: linux-iio@vger.kernel.org -- cgit v0.10.2 From 27dcb00d0dc1d532b0da940e35a6d020ee33bd47 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 29 Jul 2014 20:21:59 -0300 Subject: [media] radio-miropcm20: fix sparse NULL pointer warning Fixes the following sparse warnings: drivers/media/radio/radio-miropcm20.c:193:33: warning: Using plain integer as NULL pointer Signed-off-by: Wei Yongjun Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index ac9915d..998919e 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -190,7 +190,7 @@ static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) freql = freq & 0xff; freqh = freq >> 8; - rds_cmd(aci, RDS_RESET, 0, 0); + rds_cmd(aci, RDS_RESET, NULL, 0); return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh); } -- cgit v0.10.2 From ee27aa901805dfc9c3a5eb5f0948eba9ab481ba1 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 24 Jul 2014 16:50:03 -0300 Subject: [media] coda: fix build error by making reset control optional make reset control optional for i.MX27 The patch "[media] coda: add reset control support" introduced a build failure if CONFIG_RESET_CONTROLLER is disabled: drivers/media/platform/coda.c:3734:2: error: implicit declaration of function 'devm_reset_control_get' Since not all SoCs containing CODA VPUs do have a system reset controller, use devm_reset_control_get_optional to make it optional. Reported-by: Shawn Guo Reported-by: Olof's autobuilder Reported-by: kbuild test robot Signed-off-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index c44feee..3a6d1d2 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -3780,10 +3780,10 @@ static int coda_probe(struct platform_device *pdev) return ret; } - dev->rstc = devm_reset_control_get(&pdev->dev, NULL); + dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL); if (IS_ERR(dev->rstc)) { ret = PTR_ERR(dev->rstc); - if (ret == -ENOENT) { + if (ret == -ENOENT || ret == -ENOSYS) { dev->rstc = NULL; } else { dev_err(&pdev->dev, "failed get reset control: %d\n", ret); -- cgit v0.10.2 From 0f3bf3dc1ca394a8385079a5653088672b65c5c4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Jun 2014 11:15:41 -0300 Subject: [media] cx23885: fix UNSET/TUNER_ABSENT confusion Sometimes dev->tuner_type is compared to UNSET, sometimes to TUNER_ABSENT, but these defines have different values. Standardize to TUNER_ABSENT. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c index 95666ee..bf89fc8 100644 --- a/drivers/media/pci/cx23885/cx23885-417.c +++ b/drivers/media/pci/cx23885/cx23885-417.c @@ -1266,7 +1266,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; - if (UNSET == dev->tuner_type) + if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; if (0 != t->index) return -EINVAL; @@ -1284,7 +1284,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; - if (UNSET == dev->tuner_type) + if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; /* Update the A/V core */ @@ -1299,7 +1299,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; - if (UNSET == dev->tuner_type) + if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; f->type = V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; @@ -1347,7 +1347,7 @@ static int vidioc_querycap(struct file *file, void *priv, V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | 0; - if (UNSET != dev->tuner_type) + if (dev->tuner_type != TUNER_ABSENT) cap->capabilities |= V4L2_CAP_TUNER; return 0; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 71e3ec3..91e4cb4 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -1157,7 +1157,7 @@ static int vidioc_querycap(struct file *file, void *priv, V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_VBI_CAPTURE; - if (UNSET != dev->tuner_type) + if (dev->tuner_type != TUNER_ABSENT) cap->capabilities |= V4L2_CAP_TUNER; return 0; } @@ -1475,7 +1475,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, { struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - if (unlikely(UNSET == dev->tuner_type)) + if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; if (0 != t->index) return -EINVAL; @@ -1491,7 +1491,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, { struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - if (UNSET == dev->tuner_type) + if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; if (0 != t->index) return -EINVAL; @@ -1507,7 +1507,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct cx23885_fh *fh = priv; struct cx23885_dev *dev = fh->dev; - if (unlikely(UNSET == dev->tuner_type)) + if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */ @@ -1523,7 +1523,7 @@ static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency { struct v4l2_control ctrl; - if (unlikely(UNSET == dev->tuner_type)) + if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; if (unlikely(f->tuner != 0)) return -EINVAL; -- cgit v0.10.2