From c859e6ef33ac0c9a5e9e934fe11a2232752b4e96 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 30 Jun 2013 12:43:58 -0300 Subject: [media] dib0700: add support for PCTV 2002e & PCTV 2002e SE Signed-off-by: Michael 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 886da16..419a2d6 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -369,4 +369,6 @@ #define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500 #define USB_PID_CPYTO_REDI_PC50A 0xa803 #define USB_PID_CTVDIGDUAL_V2 0xe410 +#define USB_PID_PCTV_2002E 0x025c +#define USB_PID_PCTV_2002E_SE 0x025d #endif diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index f081360..829323e 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -3589,6 +3589,8 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7790P) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE8096P) }, /* 80 */{ USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_2) }, + { USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_2002E) }, + { USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_2002E_SE) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -3993,12 +3995,20 @@ struct dvb_usb_device_properties dib0700_devices[] = { } }, - .num_device_descs = 1, + .num_device_descs = 3, .devices = { { "Hauppauge Nova-TD Stick (52009)", { &dib0700_usb_id_table[35], NULL }, { NULL }, }, + { "PCTV 2002e", + { &dib0700_usb_id_table[81], NULL }, + { NULL }, + }, + { "PCTV 2002e SE", + { &dib0700_usb_id_table[82], NULL }, + { NULL }, + }, }, .rc.core = { -- cgit v0.10.2 From 33bdd5a88a0fb7fbd08947261b243fcec4ff089d Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 3 Jun 2013 04:23:48 -0300 Subject: [media] mem2mem: add support for hardware buffered queue On mem2mem decoders with a hardware bitstream ringbuffer, to drain the buffer at the end of the stream, remaining frames might need to be decoded from the bitstream buffer without additional input buffers being provided. To achieve this, allow a queue to be marked as buffered by the driver, and allow scheduling of device_runs when buffered ready queues are empty. This also allows a driver to copy input buffers into their bitstream ringbuffer and immediately mark them as done to be dequeued. The motivation for this patch is hardware assisted h.264 reordering support in the coda driver. For high profile streams, the coda can hold back out-of-order frames, causing a few mem2mem device runs in the beginning, that don't produce any decompressed buffer at the v4l2 capture side. At the same time, the last few frames can be decoded from the bitstream with mem2mem device runs that don't need a new input buffer at the v4l2 output side. The decoder command ioctl can be used to put the decoder into the ringbuffer draining end-of-stream mode. Signed-off-by: Philipp Zabel Acked-by: Sylwester Nawrocki Signed-off-by: Kamil Debski 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 e96497f..89b9067 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -196,6 +196,10 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) * 2) at least one destination buffer has to be queued, * 3) streaming has to be on. * + * If a queue is buffered (for example a decoder hardware ringbuffer that has + * to be drained before doing streamoff), allow scheduling without v4l2 buffers + * on that queue. + * * There may also be additional, custom requirements. In such case the driver * should supply a custom callback (job_ready in v4l2_m2m_ops) that should * return 1 if the instance is ready. @@ -224,7 +228,8 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) } spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); - if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) { + if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue) + && !m2m_ctx->out_q_ctx.buffered) { spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); @@ -232,7 +237,8 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) return; } spin_lock_irqsave(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); - if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) { + if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue) + && !m2m_ctx->cap_q_ctx.buffered) { spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 0f4555b..44542a2 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -60,6 +60,7 @@ struct v4l2_m2m_queue_ctx { struct list_head rdy_queue; spinlock_t rdy_spinlock; u8 num_rdy; + bool buffered; }; struct v4l2_m2m_ctx { @@ -134,6 +135,18 @@ struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev, void *drv_priv, int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)); +static inline void v4l2_m2m_set_src_buffered(struct v4l2_m2m_ctx *m2m_ctx, + bool buffered) +{ + m2m_ctx->out_q_ctx.buffered = buffered; +} + +static inline void v4l2_m2m_set_dst_buffered(struct v4l2_m2m_ctx *m2m_ctx, + bool buffered) +{ + m2m_ctx->cap_q_ctx.buffered = buffered; +} + void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx); void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb); -- cgit v0.10.2 From e2c257f4588e3e2de1d3a9f203644ff6619ad886 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 23 Jul 2013 09:15:31 -0300 Subject: [media] dvb-usb-v2: fix Kconfig dependency when RC_CORE=m It is not allowed to build driver as a build-in when RC_CORE (remote controller support) is build as a module. randconfig build error with next-20130719, in drivers/media/usb drivers/built-in.o: In function `dvb_usbv2_disconnect': (.text+0x154b39): undefined reference to `rc_unregister_device' drivers/built-in.o: In function `dvb_usbv2_init_work': dvb_usb_core.c:(.text+0x155b2d): undefined reference to `rc_allocate_device' dvb_usb_core.c:(.text+0x155c38): undefined reference to `rc_register_device' dvb_usb_core.c:(.text+0x155c5b): undefined reference to `rc_free_device' drivers/built-in.o: In function `anysee_rc_query': anysee.c:(.text+0x157795): undefined reference to `rc_keydown' Reported-by: Jim Davis Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index a3c8ecf..2059d0c 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -1,6 +1,6 @@ config DVB_USB_V2 tristate "Support for various USB DVB devices v2" - depends on DVB_CORE && USB && I2C + depends on DVB_CORE && USB && I2C && (RC_CORE || RC_CORE=n) help By enabling this you will be able to choose the various supported USB1.1 and USB2.0 DVB devices. -- cgit v0.10.2 From 366108f0ecfddc1957e02e9cc42db4c2e1053e24 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 21 Jun 2013 03:55:27 -0300 Subject: [media] coda: use vb2_set_plane_payload instead of setting v4l2_planes[0].bytesused directly As stated in the vb2_buffer documentation, drivers should not directly fill in v4l2_planes[0].bytesused, but should use the vb2_set_plane_payload() function instead. No functional changes. 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 df4ada88..ef3bb8b 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -1671,12 +1671,12 @@ static irqreturn_t coda_irq_handler(int irq, void *data) wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)); /* Calculate bytesused field */ if (dst_buf->v4l2_buf.sequence == 0) { - dst_buf->v4l2_planes[0].bytesused = (wr_ptr - start_ptr) + - ctx->vpu_header_size[0] + - ctx->vpu_header_size[1] + - ctx->vpu_header_size[2]; + vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr + + ctx->vpu_header_size[0] + + ctx->vpu_header_size[1] + + ctx->vpu_header_size[2]); } else { - dst_buf->v4l2_planes[0].bytesused = (wr_ptr - start_ptr); + vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr); } v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n", -- cgit v0.10.2 From c2d2251ac941a165315dcf55fa9ee00e66512e3a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 21 Jun 2013 03:55:28 -0300 Subject: [media] coda: dynamic IRAM setup for encoder This sets up IRAM areas used as temporary memory for the different hardware units depending on the frame 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 ef3bb8b..f6d790a 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -160,6 +160,18 @@ struct coda_params { u32 slice_max_mb; }; +struct coda_iram_info { + u32 axi_sram_use; + phys_addr_t buf_bit_use; + phys_addr_t buf_ip_ac_dc_use; + phys_addr_t buf_dbk_y_use; + phys_addr_t buf_dbk_c_use; + phys_addr_t buf_ovl_use; + phys_addr_t buf_btp_use; + phys_addr_t search_ram_paddr; + int search_ram_size; +}; + struct coda_ctx { struct coda_dev *dev; struct list_head list; @@ -182,6 +194,7 @@ struct coda_ctx { struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS]; int num_internal_frames; int idx; + struct coda_iram_info iram_info; }; static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff, @@ -800,6 +813,10 @@ static void coda_device_run(void *m2m_priv) CODA7_REG_BIT_AXI_SRAM_USE); } + if (dev->devtype->product != CODA_DX6) + 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); @@ -1035,6 +1052,110 @@ static int coda_h264_padding(int size, char *p) return nal_size; } +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 mb_width; + int me_size; + int size; + + memset(iram_info, 0, sizeof(*iram_info)); + size = dev->iram_size; + + if (dev->devtype->product == CODA_DX6) + return; + + if (ctx->inst_type == CODA_INST_ENCODER) { + struct coda_q_data *q_data_src; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + 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; + } + + /* 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 { + goto out; + } + + 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 { + goto out; + } + + 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; + } + + /* OVL disabled for encoder */ + } + +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"); + + if (dev->devtype->product == CODA_7541) { + /* TODO - Enabling these causes picture errors on CODA7541 */ + if (ctx->inst_type == CODA_INST_ENCODER) { + iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | + CODA7_USE_HOST_DBK_ENABLE | + CODA7_USE_IP_ENABLE | + CODA7_USE_DBK_ENABLE); + } + } +} + static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf, int header_code, u8 *header, int *size) { @@ -1207,6 +1328,8 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) } coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); + coda_setup_iram(ctx); + if (dst_fourcc == V4L2_PIX_FMT_H264) { value = (FMO_SLICE_SAVE_BUF_SIZE << 7); value |= (0 & CODA_FMOPARAM_TYPE_MASK) << CODA_FMOPARAM_TYPE_OFFSET; @@ -1214,8 +1337,10 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) if (dev->devtype->product == CODA_DX6) { coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO); } else { - coda_write(dev, dev->iram_paddr, CODA7_CMD_ENC_SEQ_SEARCH_BASE); - coda_write(dev, 48 * 1024, CODA7_CMD_ENC_SEQ_SEARCH_SIZE); + 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); } } @@ -1240,12 +1365,16 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) 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_DX6) { - coda_write(dev, round_up(q_data_src->width, 8), CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE); - coda_write(dev, dev->iram_paddr + 48 * 1024, CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); - coda_write(dev, dev->iram_paddr + 53 * 1024, CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); - coda_write(dev, dev->iram_paddr + 58 * 1024, CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); - coda_write(dev, dev->iram_paddr + 68 * 1024, CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); - coda_write(dev, 0x0, CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); + coda_write(dev, ctx->iram_info.buf_bit_use, + CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); + coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, + CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_y_use, + CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_c_use, + CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); + coda_write(dev, ctx->iram_info.buf_ovl_use, + CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); } ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF); if (ret < 0) { diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda.h index ace0bf0..39c17c6 100644 --- a/drivers/media/platform/coda.h +++ b/drivers/media/platform/coda.h @@ -47,10 +47,17 @@ #define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x)) #define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140 #define CODA7_REG_BIT_AXI_SRAM_USE 0x140 -#define CODA7_USE_BIT_ENABLE (1 << 0) +#define CODA7_USE_HOST_ME_ENABLE (1 << 11) +#define CODA7_USE_HOST_OVL_ENABLE (1 << 10) +#define CODA7_USE_HOST_DBK_ENABLE (1 << 9) +#define CODA7_USE_HOST_IP_ENABLE (1 << 8) #define CODA7_USE_HOST_BIT_ENABLE (1 << 7) #define CODA7_USE_ME_ENABLE (1 << 4) -#define CODA7_USE_HOST_ME_ENABLE (1 << 11) +#define CODA7_USE_OVL_ENABLE (1 << 3) +#define CODA7_USE_DBK_ENABLE (1 << 2) +#define CODA7_USE_IP_ENABLE (1 << 1) +#define CODA7_USE_BIT_ENABLE (1 << 0) + #define CODA_REG_BIT_BUSY 0x160 #define CODA_REG_BIT_BUSY_FLAG 1 #define CODA_REG_BIT_RUN_COMMAND 0x164 -- cgit v0.10.2 From 2039749ec219b7cbc81dfa509423d3330eaf07d0 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 21 Jun 2013 03:55:29 -0300 Subject: [media] coda: do not allocate maximum number of framebuffers for encoder The encoder only ever needs two buffers, but we'll have to increase CODA_MAX_FRAMEBUFFERS for the decoder. 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 f6d790a..6597d67 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -1006,7 +1006,6 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d ysize = round_up(q_data->width, 8) * height; /* Allocate frame buffers */ - ctx->num_internal_frames = CODA_MAX_FRAMEBUFFERS; for (i = 0; i < ctx->num_internal_frames; i++) { ctx->internal_frames[i].size = q_data->sizeimage; if (fourcc == V4L2_PIX_FMT_H264 && dev->devtype->product != CODA_DX6) @@ -1356,6 +1355,7 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) goto out; } + 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"); -- cgit v0.10.2 From 5677e3b04d3b3961200aa2bb9cc715e709eafeb9 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 21 Jun 2013 03:55:30 -0300 Subject: [media] coda: update CODA7541 to firmware 1.4.50 This patch splits the global workbuf into a global tempbuf and a per-context workbuf, adds the codec mode aux register, and restores the work buffer pointer on commands. With the new firmware, there is only a single set of read/write pointers which need to be restored between context switches. This allows more than four active contexts at the same time. All auxiliary buffers are now allocated through a helper function to avoid code duplication. 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 6597d67..5aafc7c 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -41,7 +41,8 @@ #define CODA_FMO_BUF_SIZE 32 #define CODADX6_WORK_BUF_SIZE (288 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024) -#define CODA7_WORK_BUF_SIZE (512 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024) +#define CODA7_WORK_BUF_SIZE (128 * 1024) +#define CODA7_TEMP_BUF_SIZE (304 * 1024) #define CODA_PARA_BUF_SIZE (10 * 1024) #define CODA_ISRAM_SIZE (2048 * 2) #define CODADX6_IRAM_SIZE 0xb000 @@ -129,6 +130,7 @@ struct coda_dev { struct clk *clk_ahb; struct coda_aux_buf codebuf; + struct coda_aux_buf tempbuf; struct coda_aux_buf workbuf; struct gen_pool *iram_pool; long unsigned int iram_vaddr; @@ -153,6 +155,7 @@ struct coda_params { u8 mpeg4_inter_qp; u8 gop_size; int codec_mode; + int codec_mode_aux; enum v4l2_mpeg_video_multi_slice_mode slice_mode; u32 framerate; u16 bitrate; @@ -192,8 +195,10 @@ struct coda_ctx { int vpu_header_size[3]; struct coda_aux_buf parabuf; struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS]; + struct coda_aux_buf workbuf; int num_internal_frames; int idx; + int reg_idx; struct coda_iram_info iram_info; }; @@ -241,10 +246,18 @@ static int coda_wait_timeout(struct coda_dev *dev) static void coda_command_async(struct coda_ctx *ctx, int cmd) { struct coda_dev *dev = ctx->dev; + + if (dev->devtype->product == CODA_7541) { + /* Restore context related registers to CODA */ + coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); + } + coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX); coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD); + coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD); + coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND); } @@ -968,21 +981,6 @@ static void coda_wait_finish(struct vb2_queue *q) coda_lock(ctx); } -static void coda_free_framebuffers(struct coda_ctx *ctx) -{ - int i; - - for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) { - if (ctx->internal_frames[i].vaddr) { - dma_free_coherent(&ctx->dev->plat_dev->dev, - ctx->internal_frames[i].size, - ctx->internal_frames[i].vaddr, - ctx->internal_frames[i].paddr); - ctx->internal_frames[i].vaddr = NULL; - } - } -} - static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) { struct coda_dev *dev = ctx->dev; @@ -994,28 +992,69 @@ static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) p[index ^ 1] = value; } +static int coda_alloc_aux_buf(struct coda_dev *dev, + struct coda_aux_buf *buf, size_t size) +{ + buf->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, &buf->paddr, + GFP_KERNEL); + if (!buf->vaddr) + return -ENOMEM; + + buf->size = size; + + return 0; +} + +static inline int coda_alloc_context_buf(struct coda_ctx *ctx, + struct coda_aux_buf *buf, size_t size) +{ + return coda_alloc_aux_buf(ctx->dev, buf, size); +} + +static void coda_free_aux_buf(struct coda_dev *dev, + struct coda_aux_buf *buf) +{ + if (buf->vaddr) { + dma_free_coherent(&dev->plat_dev->dev, buf->size, + buf->vaddr, buf->paddr); + buf->vaddr = NULL; + buf->size = 0; + } +} + +static void coda_free_framebuffers(struct coda_ctx *ctx) +{ + int i; + + for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) + coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i]); +} + 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; 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; /* Allocate frame buffers */ for (i = 0; i < ctx->num_internal_frames; i++) { - ctx->internal_frames[i].size = q_data->sizeimage; - if (fourcc == V4L2_PIX_FMT_H264 && dev->devtype->product != CODA_DX6) + size_t size; + + size = q_data->sizeimage; + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && + dev->devtype->product != CODA_DX6) ctx->internal_frames[i].size += ysize/4; - ctx->internal_frames[i].vaddr = dma_alloc_coherent( - &dev->plat_dev->dev, ctx->internal_frames[i].size, - &ctx->internal_frames[i].paddr, GFP_KERNEL); - if (!ctx->internal_frames[i].vaddr) { + ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i], size); + if (ret < 0) { coda_free_framebuffers(ctx); - return -ENOMEM; + return ret; } } @@ -1026,10 +1065,20 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize); /* Cb */ coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize/4); /* Cr */ - if (dev->devtype->product != CODA_DX6 && fourcc == V4L2_PIX_FMT_H264) - coda_parabuf_write(ctx, 96 + i, ctx->internal_frames[i].paddr + ysize + ysize/4 + ysize/4); + /* mvcol buffer for h.264 */ + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && + dev->devtype->product != CODA_DX6) + coda_parabuf_write(ctx, 96 + i, + ctx->internal_frames[i].paddr + + ysize + ysize/4 + ysize/4); } + /* mvcol buffer for mpeg4 */ + if ((dev->devtype->product != CODA_DX6) && + (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4)) + coda_parabuf_write(ctx, 97, ctx->internal_frames[i].paddr + + ysize + ysize/4 + ysize/4); + return 0; } @@ -1155,6 +1204,49 @@ out: } } +static void coda_free_context_buffers(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + + if (dev->devtype->product != CODA_DX6) + coda_free_aux_buf(dev, &ctx->workbuf); +} + +static int coda_alloc_context_buffers(struct coda_ctx *ctx, + struct coda_q_data *q_data) +{ + struct coda_dev *dev = ctx->dev; + size_t size; + int ret; + + switch (dev->devtype->product) { + case CODA_7541: + size = CODA7_WORK_BUF_SIZE; + break; + default: + return 0; + } + + if (ctx->workbuf.vaddr) { + v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n"); + ret = -EBUSY; + return -ENOMEM; + } + + ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte context buffer", + ctx->workbuf.size); + goto err; + } + + return 0; + +err: + coda_free_context_buffers(ctx); + return ret; +} + static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf, int header_code, u8 *header, int *size) { @@ -1170,7 +1262,7 @@ static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf, v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); return ret; } - *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - + *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); @@ -1223,6 +1315,11 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) return -EINVAL; } + /* Allocate per-instance buffers */ + ret = coda_alloc_context_buffers(ctx, q_data_src); + if (ret < 0) + return ret; + if (!coda_is_initialized(dev)) { v4l2_err(v4l2_dev, "coda is not initialized.\n"); return -EFAULT; @@ -1231,8 +1328,8 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) mutex_lock(&dev->coda_mutex); coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); - coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->idx)); - coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->idx)); + coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); switch (dev->devtype->product) { case CODA_DX6: coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN | @@ -1657,7 +1754,13 @@ static int coda_open(struct file *file) v4l2_fh_add(&ctx->fh); ctx->dev = dev; ctx->idx = idx; - + switch (dev->devtype->product) { + case CODA_7541: + ctx->reg_idx = 0; + break; + default: + ctx->reg_idx = idx; + } set_default_params(ctx); ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &coda_queue_init); @@ -1676,11 +1779,9 @@ static int coda_open(struct file *file) ctx->fh.ctrl_handler = &ctx->ctrls; - ctx->parabuf.vaddr = dma_alloc_coherent(&dev->plat_dev->dev, - CODA_PARA_BUF_SIZE, &ctx->parabuf.paddr, GFP_KERNEL); - if (!ctx->parabuf.vaddr) { + ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE); + if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf"); - ret = -ENOMEM; goto err; } @@ -1715,9 +1816,11 @@ static int coda_release(struct file *file) list_del(&ctx->list); coda_unlock(ctx); - dma_free_coherent(&dev->plat_dev->dev, CODA_PARA_BUF_SIZE, - ctx->parabuf.vaddr, ctx->parabuf.paddr); - v4l2_m2m_ctx_release(ctx->m2m_ctx); + coda_free_context_buffers(ctx); + if (ctx->dev->devtype->product == CODA_DX6) + coda_free_aux_buf(dev, &ctx->workbuf); + + coda_free_aux_buf(dev, &ctx->parabuf); v4l2_ctrl_handler_free(&ctx->ctrls); clk_disable_unprepare(dev->clk_per); clk_disable_unprepare(dev->clk_ahb); @@ -1797,7 +1900,8 @@ static irqreturn_t coda_irq_handler(int irq, void *data) /* Get results from the coda */ coda_read(dev, CODA_RET_ENC_PIC_TYPE); start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START); - wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)); + wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); + /* Calculate bytesused field */ if (dst_buf->v4l2_buf.sequence == 0) { vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr + @@ -1867,7 +1971,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, 13, 4, 29), + CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), }; static bool coda_firmware_supported(u32 vernum) @@ -1932,8 +2036,13 @@ 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 */ - coda_write(dev, dev->workbuf.paddr, - CODA_REG_BIT_WORK_BUF_ADDR); + if (dev->devtype->product == CODA_7541) { + coda_write(dev, dev->tempbuf.paddr, + CODA_REG_BIT_TEMP_BUF_ADDR); + } else { + coda_write(dev, dev->workbuf.paddr, + CODA_REG_BIT_WORK_BUF_ADDR); + } coda_write(dev, dev->codebuf.paddr, CODA_REG_BIT_CODE_BUF_ADDR); coda_write(dev, 0, CODA_REG_BIT_CODE_RUN); @@ -2020,11 +2129,8 @@ static void coda_fw_callback(const struct firmware *fw, void *context) } /* allocate auxiliary per-device code buffer for the BIT processor */ - dev->codebuf.size = fw->size; - dev->codebuf.vaddr = dma_alloc_coherent(&pdev->dev, fw->size, - &dev->codebuf.paddr, - GFP_KERNEL); - if (!dev->codebuf.vaddr) { + ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size); + if (ret < 0) { dev_err(&pdev->dev, "failed to allocate code buffer\n"); return; } @@ -2214,18 +2320,26 @@ static int coda_probe(struct platform_device *pdev) /* allocate auxiliary per-device buffers for the BIT processor */ switch (dev->devtype->product) { case CODA_DX6: - dev->workbuf.size = CODADX6_WORK_BUF_SIZE; + ret = coda_alloc_aux_buf(dev, &dev->workbuf, + CODADX6_WORK_BUF_SIZE); + if (ret < 0) { + dev_err(&pdev->dev, "failed to allocate work buffer\n"); + v4l2_device_unregister(&dev->v4l2_dev); + return ret; + } + break; + case CODA_7541: + dev->tempbuf.size = CODA7_TEMP_BUF_SIZE; break; - default: - dev->workbuf.size = CODA7_WORK_BUF_SIZE; } - dev->workbuf.vaddr = dma_alloc_coherent(&pdev->dev, dev->workbuf.size, - &dev->workbuf.paddr, - GFP_KERNEL); - if (!dev->workbuf.vaddr) { - dev_err(&pdev->dev, "failed to allocate work buffer\n"); - v4l2_device_unregister(&dev->v4l2_dev); - return -ENOMEM; + if (dev->tempbuf.size) { + ret = coda_alloc_aux_buf(dev, &dev->tempbuf, + dev->tempbuf.size); + if (ret < 0) { + dev_err(&pdev->dev, "failed to allocate temp buffer\n"); + v4l2_device_unregister(&dev->v4l2_dev); + return ret; + } } if (dev->devtype->product == CODA_DX6) @@ -2257,12 +2371,9 @@ static int coda_remove(struct platform_device *pdev) v4l2_device_unregister(&dev->v4l2_dev); if (dev->iram_vaddr) gen_pool_free(dev->iram_pool, dev->iram_vaddr, dev->iram_size); - if (dev->codebuf.vaddr) - dma_free_coherent(&pdev->dev, dev->codebuf.size, - &dev->codebuf.vaddr, dev->codebuf.paddr); - if (dev->workbuf.vaddr) - dma_free_coherent(&pdev->dev, dev->workbuf.size, &dev->workbuf.vaddr, - dev->workbuf.paddr); + coda_free_aux_buf(dev, &dev->codebuf); + coda_free_aux_buf(dev, &dev->tempbuf); + coda_free_aux_buf(dev, &dev->workbuf); return 0; } diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda.h index 39c17c6..b2b5b1d 100644 --- a/drivers/media/platform/coda.h +++ b/drivers/media/platform/coda.h @@ -43,6 +43,7 @@ #define CODA_STREAM_ENDIAN_SELECT (1 << 0) #define CODA_REG_BIT_FRAME_MEM_CTRL 0x110 #define CODA_IMAGE_ENDIAN_SELECT (1 << 0) +#define CODA_REG_BIT_TEMP_BUF_ADDR 0x118 #define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x)) #define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x)) #define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140 @@ -91,6 +92,14 @@ #define CODA_MODE_INVALID 0xffff #define CODA_REG_BIT_INT_ENABLE 0x170 #define CODA_INT_INTERRUPT_ENABLE (1 << 3) +#define CODA7_REG_BIT_RUN_AUX_STD 0x178 +#define CODA_MP4_AUX_MPEG4 0 +#define CODA_MP4_AUX_DIVX3 1 +#define CODA_VPX_AUX_THO 0 +#define CODA_VPX_AUX_VP6 1 +#define CODA_VPX_AUX_VP8 2 +#define CODA_H264_AUX_AVC 0 +#define CODA_H264_AUX_MVC 1 /* * Commands' mailbox: -- cgit v0.10.2 From 9082a7c66c688cddd305ceca1536fd3f6505ec21 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 21 Jun 2013 03:55:31 -0300 Subject: [media] coda: add bitstream ringbuffer handling for decoder Add a bitstream ringbuffer using kfifo. Queued source buffers are to be copied into the bitstream ringbuffer immediately and marked as done, if possible. 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 5aafc7c..424dd5f 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -182,6 +183,7 @@ struct coda_ctx { int streamon_out; int streamon_cap; u32 isequence; + u32 qsequence; struct coda_q_data q_data[2]; enum coda_inst_type inst_type; struct coda_codec *codec; @@ -193,6 +195,9 @@ struct coda_ctx { int gopcounter; char vpu_header[3][64]; int vpu_header_size[3]; + struct kfifo bitstream_fifo; + struct mutex bitstream_mutex; + struct coda_aux_buf bitstream; struct coda_aux_buf parabuf; struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS]; struct coda_aux_buf workbuf; @@ -200,6 +205,7 @@ struct coda_ctx { int idx; int reg_idx; struct coda_iram_info iram_info; + u32 bit_stream_param; }; static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff, @@ -249,6 +255,8 @@ static void coda_command_async(struct coda_ctx *ctx, int cmd) if (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->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); } @@ -692,6 +700,105 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_streamoff = vidioc_streamoff, }; +static inline int coda_get_bitstream_payload(struct coda_ctx *ctx) +{ + return kfifo_len(&ctx->bitstream_fifo); +} + +static void coda_kfifo_sync_from_device(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 rd_ptr; + + rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + kfifo->out = (kfifo->in & ~kfifo->mask) | + (rd_ptr - ctx->bitstream.paddr); + if (kfifo->out > kfifo->in) + kfifo->out -= kfifo->mask + 1; +} + +static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 rd_ptr, wr_ptr; + + rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask); + coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx)); + wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); + coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); +} + +static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx) +{ + struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo; + struct coda_dev *dev = ctx->dev; + u32 wr_ptr; + + wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask); + coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); +} + +static int coda_bitstream_queue(struct coda_ctx *ctx, struct vb2_buffer *src_buf) +{ + u32 src_size = vb2_get_plane_payload(src_buf, 0); + u32 n; + + n = kfifo_in(&ctx->bitstream_fifo, vb2_plane_vaddr(src_buf, 0), src_size); + if (n < src_size) + return -ENOSPC; + + dma_sync_single_for_device(&ctx->dev->plat_dev->dev, ctx->bitstream.paddr, + ctx->bitstream.size, DMA_TO_DEVICE); + + ctx->qsequence++; + + return 0; +} + +static bool coda_bitstream_try_queue(struct coda_ctx *ctx, + struct vb2_buffer *src_buf) +{ + int ret; + + if (coda_get_bitstream_payload(ctx) + + vb2_get_plane_payload(src_buf, 0) + 512 >= ctx->bitstream.size) + return false; + + if (vb2_plane_vaddr(src_buf, 0) == NULL) { + v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n"); + return true; + } + + ret = coda_bitstream_queue(ctx, src_buf); + if (ret < 0) { + v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n"); + return false; + } + /* Sync read pointer to device */ + if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev)) + coda_kfifo_sync_to_device_write(ctx); + + return true; +} + +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); + + if (coda_bitstream_try_queue(ctx, src_buf)) { + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + } else { + break; + } + } +} + /* * Mem-to-mem operations. */ @@ -842,15 +949,22 @@ static int coda_job_ready(void *m2m_priv) /* * For both 'P' and 'key' frame cases 1 picture - * and 1 frame are needed. + * 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) || - !v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)) { + if (!v4l2_m2m_num_src_bufs_ready(ctx->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)) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "not ready: not enough video capture buffers.\n"); + return 0; + } + if (ctx->aborting) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "not ready: aborting\n"); @@ -1785,6 +1899,18 @@ static int coda_open(struct file *file) goto err; } + ctx->bitstream.size = CODA_MAX_FRAME_SIZE; + ctx->bitstream.vaddr = dma_alloc_writecombine(&dev->plat_dev->dev, + ctx->bitstream.size, &ctx->bitstream.paddr, GFP_KERNEL); + if (!ctx->bitstream.vaddr) { + v4l2_err(&dev->v4l2_dev, "failed to allocate bitstream ringbuffer"); + ret = -ENOMEM; + goto err; + } + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + mutex_init(&ctx->bitstream_mutex); + coda_lock(ctx); list_add(&ctx->list, &dev->instances); coda_unlock(ctx); @@ -1816,6 +1942,8 @@ static int coda_release(struct file *file) list_del(&ctx->list); coda_unlock(ctx); + dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size, + ctx->bitstream.vaddr, ctx->bitstream.paddr); coda_free_context_buffers(ctx); if (ctx->dev->devtype->product == CODA_DX6) coda_free_aux_buf(dev, &ctx->workbuf); diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda.h index b2b5b1d..140eea5 100644 --- a/drivers/media/platform/coda.h +++ b/drivers/media/platform/coda.h @@ -43,6 +43,9 @@ #define CODA_STREAM_ENDIAN_SELECT (1 << 0) #define CODA_REG_BIT_FRAME_MEM_CTRL 0x110 #define CODA_IMAGE_ENDIAN_SELECT (1 << 0) +#define CODA_REG_BIT_BIT_STREAM_PARAM 0x114 +#define CODA_BIT_STREAM_END_FLAG (1 << 2) +#define CODA_BIT_DEC_SEQ_INIT_ESCAPE (1 << 0) #define CODA_REG_BIT_TEMP_BUF_ADDR 0x118 #define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x)) #define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x)) -- cgit v0.10.2 From ed29f894970065402e988088a19698f252b80144 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 22 Jun 2013 05:46:34 -0300 Subject: [media] media: i2c: ths8200: support asynchronous probing This patch supports both synchronous and asynchronous ths8200 subdevice probing. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index a24f90c..cc1339a 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -21,6 +21,7 @@ #include #include +#include #include #include "ths8200_regs.h" @@ -500,6 +501,7 @@ static int ths8200_probe(struct i2c_client *client, { struct ths8200_state *state; struct v4l2_subdev *sd; + int error; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -517,6 +519,10 @@ static int ths8200_probe(struct i2c_client *client, ths8200_core_init(sd); + error = v4l2_async_register_subdev(&state->sd); + if (error) + return error; + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); @@ -526,12 +532,13 @@ static int ths8200_probe(struct i2c_client *client, static int ths8200_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ths8200_state *decoder = to_state(sd); v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); ths8200_s_power(sd, false); - + v4l2_async_unregister_subdev(&decoder->sd); v4l2_device_unregister_subdev(sd); return 0; -- cgit v0.10.2 From 0fb0f8f5ed9ff1419cd59834e4d1e95d19fb2eef Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 22 Jun 2013 05:46:35 -0300 Subject: [media] media: i2c: ths8200: add OF support add OF support for the ths8200 driver. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/i2c/ths8200.txt b/Documentation/devicetree/bindings/media/i2c/ths8200.txt new file mode 100644 index 0000000..285f6ae --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ths8200.txt @@ -0,0 +1,19 @@ +* Texas Instruments THS8200 video encoder + +The ths8200 device is a digital to analog converter used in DVD players, video +recorders, set-top boxes. + +Required Properties : +- compatible : value must be "ti,ths8200" + +Example: + + i2c0@1c22000 { + ... + ... + ths8200@5c { + compatible = "ti,ths8200"; + reg = <0x5c>; + }; + ... + }; diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index cc1339a..8a29810 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -550,10 +550,19 @@ static struct i2c_device_id ths8200_id[] = { }; MODULE_DEVICE_TABLE(i2c, ths8200_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ths8200_of_match[] = { + { .compatible = "ti,ths8200", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ths8200_of_match); +#endif + static struct i2c_driver ths8200_driver = { .driver = { .owner = THIS_MODULE, .name = "ths8200", + .of_match_table = of_match_ptr(ths8200_of_match), }, .probe = ths8200_probe, .remove = ths8200_remove, -- cgit v0.10.2 From 6555cfc5e7f8080a76edc2f556c709770fc1db57 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 22 Jun 2013 06:07:37 -0300 Subject: [media] media: i2c: adv7343: add support for asynchronous probing Both synchronous and asynchronous adv7343 subdevice probing is supported by this patch. Signed-off-by: Lad, Prabhakar Acked-by: Guennadi Liakhovetski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index 7606218..8080c2c 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -445,16 +446,21 @@ static int adv7343_probe(struct i2c_client *client, ADV7343_GAIN_DEF); state->sd.ctrl_handler = &state->hdl; if (state->hdl.error) { - int err = state->hdl.error; - - v4l2_ctrl_handler_free(&state->hdl); - return err; + err = state->hdl.error; + goto done; } v4l2_ctrl_handler_setup(&state->hdl); err = adv7343_initialize(&state->sd); if (err) + goto done; + + err = v4l2_async_register_subdev(&state->sd); + +done: + if (err < 0) v4l2_ctrl_handler_free(&state->hdl); + return err; } @@ -463,6 +469,7 @@ static int adv7343_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct adv7343_state *state = to_state(sd); + v4l2_async_unregister_subdev(&state->sd); v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(&state->hdl); -- cgit v0.10.2 From 25ba2c802f0c675bc846cc5d0adcaba5b76e6c1f Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 22 Jun 2013 13:44:14 -0300 Subject: [media] media: i2c: tvp7002: add support for asynchronous probing Both synchronous and asynchronous tvp7002 subdevice probing is supported by this patch. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index a4e4948..f6b1f3f 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -1039,6 +1040,10 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) } v4l2_ctrl_handler_setup(&device->hdl); + error = v4l2_async_register_subdev(&device->sd); + if (error) + goto error; + return 0; error: @@ -1063,6 +1068,7 @@ static int tvp7002_remove(struct i2c_client *c) v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter" "on address 0x%x\n", c->addr); + v4l2_async_unregister_subdev(&device->sd); #if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&device->sd.entity); #endif -- cgit v0.10.2 From 8f23acb52b168282dd818a48fb3d011ed4200978 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Mon, 24 Jun 2013 11:51:39 -0300 Subject: [media] media: i2c: tvp514x: add support for asynchronous probing Both synchronous and asynchronous tvp514x subdevice probing is supported by this patch. This patch also fixes the error path by calling media_entity_cleanup() on failure in probe when CONFIG_MEDIA_CONTROLLER is enabled. Signed-off-by: Prabhakar Lad Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index 9c6d66a..91f3dd4 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -1175,16 +1176,22 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) sd->ctrl_handler = &decoder->hdl; if (decoder->hdl.error) { ret = decoder->hdl.error; - - v4l2_ctrl_handler_free(&decoder->hdl); - return ret; + goto done; } v4l2_ctrl_handler_setup(&decoder->hdl); - v4l2_info(sd, "%s decoder driver registered !!\n", sd->name); - - return 0; + ret = v4l2_async_register_subdev(&decoder->sd); + if (!ret) + v4l2_info(sd, "%s decoder driver registered !!\n", sd->name); +done: + if (ret < 0) { + v4l2_ctrl_handler_free(&decoder->hdl); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&decoder->sd.entity); +#endif + } + return ret; } /** @@ -1199,6 +1206,7 @@ static int tvp514x_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct tvp514x_decoder *decoder = to_decoder(sd); + v4l2_async_unregister_subdev(&decoder->sd); v4l2_device_unregister_subdev(sd); #if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&decoder->sd.entity); -- cgit v0.10.2 From 873229e4fdf34196aa5d707957c59ba54c25eaba Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 25 Jun 2013 11:17:34 -0300 Subject: [media] media: davinci: vpif: capture: add V4L2-async support Add support for asynchronous subdevice probing, using the v4l2-async API. The legacy synchronous mode is still supported too, which allows to gradually update drivers and platforms. Signed-off-by: Prabhakar Lad Cc: Guennadi Liakhovetski Cc: Hans Verkuil Cc: Laurent Pinchart Cc: Sakari Ailus Cc: Mauro Carvalho Chehab 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 5514175..b11d7a7 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1979,6 +1979,76 @@ vpif_init_free_channel_objects: return err; } +static int vpif_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + int i; + + for (i = 0; i < vpif_obj.config->subdev_count; i++) + if (!strcmp(vpif_obj.config->subdev_info[i].name, + subdev->name)) { + vpif_obj.sd[i] = subdev; + return 0; + } + + return -EINVAL; +} + +static int vpif_probe_complete(void) +{ + struct common_obj *common; + struct channel_obj *ch; + int i, j, err, k; + + for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { + ch = vpif_obj.dev[j]; + ch->channel_id = j; + common = &(ch->common[VPIF_VIDEO_INDEX]); + spin_lock_init(&common->irqlock); + mutex_init(&common->lock); + ch->video_dev->lock = &common->lock; + /* Initialize prio member of channel object */ + v4l2_prio_init(&ch->prio); + video_set_drvdata(ch->video_dev, ch); + + /* select input 0 */ + err = vpif_set_input(vpif_obj.config, ch, 0); + if (err) + goto probe_out; + + err = video_register_device(ch->video_dev, + VFL_TYPE_GRABBER, (j ? 1 : 0)); + if (err) + goto probe_out; + } + + v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n"); + return 0; + +probe_out: + for (k = 0; k < j; k++) { + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[k]; + /* Unregister video device */ + video_unregister_device(ch->video_dev); + } + kfree(vpif_obj.sd); + for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { + ch = vpif_obj.dev[i]; + /* Note: does nothing if ch->video_dev == NULL */ + video_device_release(ch->video_dev); + } + v4l2_device_unregister(&vpif_obj.v4l2_dev); + + return err; +} + +static int vpif_async_complete(struct v4l2_async_notifier *notifier) +{ + return vpif_probe_complete(); +} + /** * vpif_probe : This function probes the vpif capture driver * @pdev: platform device pointer @@ -1989,12 +2059,10 @@ vpif_init_free_channel_objects: static __init int vpif_probe(struct platform_device *pdev) { struct vpif_subdev_info *subdevdata; - struct vpif_capture_config *config; - int i, j, k, err; + int i, j, err; int res_idx = 0; struct i2c_adapter *i2c_adap; struct channel_obj *ch; - struct common_obj *common; struct video_device *vfd; struct resource *res; int subdev_count; @@ -2068,10 +2136,9 @@ static __init int vpif_probe(struct platform_device *pdev) } } - i2c_adap = i2c_get_adapter(1); - config = pdev->dev.platform_data; + vpif_obj.config = pdev->dev.platform_data; - subdev_count = config->subdev_count; + subdev_count = vpif_obj.config->subdev_count; vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count, GFP_KERNEL); if (vpif_obj.sd == NULL) { @@ -2080,54 +2147,42 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_sd_error; } - for (i = 0; i < subdev_count; i++) { - subdevdata = &config->subdev_info[i]; - vpif_obj.sd[i] = - v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, - i2c_adap, - &subdevdata->board_info, - NULL); - - if (!vpif_obj.sd[i]) { - vpif_err("Error registering v4l2 subdevice\n"); - err = -ENODEV; + if (!vpif_obj.config->asd_sizes) { + i2c_adap = i2c_get_adapter(1); + for (i = 0; i < subdev_count; i++) { + subdevdata = &vpif_obj.config->subdev_info[i]; + vpif_obj.sd[i] = + v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, + &subdevdata-> + board_info, + NULL); + + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + goto probe_subdev_out; + } + v4l2_info(&vpif_obj.v4l2_dev, + "registered sub device %s\n", + subdevdata->name); + } + vpif_probe_complete(); + } else { + vpif_obj.notifier.subdev = vpif_obj.config->asd; + vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; + vpif_obj.notifier.bound = vpif_async_bound; + vpif_obj.notifier.complete = vpif_async_complete; + err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev, + &vpif_obj.notifier); + if (err) { + vpif_err("Error registering async notifier\n"); + err = -EINVAL; goto probe_subdev_out; } - v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n", - subdevdata->name); } - for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { - ch = vpif_obj.dev[j]; - ch->channel_id = j; - common = &(ch->common[VPIF_VIDEO_INDEX]); - spin_lock_init(&common->irqlock); - mutex_init(&common->lock); - ch->video_dev->lock = &common->lock; - /* Initialize prio member of channel object */ - v4l2_prio_init(&ch->prio); - video_set_drvdata(ch->video_dev, ch); - - /* select input 0 */ - err = vpif_set_input(config, ch, 0); - if (err) - goto probe_out; - - err = video_register_device(ch->video_dev, - VFL_TYPE_GRABBER, (j ? 1 : 0)); - if (err) - goto probe_out; - } - v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n"); return 0; -probe_out: - for (k = 0; k < j; k++) { - /* Get the pointer to the channel object */ - ch = vpif_obj.dev[k]; - /* Unregister video device */ - video_unregister_device(ch->video_dev); - } probe_subdev_out: /* free sub devices memory */ kfree(vpif_obj.sd); diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h index 0ebb312..5a29d9a 100644 --- a/drivers/media/platform/davinci/vpif_capture.h +++ b/drivers/media/platform/davinci/vpif_capture.h @@ -142,6 +142,8 @@ struct vpif_device { struct v4l2_device v4l2_dev; struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS]; struct v4l2_subdev **sd; + struct v4l2_async_notifier notifier; + struct vpif_capture_config *config; }; struct vpif_config_params { diff --git a/include/media/davinci/vpif_types.h b/include/media/davinci/vpif_types.h index 3882e06..e08bcde 100644 --- a/include/media/davinci/vpif_types.h +++ b/include/media/davinci/vpif_types.h @@ -81,5 +81,7 @@ struct vpif_capture_config { struct vpif_subdev_info *subdev_info; int subdev_count; const char *card_name; + struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ + int *asd_sizes; /* 0-terminated array of asd group sizes */ }; #endif /* _VPIF_TYPES_H */ -- cgit v0.10.2 From 4b8a531e6bb0686203e9cf82a54dfe189de7d5c2 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 25 Jun 2013 11:17:35 -0300 Subject: [media] media: davinci: vpif: display: add V4L2-async support Add support for asynchronous subdevice probing, using the v4l2-async API. The legacy synchronous mode is still supported too, which allows to gradually update drivers and platforms. Signed-off-by: Lad, Prabhakar Cc: Guennadi Liakhovetski Cc: Hans Verkuil Cc: Laurent Pinchart Cc: Sakari Ailus Cc: Mauro Carvalho Chehab Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index e6e5736..c2ff067 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1618,6 +1618,102 @@ vpif_init_free_channel_objects: return err; } +static int vpif_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + int i; + + for (i = 0; i < vpif_obj.config->subdev_count; i++) + if (!strcmp(vpif_obj.config->subdevinfo[i].name, + subdev->name)) { + vpif_obj.sd[i] = subdev; + vpif_obj.sd[i]->grp_id = 1 << i; + return 0; + } + + return -EINVAL; +} + +static int vpif_probe_complete(void) +{ + struct common_obj *common; + struct channel_obj *ch; + int j, err, k; + + for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { + ch = vpif_obj.dev[j]; + /* Initialize field of the channel objects */ + atomic_set(&ch->usrs, 0); + for (k = 0; k < VPIF_NUMOBJECTS; k++) { + ch->common[k].numbuffers = 0; + common = &ch->common[k]; + common->io_usrs = 0; + common->started = 0; + spin_lock_init(&common->irqlock); + mutex_init(&common->lock); + common->numbuffers = 0; + common->set_addr = NULL; + common->ytop_off = 0; + common->ybtm_off = 0; + common->ctop_off = 0; + common->cbtm_off = 0; + common->cur_frm = NULL; + common->next_frm = NULL; + memset(&common->fmt, 0, sizeof(common->fmt)); + common->numbuffers = config_params.numbuffers[k]; + } + ch->initialized = 0; + if (vpif_obj.config->subdev_count) + ch->sd = vpif_obj.sd[0]; + ch->channel_id = j; + if (j < 2) + ch->common[VPIF_VIDEO_INDEX].numbuffers = + config_params.numbuffers[ch->channel_id]; + else + ch->common[VPIF_VIDEO_INDEX].numbuffers = 0; + + memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); + + /* Initialize prio member of channel object */ + v4l2_prio_init(&ch->prio); + ch->common[VPIF_VIDEO_INDEX].fmt.type = + V4L2_BUF_TYPE_VIDEO_OUTPUT; + ch->video_dev->lock = &common->lock; + video_set_drvdata(ch->video_dev, ch); + + /* select output 0 */ + err = vpif_set_output(vpif_obj.config, ch, 0); + if (err) + goto probe_out; + + /* register video device */ + vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", + (int)ch, (int)&ch->video_dev); + + err = video_register_device(ch->video_dev, + VFL_TYPE_GRABBER, (j ? 3 : 2)); + if (err < 0) + goto probe_out; + } + + return 0; + +probe_out: + for (k = 0; k < j; k++) { + ch = vpif_obj.dev[k]; + video_unregister_device(ch->video_dev); + video_device_release(ch->video_dev); + ch->video_dev = NULL; + } + return err; +} + +static int vpif_async_complete(struct v4l2_async_notifier *notifier) +{ + return vpif_probe_complete(); +} + /* * vpif_probe: This function creates device entries by register itself to the * V4L2 driver and initializes fields of each channel objects @@ -1625,11 +1721,9 @@ vpif_init_free_channel_objects: static __init int vpif_probe(struct platform_device *pdev) { struct vpif_subdev_info *subdevdata; - struct vpif_display_config *config; - int i, j = 0, k, err = 0; + int i, j = 0, err = 0; int res_idx = 0; struct i2c_adapter *i2c_adap; - struct common_obj *common; struct channel_obj *ch; struct video_device *vfd; struct resource *res; @@ -1708,11 +1802,9 @@ static __init int vpif_probe(struct platform_device *pdev) size/2; } } - - i2c_adap = i2c_get_adapter(1); - config = pdev->dev.platform_data; - subdev_count = config->subdev_count; - subdevdata = config->subdevinfo; + vpif_obj.config = pdev->dev.platform_data; + subdev_count = vpif_obj.config->subdev_count; + subdevdata = vpif_obj.config->subdevinfo; vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count, GFP_KERNEL); if (vpif_obj.sd == NULL) { @@ -1721,86 +1813,40 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_sd_error; } - for (i = 0; i < subdev_count; i++) { - vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, - i2c_adap, - &subdevdata[i].board_info, - NULL); - if (!vpif_obj.sd[i]) { - vpif_err("Error registering v4l2 subdevice\n"); - err = -ENODEV; - goto probe_subdev_out; - } - - if (vpif_obj.sd[i]) - vpif_obj.sd[i]->grp_id = 1 << i; - } - - for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { - ch = vpif_obj.dev[j]; - /* Initialize field of the channel objects */ - atomic_set(&ch->usrs, 0); - for (k = 0; k < VPIF_NUMOBJECTS; k++) { - ch->common[k].numbuffers = 0; - common = &ch->common[k]; - common->io_usrs = 0; - common->started = 0; - spin_lock_init(&common->irqlock); - mutex_init(&common->lock); - common->numbuffers = 0; - common->set_addr = NULL; - common->ytop_off = common->ybtm_off = 0; - common->ctop_off = common->cbtm_off = 0; - common->cur_frm = common->next_frm = NULL; - memset(&common->fmt, 0, sizeof(common->fmt)); - common->numbuffers = config_params.numbuffers[k]; + if (!vpif_obj.config->asd_sizes) { + i2c_adap = i2c_get_adapter(1); + for (i = 0; i < subdev_count; i++) { + vpif_obj.sd[i] = + v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, + &subdevdata[i]. + board_info, + NULL); + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + goto probe_subdev_out; + } + if (vpif_obj.sd[i]) + vpif_obj.sd[i]->grp_id = 1 << i; + } + vpif_probe_complete(); + } else { + vpif_obj.notifier.subdev = vpif_obj.config->asd; + vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; + vpif_obj.notifier.bound = vpif_async_bound; + vpif_obj.notifier.complete = vpif_async_complete; + err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev, + &vpif_obj.notifier); + if (err) { + vpif_err("Error registering async notifier\n"); + err = -EINVAL; + goto probe_subdev_out; } - ch->initialized = 0; - if (subdev_count) - ch->sd = vpif_obj.sd[0]; - ch->channel_id = j; - if (j < 2) - ch->common[VPIF_VIDEO_INDEX].numbuffers = - config_params.numbuffers[ch->channel_id]; - else - ch->common[VPIF_VIDEO_INDEX].numbuffers = 0; - - memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); - - /* Initialize prio member of channel object */ - v4l2_prio_init(&ch->prio); - ch->common[VPIF_VIDEO_INDEX].fmt.type = - V4L2_BUF_TYPE_VIDEO_OUTPUT; - ch->video_dev->lock = &common->lock; - video_set_drvdata(ch->video_dev, ch); - - /* select output 0 */ - err = vpif_set_output(config, ch, 0); - if (err) - goto probe_out; - - /* register video device */ - vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", - (int)ch, (int)&ch->video_dev); - - err = video_register_device(ch->video_dev, - VFL_TYPE_GRABBER, (j ? 3 : 2)); - if (err < 0) - goto probe_out; } - v4l2_info(&vpif_obj.v4l2_dev, - " VPIF display driver initialized\n"); return 0; -probe_out: - for (k = 0; k < j; k++) { - ch = vpif_obj.dev[k]; - video_unregister_device(ch->video_dev); - video_device_release(ch->video_dev); - ch->video_dev = NULL; - } probe_subdev_out: kfree(vpif_obj.sd); vpif_sd_error: diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index 5d87fc8..4d0485b 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -148,7 +148,8 @@ struct vpif_device { struct v4l2_device v4l2_dev; struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS]; struct v4l2_subdev **sd; - + struct v4l2_async_notifier notifier; + struct vpif_display_config *config; }; struct vpif_config_params { diff --git a/include/media/davinci/vpif_types.h b/include/media/davinci/vpif_types.h index e08bcde..3cb1704 100644 --- a/include/media/davinci/vpif_types.h +++ b/include/media/davinci/vpif_types.h @@ -59,6 +59,8 @@ struct vpif_display_config { int subdev_count; struct vpif_display_chan_config chan_config[VPIF_DISPLAY_MAX_CHANNELS]; const char *card_name; + struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ + int *asd_sizes; /* 0-terminated array of asd group sizes */ }; struct vpif_input { -- cgit v0.10.2 From 371376d202ebbdda1d30caaf29125e4cb79884b1 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 13 Jul 2013 04:50:27 -0300 Subject: [media] media: davinci: vpbe_venc: convert to devm_* api Replace existing resource handling in the driver with managed device resource, this ensures more consistent error values and simplifies error paths. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c index 87eef9b..14a023a 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/davinci/vpbe_venc.c @@ -639,105 +639,46 @@ static int venc_probe(struct platform_device *pdev) const struct platform_device_id *pdev_id; struct venc_state *venc; struct resource *res; - int ret; - venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL); + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "No platform data for VENC sub device"); + return -EINVAL; + } + + pdev_id = platform_get_device_id(pdev); + if (!pdev_id) + return -EINVAL; + + venc = devm_kzalloc(&pdev->dev, sizeof(struct venc_state), GFP_KERNEL); if (venc == NULL) return -ENOMEM; - pdev_id = platform_get_device_id(pdev); - if (!pdev_id) { - ret = -EINVAL; - goto free_mem; - } venc->venc_type = pdev_id->driver_data; venc->pdev = &pdev->dev; venc->pdata = pdev->dev.platform_data; - if (NULL == venc->pdata) { - dev_err(venc->pdev, "Unable to get platform data for" - " VENC sub device"); - ret = -ENOENT; - goto free_mem; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(venc->pdev, - "Unable to get VENC register address map\n"); - ret = -ENODEV; - goto free_mem; - } - if (!request_mem_region(res->start, resource_size(res), "venc")) { - dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n"); - ret = -ENODEV; - goto free_mem; - } - - venc->venc_base = ioremap_nocache(res->start, resource_size(res)); - if (!venc->venc_base) { - dev_err(venc->pdev, "Unable to map VENC IO space\n"); - ret = -ENODEV; - goto release_venc_mem_region; - } + venc->venc_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(venc->venc_base)) + return PTR_ERR(venc->venc_base); if (venc->venc_type != VPBE_VERSION_1) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - dev_err(venc->pdev, - "Unable to get VDAC_CONFIG address map\n"); - ret = -ENODEV; - goto unmap_venc_io; - } - - if (!request_mem_region(res->start, - resource_size(res), "venc")) { - dev_err(venc->pdev, - "Unable to reserve VDAC_CONFIG MMIO region\n"); - ret = -ENODEV; - goto unmap_venc_io; - } - - venc->vdaccfg_reg = ioremap_nocache(res->start, - resource_size(res)); - if (!venc->vdaccfg_reg) { - dev_err(venc->pdev, - "Unable to map VDAC_CONFIG IO space\n"); - ret = -ENODEV; - goto release_vdaccfg_mem_region; - } + + venc->vdaccfg_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(venc->vdaccfg_reg)) + return PTR_ERR(venc->vdaccfg_reg); } spin_lock_init(&venc->lock); platform_set_drvdata(pdev, venc); dev_notice(venc->pdev, "VENC sub device probe success\n"); - return 0; -release_vdaccfg_mem_region: - release_mem_region(res->start, resource_size(res)); -unmap_venc_io: - iounmap(venc->venc_base); -release_venc_mem_region: - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); -free_mem: - kfree(venc); - return ret; + return 0; } static int venc_remove(struct platform_device *pdev) { - struct venc_state *venc = platform_get_drvdata(pdev); - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - iounmap((void *)venc->venc_base); - release_mem_region(res->start, resource_size(res)); - if (venc->venc_type != VPBE_VERSION_1) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - iounmap((void *)venc->vdaccfg_reg); - release_mem_region(res->start, resource_size(res)); - } - kfree(venc); - return 0; } -- cgit v0.10.2 From 418f7d60c05c638d6557927a92cf9b2e328ae7dc Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 13 Jul 2013 04:50:28 -0300 Subject: [media] media: davinci: vpbe_osd: convert to devm_* api Replace existing resource handling in the driver with managed device resource, this ensures more consistent error values and simplifies error paths. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index 6ed82e8..d053c26 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -1547,61 +1547,36 @@ static int osd_probe(struct platform_device *pdev) const struct platform_device_id *pdev_id; struct osd_state *osd; struct resource *res; - int ret = 0; - osd = kzalloc(sizeof(struct osd_state), GFP_KERNEL); + pdev_id = platform_get_device_id(pdev); + if (!pdev_id) + return -EINVAL; + + osd = devm_kzalloc(&pdev->dev, sizeof(struct osd_state), GFP_KERNEL); if (osd == NULL) return -ENOMEM; - pdev_id = platform_get_device_id(pdev); - if (!pdev_id) { - ret = -EINVAL; - goto free_mem; - } osd->dev = &pdev->dev; osd->vpbe_type = pdev_id->driver_data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(osd->dev, "Unable to get OSD register address map\n"); - ret = -ENODEV; - goto free_mem; - } + osd->osd_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(osd->osd_base)) + return PTR_ERR(osd->osd_base); + osd->osd_base_phys = res->start; osd->osd_size = resource_size(res); - if (!request_mem_region(osd->osd_base_phys, osd->osd_size, - MODULE_NAME)) { - dev_err(osd->dev, "Unable to reserve OSD MMIO region\n"); - ret = -ENODEV; - goto free_mem; - } - osd->osd_base = ioremap_nocache(res->start, osd->osd_size); - if (!osd->osd_base) { - dev_err(osd->dev, "Unable to map the OSD region\n"); - ret = -ENODEV; - goto release_mem_region; - } spin_lock_init(&osd->lock); osd->ops = osd_ops; platform_set_drvdata(pdev, osd); dev_notice(osd->dev, "OSD sub device probe success\n"); - return ret; -release_mem_region: - release_mem_region(osd->osd_base_phys, osd->osd_size); -free_mem: - kfree(osd); - return ret; + return 0; } static int osd_remove(struct platform_device *pdev) { - struct osd_state *osd = platform_get_drvdata(pdev); - - iounmap((void *)osd->osd_base); - release_mem_region(osd->osd_base_phys, osd->osd_size); - kfree(osd); return 0; } -- cgit v0.10.2 From 6b55b4516cf6f4711024a158dabbb50b282c5d3f Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 13 Jul 2013 04:50:29 -0300 Subject: [media] media: davinci: vpbe_display: convert to devm* api Replace existing resource handling in the driver with managed device resource, this ensures more consistent error values and simplifies error paths. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index e180ff7..04609cc6 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1743,11 +1743,10 @@ static int vpbe_display_probe(struct platform_device *pdev) printk(KERN_DEBUG "vpbe_display_probe\n"); /* Allocate memory for vpbe_display */ - disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL); - if (!disp_dev) { - printk(KERN_ERR "ran out of memory\n"); + disp_dev = devm_kzalloc(&pdev->dev, sizeof(struct vpbe_display), + GFP_KERNEL); + if (!disp_dev) return -ENOMEM; - } spin_lock_init(&disp_dev->dma_queue_lock); /* @@ -1786,26 +1785,24 @@ static int vpbe_display_probe(struct platform_device *pdev) } irq = res->start; - if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER, - disp_dev)) { + err = devm_request_irq(&pdev->dev, irq, venc_isr, IRQF_DISABLED, + VPBE_DISPLAY_DRIVER, disp_dev); + if (err) { v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); - err = -ENODEV; goto probe_out; } for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { if (register_device(disp_dev->dev[i], disp_dev, pdev)) { err = -ENODEV; - goto probe_out_irq; + goto probe_out; } } printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); return 0; -probe_out_irq: - free_irq(res->start, disp_dev); probe_out: for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) { /* Get the pointer to the layer object */ @@ -1817,7 +1814,6 @@ probe_out: kfree(disp_dev->dev[k]); } } - kfree(disp_dev); return err; } @@ -1830,15 +1826,10 @@ static int vpbe_display_remove(struct platform_device *pdev) struct vpbe_layer *vpbe_display_layer; struct vpbe_display *disp_dev = platform_get_drvdata(pdev); struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; - struct resource *res; int i; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n"); - /* unregister irq */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - free_irq(res->start, disp_dev); - /* deinitialize the vpbe display controller */ if (NULL != vpbe_dev->ops.deinitialize) vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev); -- cgit v0.10.2 From 6ef8335928400ab58d547a3a59974d12f1c55d94 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 13 Jul 2013 04:50:30 -0300 Subject: [media] media: davinci: vpss: convert to devm* api Replace existing resource handling in the driver with managed device resource, this ensures more consistent error values and simplifies error paths. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index 8a2f01e..31120b4 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -404,9 +405,8 @@ EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size); static int vpss_probe(struct platform_device *pdev) { - struct resource *r1, *r2; + struct resource *res; char *platform_name; - int status; if (!pdev->dev.platform_data) { dev_err(&pdev->dev, "no platform data\n"); @@ -427,38 +427,19 @@ static int vpss_probe(struct platform_device *pdev) } dev_info(&pdev->dev, "%s vpss probed\n", platform_name); - r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r1) - return -ENOENT; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - r1 = request_mem_region(r1->start, resource_size(r1), r1->name); - if (!r1) - return -EBUSY; - - oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1)); - if (!oper_cfg.vpss_regs_base0) { - status = -EBUSY; - goto fail1; - } + oper_cfg.vpss_regs_base0 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(oper_cfg.vpss_regs_base0)) + return PTR_ERR(oper_cfg.vpss_regs_base0); if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) { - r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!r2) { - status = -ENOENT; - goto fail2; - } - r2 = request_mem_region(r2->start, resource_size(r2), r2->name); - if (!r2) { - status = -EBUSY; - goto fail2; - } - - oper_cfg.vpss_regs_base1 = ioremap(r2->start, - resource_size(r2)); - if (!oper_cfg.vpss_regs_base1) { - status = -EBUSY; - goto fail3; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + + oper_cfg.vpss_regs_base1 = devm_ioremap_resource(&pdev->dev, + res); + if (IS_ERR(oper_cfg.vpss_regs_base1)) + return PTR_ERR(oper_cfg.vpss_regs_base1); } if (oper_cfg.platform == DM355) { @@ -493,30 +474,13 @@ static int vpss_probe(struct platform_device *pdev) spin_lock_init(&oper_cfg.vpss_lock); dev_info(&pdev->dev, "%s vpss probe success\n", platform_name); - return 0; -fail3: - release_mem_region(r2->start, resource_size(r2)); -fail2: - iounmap(oper_cfg.vpss_regs_base0); -fail1: - release_mem_region(r1->start, resource_size(r1)); - return status; + return 0; } static int vpss_remove(struct platform_device *pdev) { - struct resource *res; - pm_runtime_disable(&pdev->dev); - iounmap(oper_cfg.vpss_regs_base0); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) { - iounmap(oper_cfg.vpss_regs_base1); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - release_mem_region(res->start, resource_size(res)); - } return 0; } -- cgit v0.10.2 From 5e95814ff3f2a6ea7d76e822bbc3b0c0b94495a4 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 20 Jul 2013 02:21:05 -0300 Subject: [media] media: i2c: adv7343: make the platform data members as array This patch makes the platform data members as array wherever possible, so as this makes easier while collecting the data in DT case and read the entire array at once. This patch also makes appropriate changes to board-da850-evm.c Signed-off-by: Lad, Prabhakar Acked-by: Sekhar Nori Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index bea6793..9f09f45 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1249,12 +1249,10 @@ static struct vpif_capture_config da850_vpif_capture_config = { static struct adv7343_platform_data adv7343_pdata = { .mode_config = { - .dac_3 = 1, - .dac_2 = 1, - .dac_1 = 1, + .dac = { 1, 1, 1 }, }, .sd_config = { - .sd_dac_out1 = 1, + .sd_dac_out = { 1 }, }, }; diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index 8080c2c..f0238fb 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -227,12 +227,12 @@ static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) else val = state->pdata->mode_config.sleep_mode << 0 | state->pdata->mode_config.pll_control << 1 | - state->pdata->mode_config.dac_3 << 2 | - state->pdata->mode_config.dac_2 << 3 | - state->pdata->mode_config.dac_1 << 4 | - state->pdata->mode_config.dac_6 << 5 | - state->pdata->mode_config.dac_5 << 6 | - state->pdata->mode_config.dac_4 << 7; + state->pdata->mode_config.dac[2] << 2 | + state->pdata->mode_config.dac[1] << 3 | + state->pdata->mode_config.dac[0] << 4 | + state->pdata->mode_config.dac[5] << 5 | + state->pdata->mode_config.dac[4] << 6 | + state->pdata->mode_config.dac[3] << 7; err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val); if (err < 0) @@ -251,15 +251,15 @@ static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) /* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */ val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI); - if (state->pdata && state->pdata->sd_config.sd_dac_out1) - val = val | (state->pdata->sd_config.sd_dac_out1 << 1); - else if (state->pdata && !state->pdata->sd_config.sd_dac_out1) - val = val & ~(state->pdata->sd_config.sd_dac_out1 << 1); + if (state->pdata && state->pdata->sd_config.sd_dac_out[0]) + val = val | (state->pdata->sd_config.sd_dac_out[0] << 1); + else if (state->pdata && !state->pdata->sd_config.sd_dac_out[0]) + val = val & ~(state->pdata->sd_config.sd_dac_out[0] << 1); - if (state->pdata && state->pdata->sd_config.sd_dac_out2) - val = val | (state->pdata->sd_config.sd_dac_out2 << 2); - else if (state->pdata && !state->pdata->sd_config.sd_dac_out2) - val = val & ~(state->pdata->sd_config.sd_dac_out2 << 2); + if (state->pdata && state->pdata->sd_config.sd_dac_out[1]) + val = val | (state->pdata->sd_config.sd_dac_out[1] << 2); + else if (state->pdata && !state->pdata->sd_config.sd_dac_out[1]) + val = val & ~(state->pdata->sd_config.sd_dac_out[1] << 2); err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val); if (err < 0) diff --git a/include/media/adv7343.h b/include/media/adv7343.h index 944757b..e4142b1 100644 --- a/include/media/adv7343.h +++ b/include/media/adv7343.h @@ -28,12 +28,7 @@ * @pll_control: PLL and oversampling control. This control allows internal * PLL 1 circuit to be powered down and the oversampling to be * switched off. - * @dac_1: power on/off DAC 1. - * @dac_2: power on/off DAC 2. - * @dac_3: power on/off DAC 3. - * @dac_4: power on/off DAC 4. - * @dac_5: power on/off DAC 5. - * @dac_6: power on/off DAC 6. + * @dac: array to configure power on/off DAC's 1..6 * * Power mode register (Register 0x0), for more info refer REGISTER MAP ACCESS * section of datasheet[1], table 17 page no 30. @@ -43,23 +38,16 @@ struct adv7343_power_mode { bool sleep_mode; bool pll_control; - bool dac_1; - bool dac_2; - bool dac_3; - bool dac_4; - bool dac_5; - bool dac_6; + u32 dac[6]; }; /** * struct adv7343_sd_config - SD Only Output Configuration. - * @sd_dac_out1: Configure SD DAC Output 1. - * @sd_dac_out2: Configure SD DAC Output 2. + * @sd_dac_out: array configuring SD DAC Outputs 1 and 2 */ struct adv7343_sd_config { /* SD only Output Configuration */ - bool sd_dac_out1; - bool sd_dac_out2; + u32 sd_dac_out[2]; }; /** -- cgit v0.10.2 From 187d42d6da62aa3eb3d76866584382625f141b3c Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 20 Jul 2013 02:21:06 -0300 Subject: [media] media: i2c: adv7343: add OF support add OF support for the adv7343 driver. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/i2c/adv7343.txt b/Documentation/devicetree/bindings/media/i2c/adv7343.txt new file mode 100644 index 0000000..5653bc2 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/adv7343.txt @@ -0,0 +1,48 @@ +* Analog Devices adv7343 video encoder + +The ADV7343 are high speed, digital-to-analog video encoders in a 64-lead LQFP +package. Six high speed, 3.3 V, 11-bit video DACs provide support for composite +(CVBS), S-Video (Y-C), and component (YPrPb/RGB) analog outputs in standard +definition (SD), enhanced definition (ED), or high definition (HD) video +formats. + +Required Properties : +- compatible: Must be "adi,adv7343" + +Optional Properties : +- adi,power-mode-sleep-mode: on enable the current consumption is reduced to + micro ampere level. All DACs and the internal PLL + circuit are disabled. +- adi,power-mode-pll-ctrl: PLL and oversampling control. This control allows + internal PLL 1 circuit to be powered down and the + oversampling to be switched off. +- ad,adv7343-power-mode-dac: array configuring the power on/off DAC's 1..6, + 0 = OFF and 1 = ON, Default value when this + property is not specified is <0 0 0 0 0 0>. +- ad,adv7343-sd-config-dac-out: array configure SD DAC Output's 1 and 2, 0 = OFF + and 1 = ON, Default value when this property is + not specified is <0 0>. + +Example: + +i2c0@1c22000 { + ... + ... + + adv7343@2a { + compatible = "adi,adv7343"; + reg = <0x2a>; + + port { + adv7343_1: endpoint { + adi,power-mode-sleep-mode; + adi,power-mode-pll-ctrl; + /* Use DAC1..3, DAC6 */ + adi,dac-enable = <1 1 1 0 0 1>; + /* Use SD DAC output 1 */ + adi,sd-dac-enable = <1 0>; + }; + }; + }; + ... +}; diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index f0238fb..aeb56c5 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "adv7343_regs.h" @@ -399,6 +400,40 @@ static int adv7343_initialize(struct v4l2_subdev *sd) return err; } +static struct adv7343_platform_data * +adv7343_get_pdata(struct i2c_client *client) +{ + struct adv7343_platform_data *pdata; + struct device_node *np; + + if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) + return client->dev.platform_data; + + np = v4l2_of_get_next_endpoint(client->dev.of_node, NULL); + if (!np) + return NULL; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + goto done; + + pdata->mode_config.sleep_mode = + of_property_read_bool(np, "adi,power-mode-sleep-mode"); + + pdata->mode_config.pll_control = + of_property_read_bool(np, "adi,power-mode-pll-ctrl"); + + of_property_read_u32_array(np, "adi,dac-enable", + pdata->mode_config.dac, 6); + + of_property_read_u32_array(np, "adi,sd-dac-enable", + pdata->sd_config.sd_dac_out, 2); + +done: + of_node_put(np); + return pdata; +} + static int adv7343_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -417,7 +452,7 @@ static int adv7343_probe(struct i2c_client *client, return -ENOMEM; /* Copy board specific information here */ - state->pdata = client->dev.platform_data; + state->pdata = adv7343_get_pdata(client); state->reg00 = 0x80; state->reg01 = 0x00; @@ -483,8 +518,17 @@ static const struct i2c_device_id adv7343_id[] = { MODULE_DEVICE_TABLE(i2c, adv7343_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id adv7343_of_match[] = { + {.compatible = "adi,adv7343", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, adv7343_of_match); +#endif + static struct i2c_driver adv7343_driver = { .driver = { + .of_match_table = of_match_ptr(adv7343_of_match), .owner = THIS_MODULE, .name = "adv7343", }, -- cgit v0.10.2 From 05fed81625bf755cc67c5864cdfd18b69ea828d1 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 3 Jul 2013 01:55:58 -0300 Subject: [media] marvell-ccic: add MIPI support for marvell-ccic driver This patch adds the MIPI support for marvell-ccic. Board driver should determine whether using MIPI or not. Signed-off-by: Albert Wang Signed-off-by: Libin Yang Acked-by: Jonathan Corbet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index 1f079ff..5628453 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -399,7 +399,7 @@ static void cafe_ctlr_init(struct mcam_camera *mcam) } -static void cafe_ctlr_power_up(struct mcam_camera *mcam) +static int cafe_ctlr_power_up(struct mcam_camera *mcam) { /* * Part one of the sensor dance: turn the global @@ -414,6 +414,8 @@ static void cafe_ctlr_power_up(struct mcam_camera *mcam) */ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0); + + return 0; } static void cafe_ctlr_power_down(struct mcam_camera *mcam) diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 0821ed0..a256804 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -253,6 +254,45 @@ static void mcam_ctlr_stop(struct mcam_camera *cam) mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); } +static void mcam_enable_mipi(struct mcam_camera *mcam) +{ + /* Using MIPI mode and enable MIPI */ + cam_dbg(mcam, "camera: DPHY3=0x%x, DPHY5=0x%x, DPHY6=0x%x\n", + mcam->dphy[0], mcam->dphy[1], mcam->dphy[2]); + mcam_reg_write(mcam, REG_CSI2_DPHY3, mcam->dphy[0]); + mcam_reg_write(mcam, REG_CSI2_DPHY5, mcam->dphy[1]); + mcam_reg_write(mcam, REG_CSI2_DPHY6, mcam->dphy[2]); + + if (!mcam->mipi_enabled) { + if (mcam->lane > 4 || mcam->lane <= 0) { + cam_warn(mcam, "lane number error\n"); + mcam->lane = 1; /* set the default value */ + } + /* + * 0x41 actives 1 lane + * 0x43 actives 2 lanes + * 0x45 actives 3 lanes (never happen) + * 0x47 actives 4 lanes + */ + mcam_reg_write(mcam, REG_CSI2_CTRL0, + CSI2_C0_MIPI_EN | CSI2_C0_ACT_LANE(mcam->lane)); + mcam_reg_write(mcam, REG_CLKCTRL, + (mcam->mclk_src << 29) | mcam->mclk_div); + + mcam->mipi_enabled = true; + } +} + +static void mcam_disable_mipi(struct mcam_camera *mcam) +{ + /* Using Parallel mode or disable MIPI */ + mcam_reg_write(mcam, REG_CSI2_CTRL0, 0x0); + mcam_reg_write(mcam, REG_CSI2_DPHY3, 0x0); + mcam_reg_write(mcam, REG_CSI2_DPHY5, 0x0); + mcam_reg_write(mcam, REG_CSI2_DPHY6, 0x0); + mcam->mipi_enabled = false; +} + /* ------------------------------------------------------------------- */ #ifdef MCAM_MODE_VMALLOC @@ -656,6 +696,13 @@ static void mcam_ctlr_image(struct mcam_camera *cam) */ mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, C0_SIFM_MASK); + + /* + * This field controls the generation of EOF(DVP only) + */ + if (cam->bus_type != V4L2_MBUS_CSI2) + mcam_reg_set_bit(cam, REG_CTRL0, + C0_EOF_VSYNC | C0_VEDGE_CTRL); } @@ -753,15 +800,21 @@ static void mcam_ctlr_stop_dma(struct mcam_camera *cam) /* * Power up and down. */ -static void mcam_ctlr_power_up(struct mcam_camera *cam) +static int mcam_ctlr_power_up(struct mcam_camera *cam) { unsigned long flags; + int ret; spin_lock_irqsave(&cam->dev_lock, flags); - cam->plat_power_up(cam); + ret = cam->plat_power_up(cam); + if (ret) { + spin_unlock_irqrestore(&cam->dev_lock, flags); + return ret; + } mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); spin_unlock_irqrestore(&cam->dev_lock, flags); msleep(5); /* Just to be sure */ + return 0; } static void mcam_ctlr_power_down(struct mcam_camera *cam) @@ -869,6 +922,17 @@ static int mcam_read_setup(struct mcam_camera *cam) spin_lock_irqsave(&cam->dev_lock, flags); clear_bit(CF_DMA_ACTIVE, &cam->flags); mcam_reset_buffers(cam); + /* + * Update CSI2_DPHY value + */ + if (cam->calc_dphy) + cam->calc_dphy(cam); + cam_dbg(cam, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n", + cam->dphy[0], cam->dphy[1], cam->dphy[2]); + if (cam->bus_type == V4L2_MBUS_CSI2) + mcam_enable_mipi(cam); + else + mcam_disable_mipi(cam); mcam_ctlr_irq_enable(cam); cam->state = S_STREAMING; if (!test_bit(CF_SG_RESTART, &cam->flags)) @@ -1475,7 +1539,9 @@ static int mcam_v4l_open(struct file *filp) ret = mcam_setup_vb2(cam); if (ret) goto out; - mcam_ctlr_power_up(cam); + ret = mcam_ctlr_power_up(cam); + if (ret) + goto out; __mcam_cam_reset(cam); mcam_set_config_needed(cam, 1); } @@ -1498,10 +1564,12 @@ static int mcam_v4l_release(struct file *filp) if (cam->users == 0) { mcam_ctlr_stop_dma(cam); mcam_cleanup_vb2(cam); + mcam_disable_mipi(cam); mcam_ctlr_power_down(cam); if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read) mcam_free_dma_bufs(cam); } + mutex_unlock(&cam->s_mutex); return 0; } @@ -1787,7 +1855,11 @@ int mccic_resume(struct mcam_camera *cam) mutex_lock(&cam->s_mutex); if (cam->users > 0) { - mcam_ctlr_power_up(cam); + ret = mcam_ctlr_power_up(cam); + if (ret) { + mutex_unlock(&cam->s_mutex); + return ret; + } __mcam_cam_reset(cam); } else { mcam_ctlr_power_down(cam); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 520c8de..7439432 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -108,11 +108,28 @@ struct mcam_camera { short int clock_speed; /* Sensor clock speed, default 30 */ short int use_smbus; /* SMBUS or straight I2c? */ enum mcam_buffer_mode buffer_mode; + + int mclk_min; /* The minimal value of mclk */ + int mclk_src; /* which clock source the mclk derives from */ + int mclk_div; /* Clock Divider Value for MCLK */ + + enum v4l2_mbus_type bus_type; + /* MIPI support */ + /* The dphy config value, allocated in board file + * dphy[0]: DPHY3 + * dphy[1]: DPHY5 + * dphy[2]: DPHY6 + */ + int *dphy; + bool mipi_enabled; /* flag whether mipi is enabled already */ + int lane; /* lane number */ + /* * Callbacks from the core to the platform code. */ - void (*plat_power_up) (struct mcam_camera *cam); + int (*plat_power_up) (struct mcam_camera *cam); void (*plat_power_down) (struct mcam_camera *cam); + void (*calc_dphy) (struct mcam_camera *cam); /* * Everything below here is private to the mcam core and @@ -225,6 +242,17 @@ int mccic_resume(struct mcam_camera *cam); #define REG_Y0BAR 0x00 #define REG_Y1BAR 0x04 #define REG_Y2BAR 0x08 + +/* + * register definitions for MIPI support + */ +#define REG_CSI2_CTRL0 0x100 +#define CSI2_C0_MIPI_EN (0x1 << 0) +#define CSI2_C0_ACT_LANE(n) ((n-1) << 1) +#define REG_CSI2_DPHY3 0x12c +#define REG_CSI2_DPHY5 0x134 +#define REG_CSI2_DPHY6 0x138 + /* ... */ #define REG_IMGPITCH 0x24 /* Image pitch register */ @@ -293,13 +321,16 @@ int mccic_resume(struct mcam_camera *cam); #define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */ #define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */ /* Bayer bits 18,19 if needed */ +#define C0_EOF_VSYNC 0x00400000 /* Generate EOF by VSYNC */ +#define C0_VEDGE_CTRL 0x00800000 /* Detect falling edge of VSYNC */ #define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */ #define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */ #define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */ #define C0_DOWNSCALE 0x08000000 /* Enable downscaler */ -#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */ +/* SIFMODE */ #define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */ -#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */ +#define C0_SOF_NOSYNC 0x40000000 /* Use inband active signaling */ +#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */ /* Bits below C1_444ALPHA are not present in Cafe */ #define REG_CTRL1 0x40 /* Control 1 */ diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index a634888..bd03668 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "mcam-core.h" @@ -38,6 +39,7 @@ struct mmp_camera { struct platform_device *pdev; struct mcam_camera mcam; struct list_head devlist; + struct clk *mipi_clk; int irq; }; @@ -112,10 +114,17 @@ static void mmpcam_power_up_ctlr(struct mmp_camera *cam) mdelay(1); } -static void mmpcam_power_up(struct mcam_camera *mcam) +static int mmpcam_power_up(struct mcam_camera *mcam) { struct mmp_camera *cam = mcam_to_cam(mcam); struct mmp_camera_platform_data *pdata; + + if (mcam->bus_type == V4L2_MBUS_CSI2) { + cam->mipi_clk = devm_clk_get(mcam->dev, "mipi"); + if ((IS_ERR(cam->mipi_clk) && mcam->dphy[2] == 0)) + return PTR_ERR(cam->mipi_clk); + } + /* * Turn on power and clocks to the controller. */ @@ -132,6 +141,7 @@ static void mmpcam_power_up(struct mcam_camera *mcam) mdelay(5); gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */ mdelay(5); + return 0; } static void mmpcam_power_down(struct mcam_camera *mcam) @@ -149,8 +159,109 @@ static void mmpcam_power_down(struct mcam_camera *mcam) pdata = cam->pdev->dev.platform_data; gpio_set_value(pdata->sensor_power_gpio, 0); gpio_set_value(pdata->sensor_reset_gpio, 0); + + if (mcam->bus_type == V4L2_MBUS_CSI2 && !IS_ERR(cam->mipi_clk)) { + if (cam->mipi_clk) + devm_clk_put(mcam->dev, cam->mipi_clk); + cam->mipi_clk = NULL; + } } +/* + * calc the dphy register values + * There are three dphy registers being used. + * dphy[0] - CSI2_DPHY3 + * dphy[1] - CSI2_DPHY5 + * dphy[2] - CSI2_DPHY6 + * CSI2_DPHY3 and CSI2_DPHY6 can be set with a default value + * or be calculated dynamically + */ +void mmpcam_calc_dphy(struct mcam_camera *mcam) +{ + struct mmp_camera *cam = mcam_to_cam(mcam); + struct mmp_camera_platform_data *pdata = cam->pdev->dev.platform_data; + struct device *dev = &cam->pdev->dev; + unsigned long tx_clk_esc; + + /* + * If CSI2_DPHY3 is calculated dynamically, + * pdata->lane_clk should be already set + * either in the board driver statically + * or in the sensor driver dynamically. + */ + /* + * dphy[0] - CSI2_DPHY3: + * bit 0 ~ bit 7: HS Term Enable. + * defines the time that the DPHY + * wait before enabling the data + * lane termination after detecting + * that the sensor has driven the data + * lanes to the LP00 bridge state. + * The value is calculated by: + * (Max T(D_TERM_EN)/Period(DDR)) - 1 + * bit 8 ~ bit 15: HS_SETTLE + * Time interval during which the HS + * receiver shall ignore any Data Lane + * HS transistions. + * The vaule has been calibrated on + * different boards. It seems to work well. + * + * More detail please refer + * MIPI Alliance Spectification for D-PHY + * document for explanation of HS-SETTLE + * and D-TERM-EN. + */ + switch (pdata->dphy3_algo) { + case DPHY3_ALGO_PXA910: + /* + * Calculate CSI2_DPHY3 algo for PXA910 + */ + pdata->dphy[0] = + (((1 + (pdata->lane_clk * 80) / 1000) & 0xff) << 8) + | (1 + pdata->lane_clk * 35 / 1000); + break; + case DPHY3_ALGO_PXA2128: + /* + * Calculate CSI2_DPHY3 algo for PXA2128 + */ + pdata->dphy[0] = + (((2 + (pdata->lane_clk * 110) / 1000) & 0xff) << 8) + | (1 + pdata->lane_clk * 35 / 1000); + break; + default: + /* + * Use default CSI2_DPHY3 value for PXA688/PXA988 + */ + dev_dbg(dev, "camera: use the default CSI2_DPHY3 value\n"); + } + + /* + * mipi_clk will never be changed, it is a fixed value on MMP + */ + if (IS_ERR(cam->mipi_clk)) + return; + + /* get the escape clk, this is hard coded */ + tx_clk_esc = (clk_get_rate(cam->mipi_clk) / 1000000) / 12; + + /* + * dphy[2] - CSI2_DPHY6: + * bit 0 ~ bit 7: CK Term Enable + * Time for the Clock Lane receiver to enable the HS line + * termination. The value is calculated similarly with + * HS Term Enable + * bit 8 ~ bit 15: CK Settle + * Time interval during which the HS receiver shall ignore + * any Clock Lane HS transitions. + * The value is calibrated on the boards. + */ + pdata->dphy[2] = + ((((534 * tx_clk_esc) / 2000 - 1) & 0xff) << 8) + | (((38 * tx_clk_esc) / 1000 - 1) & 0xff); + + dev_dbg(dev, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n", + pdata->dphy[0], pdata->dphy[1], pdata->dphy[2]); +} static irqreturn_t mmpcam_irq(int irq, void *data) { @@ -173,17 +284,30 @@ static int mmpcam_probe(struct platform_device *pdev) struct mmp_camera_platform_data *pdata; int ret; + pdata = pdev->dev.platform_data; + if (!pdata) + return -ENODEV; + cam = kzalloc(sizeof(*cam), GFP_KERNEL); if (cam == NULL) return -ENOMEM; cam->pdev = pdev; + cam->mipi_clk = NULL; INIT_LIST_HEAD(&cam->devlist); mcam = &cam->mcam; mcam->plat_power_up = mmpcam_power_up; mcam->plat_power_down = mmpcam_power_down; + mcam->calc_dphy = mmpcam_calc_dphy; mcam->dev = &pdev->dev; mcam->use_smbus = 0; + mcam->mclk_min = pdata->mclk_min; + mcam->mclk_src = pdata->mclk_src; + mcam->mclk_div = pdata->mclk_div; + mcam->bus_type = pdata->bus_type; + mcam->dphy = pdata->dphy; + mcam->mipi_enabled = false; + mcam->lane = pdata->lane; mcam->chip_id = MCAM_ARMADA610; mcam->buffer_mode = B_DMA_sg; spin_lock_init(&mcam->dev_lock); @@ -223,7 +347,6 @@ static int mmpcam_probe(struct platform_device *pdev) * Find the i2c adapter. This assumes, of course, that the * i2c bus is already up and functioning. */ - pdata = pdev->dev.platform_data; mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device); if (mcam->i2c_adapter == NULL) { ret = -ENODEV; @@ -250,10 +373,12 @@ static int mmpcam_probe(struct platform_device *pdev) /* * Power the device up and hand it off to the core. */ - mmpcam_power_up(mcam); - ret = mccic_register(mcam); + ret = mmpcam_power_up(mcam); if (ret) goto out_gpio2; + ret = mccic_register(mcam); + if (ret) + goto out_pwdn; /* * Finally, set up our IRQ now that the core is ready to * deal with it. @@ -273,8 +398,9 @@ static int mmpcam_probe(struct platform_device *pdev) out_unregister: mccic_shutdown(mcam); -out_gpio2: +out_pwdn: mmpcam_power_down(mcam); +out_gpio2: gpio_free(pdata->sensor_reset_gpio); out_gpio: gpio_free(pdata->sensor_power_gpio); -- cgit v0.10.2 From 0e394f44fbd6d6bf87afd37cee74bc341019f86b Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 3 Jul 2013 01:55:59 -0300 Subject: [media] marvell-ccic: add clock tree support for marvell-ccic driver This patch adds the clock tree support for marvell-ccic. Signed-off-by: Libin Yang Signed-off-by: Albert Wang Acked-by: Jonathan Corbet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 7439432..0de7e5f 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -88,6 +88,8 @@ struct mcam_frame_state { unsigned int delivered; }; +#define NR_MCAM_CLK 3 + /* * A description of one of our devices. * Locking: controlled by s_mutex. Certain fields, however, require @@ -124,6 +126,9 @@ struct mcam_camera { bool mipi_enabled; /* flag whether mipi is enabled already */ int lane; /* lane number */ + /* clock tree support */ + struct clk *clk[NR_MCAM_CLK]; + /* * Callbacks from the core to the platform code. */ diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index bd03668..e7be410 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -34,6 +34,8 @@ MODULE_ALIAS("platform:mmp-camera"); MODULE_AUTHOR("Jonathan Corbet "); MODULE_LICENSE("GPL"); +static char *mcam_clks[] = {"CCICAXICLK", "CCICFUNCLK", "CCICPHYCLK"}; + struct mmp_camera { void *power_regs; struct platform_device *pdev; @@ -104,6 +106,26 @@ static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev) #define REG_CCIC_DCGCR 0x28 /* CCIC dyn clock gate ctrl reg */ #define REG_CCIC_CRCR 0x50 /* CCIC clk reset ctrl reg */ +static void mcam_clk_enable(struct mcam_camera *mcam) +{ + unsigned int i; + + for (i = 0; i < NR_MCAM_CLK; i++) { + if (!IS_ERR(mcam->clk[i])) + clk_prepare_enable(mcam->clk[i]); + } +} + +static void mcam_clk_disable(struct mcam_camera *mcam) +{ + int i; + + for (i = NR_MCAM_CLK - 1; i >= 0; i--) { + if (!IS_ERR(mcam->clk[i])) + clk_disable_unprepare(mcam->clk[i]); + } +} + /* * Power control. */ @@ -141,6 +163,9 @@ static int mmpcam_power_up(struct mcam_camera *mcam) mdelay(5); gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */ mdelay(5); + + mcam_clk_enable(mcam); + return 0; } @@ -165,6 +190,8 @@ static void mmpcam_power_down(struct mcam_camera *mcam) devm_clk_put(mcam->dev, cam->mipi_clk); cam->mipi_clk = NULL; } + + mcam_clk_disable(mcam); } /* @@ -275,6 +302,35 @@ static irqreturn_t mmpcam_irq(int irq, void *data) return IRQ_RETVAL(handled); } +static void mcam_deinit_clk(struct mcam_camera *mcam) +{ + unsigned int i; + + for (i = 0; i < NR_MCAM_CLK; i++) { + if (!IS_ERR(mcam->clk[i])) { + if (mcam->clk[i]) + devm_clk_put(mcam->dev, mcam->clk[i]); + } + mcam->clk[i] = NULL; + } +} + +static void mcam_init_clk(struct mcam_camera *mcam) +{ + unsigned int i; + + for (i = 0; i < NR_MCAM_CLK; i++) { + if (mcam_clks[i] != NULL) { + /* Some clks are not necessary on some boards + * We still try to run even it fails getting clk + */ + mcam->clk[i] = devm_clk_get(mcam->dev, mcam_clks[i]); + if (IS_ERR(mcam->clk[i])) + dev_warn(mcam->dev, "Could not get clk: %s\n", + mcam_clks[i]); + } + } +} static int mmpcam_probe(struct platform_device *pdev) { @@ -370,6 +426,9 @@ static int mmpcam_probe(struct platform_device *pdev) goto out_gpio; } gpio_direction_output(pdata->sensor_reset_gpio, 0); + + mcam_init_clk(mcam); + /* * Power the device up and hand it off to the core. */ @@ -401,6 +460,7 @@ out_unregister: out_pwdn: mmpcam_power_down(mcam); out_gpio2: + mcam_deinit_clk(mcam); gpio_free(pdata->sensor_reset_gpio); out_gpio: gpio_free(pdata->sensor_power_gpio); @@ -426,6 +486,7 @@ static int mmpcam_remove(struct mmp_camera *cam) pdata = cam->pdev->dev.platform_data; gpio_free(pdata->sensor_reset_gpio); gpio_free(pdata->sensor_power_gpio); + mcam_deinit_clk(mcam); iounmap(cam->power_regs); iounmap(mcam->regs); kfree(cam); -- cgit v0.10.2 From 7c269f454e7a51b151d94f99344120efa1cd0acb Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 3 Jul 2013 01:56:00 -0300 Subject: [media] marvell-ccic: reset ccic phy when stop streaming for stability This patch adds the reset ccic phy operation when stop streaming. Stop streaming without reset ccic phy, the next start streaming may be unstable. Also need add CCIC2 definition when PXA688/PXA2128 support dual ccics. Signed-off-by: Albert Wang Signed-off-by: Libin Yang Acked-by: Jonathan Corbet Acked-by: Guennadi Liakhovetski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index a256804..56d489b 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1041,6 +1041,12 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq) return -EINVAL; mcam_ctlr_stop_dma(cam); /* + * Reset the CCIC PHY after stopping streaming, + * otherwise, the CCIC may be unstable. + */ + if (cam->ctlr_reset) + cam->ctlr_reset(cam); + /* * VB2 reclaims the buffers, so we need to forget * about them. */ diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 0de7e5f..39c6786 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -115,6 +115,7 @@ struct mcam_camera { int mclk_src; /* which clock source the mclk derives from */ int mclk_div; /* Clock Divider Value for MCLK */ + int ccic_id; enum v4l2_mbus_type bus_type; /* MIPI support */ /* The dphy config value, allocated in board file @@ -135,6 +136,7 @@ struct mcam_camera { int (*plat_power_up) (struct mcam_camera *cam); void (*plat_power_down) (struct mcam_camera *cam); void (*calc_dphy) (struct mcam_camera *cam); + void (*ctlr_reset) (struct mcam_camera *cam); /* * Everything below here is private to the mcam core and diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index e7be410..3665fbb 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -105,6 +105,7 @@ static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev) #define CPU_SUBSYS_PMU_BASE 0xd4282800 #define REG_CCIC_DCGCR 0x28 /* CCIC dyn clock gate ctrl reg */ #define REG_CCIC_CRCR 0x50 /* CCIC clk reset ctrl reg */ +#define REG_CCIC2_CRCR 0xf4 /* CCIC2 clk reset ctrl reg */ static void mcam_clk_enable(struct mcam_camera *mcam) { @@ -194,6 +195,28 @@ static void mmpcam_power_down(struct mcam_camera *mcam) mcam_clk_disable(mcam); } +void mcam_ctlr_reset(struct mcam_camera *mcam) +{ + unsigned long val; + struct mmp_camera *cam = mcam_to_cam(mcam); + + if (mcam->ccic_id) { + /* + * Using CCIC2 + */ + val = ioread32(cam->power_regs + REG_CCIC2_CRCR); + iowrite32(val & ~0x2, cam->power_regs + REG_CCIC2_CRCR); + iowrite32(val | 0x2, cam->power_regs + REG_CCIC2_CRCR); + } else { + /* + * Using CCIC1 + */ + val = ioread32(cam->power_regs + REG_CCIC_CRCR); + iowrite32(val & ~0x2, cam->power_regs + REG_CCIC_CRCR); + iowrite32(val | 0x2, cam->power_regs + REG_CCIC_CRCR); + } +} + /* * calc the dphy register values * There are three dphy registers being used. @@ -354,9 +377,11 @@ static int mmpcam_probe(struct platform_device *pdev) mcam = &cam->mcam; mcam->plat_power_up = mmpcam_power_up; mcam->plat_power_down = mmpcam_power_down; + mcam->ctlr_reset = mcam_ctlr_reset; mcam->calc_dphy = mmpcam_calc_dphy; mcam->dev = &pdev->dev; mcam->use_smbus = 0; + mcam->ccic_id = pdev->id; mcam->mclk_min = pdata->mclk_min; mcam->mclk_src = pdata->mclk_src; mcam->mclk_div = pdata->mclk_div; -- cgit v0.10.2 From 1d3953fb16c6d0c6ea099b6cfece08d19e6b1c51 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 3 Jul 2013 01:56:01 -0300 Subject: [media] marvell-ccic: refine mcam_set_contig_buffer function This patch refines mcam_set_contig_buffer() in mcam core. It can remove redundant code line and enhance readability. Signed-off-by: Albert Wang Signed-off-by: Libin Yang Acked-by: Guennadi Liakhovetski Acked-by: Jonathan Corbet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 56d489b..b0f7e23 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -481,22 +481,21 @@ static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame) */ if (list_empty(&cam->buffers)) { buf = cam->vb_bufs[frame ^ 0x1]; - cam->vb_bufs[frame] = buf; - mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, - vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0)); set_bit(CF_SINGLE_BUFFER, &cam->flags); cam->frame_state.singles++; - return; + } else { + /* + * OK, we have a buffer we can use. + */ + buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, + queue); + list_del_init(&buf->queue); + clear_bit(CF_SINGLE_BUFFER, &cam->flags); } - /* - * OK, we have a buffer we can use. - */ - buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue); - list_del_init(&buf->queue); + + cam->vb_bufs[frame] = buf; mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0)); - cam->vb_bufs[frame] = buf; - clear_bit(CF_SINGLE_BUFFER, &cam->flags); } /* -- cgit v0.10.2 From ad6ac452227b7cb93ac79beec092850d178740b1 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 3 Jul 2013 01:56:02 -0300 Subject: [media] marvell-ccic: add new formats support for marvell-ccic driver This patch adds some planar formats support for marvell-ccic. Signed-off-by: Albert Wang Signed-off-by: Libin Yang Acked-by: Jonathan Corbet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index b0f7e23..e2ad68a 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -102,6 +102,7 @@ static struct mcam_format_struct { __u8 *desc; __u32 pixelformat; int bpp; /* Bytes per pixel */ + bool planar; enum v4l2_mbus_pixelcode mbus_code; } mcam_formats[] = { { @@ -109,24 +110,56 @@ static struct mcam_format_struct { .pixelformat = V4L2_PIX_FMT_YUYV, .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, .bpp = 2, + .planar = false, + }, + { + .desc = "UYVY 4:2:2", + .pixelformat = V4L2_PIX_FMT_UYVY, + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .bpp = 2, + .planar = false, + }, + { + .desc = "YUV 4:2:2 PLANAR", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .bpp = 2, + .planar = true, + }, + { + .desc = "YUV 4:2:0 PLANAR", + .pixelformat = V4L2_PIX_FMT_YUV420, + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .bpp = 2, + .planar = true, + }, + { + .desc = "YVU 4:2:0 PLANAR", + .pixelformat = V4L2_PIX_FMT_YVU420, + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .bpp = 2, + .planar = true, }, { .desc = "RGB 444", .pixelformat = V4L2_PIX_FMT_RGB444, .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, .bpp = 2, + .planar = false, }, { .desc = "RGB 565", .pixelformat = V4L2_PIX_FMT_RGB565, .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, .bpp = 2, + .planar = false, }, { .desc = "Raw RGB Bayer", .pixelformat = V4L2_PIX_FMT_SBGGR8, .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8, - .bpp = 1 + .bpp = 1, + .planar = false, }, }; #define N_MCAM_FMTS ARRAY_SIZE(mcam_formats) @@ -169,6 +202,12 @@ struct mcam_dma_desc { u32 segment_len; }; +struct yuv_pointer_t { + dma_addr_t y; + dma_addr_t u; + dma_addr_t v; +}; + /* * Our buffer type for working with videobuf2. Note that the vb2 * developers have decreed that struct vb2_buffer must be at the @@ -180,6 +219,7 @@ struct mcam_vb_buffer { struct mcam_dma_desc *dma_desc; /* Descriptor virtual address */ dma_addr_t dma_desc_pa; /* Descriptor physical address */ int dma_desc_nent; /* Number of mapped descriptors */ + struct yuv_pointer_t yuv_p; }; static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_buffer *vb) @@ -465,6 +505,15 @@ static inline int mcam_check_dma_buffers(struct mcam_camera *cam) /* * DMA-contiguous code. */ + +static bool mcam_fmt_is_planar(__u32 pfmt) +{ + struct mcam_format_struct *f; + + f = mcam_find_format(pfmt); + return f->planar; +} + /* * Set up a contiguous buffer for the given frame. Here also is where * the underrun strategy is set: if there is no buffer available, reuse @@ -476,6 +525,11 @@ static inline int mcam_check_dma_buffers(struct mcam_camera *cam) static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame) { struct mcam_vb_buffer *buf; + struct v4l2_pix_format *fmt = &cam->pix_format; + dma_addr_t dma_handle; + u32 pixel_count = fmt->width * fmt->height; + struct vb2_buffer *vb; + /* * If there are no available buffers, go into single mode */ @@ -494,8 +548,35 @@ static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame) } cam->vb_bufs[frame] = buf; - mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, - vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0)); + vb = &buf->vb_buf; + + dma_handle = vb2_dma_contig_plane_dma_addr(vb, 0); + buf->yuv_p.y = dma_handle; + + switch (cam->pix_format.pixelformat) { + case V4L2_PIX_FMT_YUV422P: + buf->yuv_p.u = buf->yuv_p.y + pixel_count; + buf->yuv_p.v = buf->yuv_p.u + pixel_count / 2; + break; + case V4L2_PIX_FMT_YUV420: + buf->yuv_p.u = buf->yuv_p.y + pixel_count; + buf->yuv_p.v = buf->yuv_p.u + pixel_count / 4; + break; + case V4L2_PIX_FMT_YVU420: + buf->yuv_p.v = buf->yuv_p.y + pixel_count; + buf->yuv_p.u = buf->yuv_p.v + pixel_count / 4; + break; + default: + break; + } + + mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, buf->yuv_p.y); + if (mcam_fmt_is_planar(fmt->pixelformat)) { + mcam_reg_write(cam, frame == 0 ? + REG_U0BAR : REG_U1BAR, buf->yuv_p.u); + mcam_reg_write(cam, frame == 0 ? + REG_V0BAR : REG_V1BAR, buf->yuv_p.v); + } } /* @@ -653,49 +734,84 @@ static inline void mcam_sg_restart(struct mcam_camera *cam) */ static void mcam_ctlr_image(struct mcam_camera *cam) { - int imgsz; struct v4l2_pix_format *fmt = &cam->pix_format; + u32 widthy = 0, widthuv = 0, imgsz_h, imgsz_w; + + cam_dbg(cam, "camera: bytesperline = %d; height = %d\n", + fmt->bytesperline, fmt->sizeimage / fmt->bytesperline); + imgsz_h = (fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK; + imgsz_w = (fmt->width * 2) & IMGSZ_H_MASK; + + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + widthy = fmt->width * 2; + widthuv = 0; + break; + case V4L2_PIX_FMT_JPEG: + imgsz_h = (fmt->sizeimage / fmt->bytesperline) << IMGSZ_V_SHIFT; + widthy = fmt->bytesperline; + widthuv = 0; + break; + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + widthy = fmt->width; + widthuv = fmt->width / 2; + break; + default: + widthy = fmt->bytesperline; + widthuv = 0; + } + + mcam_reg_write_mask(cam, REG_IMGPITCH, widthuv << 16 | widthy, + IMGP_YP_MASK | IMGP_UVP_MASK); + mcam_reg_write(cam, REG_IMGSIZE, imgsz_h | imgsz_w); + mcam_reg_write(cam, REG_IMGOFFSET, 0x0); - imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) | - (fmt->bytesperline & IMGSZ_H_MASK); - mcam_reg_write(cam, REG_IMGSIZE, imgsz); - mcam_reg_write(cam, REG_IMGOFFSET, 0); - /* YPITCH just drops the last two bits */ - mcam_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline, - IMGP_YP_MASK); /* * Tell the controller about the image format we are using. */ - switch (cam->pix_format.pixelformat) { + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_YUV422P: + mcam_reg_write_mask(cam, REG_CTRL0, + C0_DF_YUV | C0_YUV_PLANAR | C0_YUVE_YVYU, C0_DF_MASK); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + mcam_reg_write_mask(cam, REG_CTRL0, + C0_DF_YUV | C0_YUV_420PL | C0_YUVE_YVYU, C0_DF_MASK); + break; case V4L2_PIX_FMT_YUYV: - mcam_reg_write_mask(cam, REG_CTRL0, - C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV, - C0_DF_MASK); - break; - + mcam_reg_write_mask(cam, REG_CTRL0, + C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_UYVY, C0_DF_MASK); + break; + case V4L2_PIX_FMT_UYVY: + mcam_reg_write_mask(cam, REG_CTRL0, + C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_YUYV, C0_DF_MASK); + break; + case V4L2_PIX_FMT_JPEG: + mcam_reg_write_mask(cam, REG_CTRL0, + C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_YUYV, C0_DF_MASK); + break; case V4L2_PIX_FMT_RGB444: - mcam_reg_write_mask(cam, REG_CTRL0, - C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB, - C0_DF_MASK); + mcam_reg_write_mask(cam, REG_CTRL0, + C0_DF_RGB | C0_RGBF_444 | C0_RGB4_XRGB, C0_DF_MASK); /* Alpha value? */ - break; - + break; case V4L2_PIX_FMT_RGB565: - mcam_reg_write_mask(cam, REG_CTRL0, - C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR, - C0_DF_MASK); - break; - + mcam_reg_write_mask(cam, REG_CTRL0, + C0_DF_RGB | C0_RGBF_565 | C0_RGB5_BGGR, C0_DF_MASK); + break; default: - cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat); - break; + cam_err(cam, "camera: unknown format: %#x\n", fmt->pixelformat); + break; } + /* * Make sure it knows we want to use hsync/vsync. */ - mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, - C0_SIFM_MASK); - + mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, C0_SIFM_MASK); /* * This field controls the generation of EOF(DVP only) */ @@ -1156,6 +1272,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam) #ifdef MCAM_MODE_DMA_CONTIG vq->ops = &mcam_vb2_ops; vq->mem_ops = &vb2_dma_contig_memops; + vq->buf_struct_size = sizeof(struct mcam_vb_buffer); cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev); vq->io_modes = VB2_MMAP | VB2_USERPTR; cam->dma_setup = mcam_ctlr_dma_contig; @@ -1166,6 +1283,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam) #ifdef MCAM_MODE_DMA_SG vq->ops = &mcam_vb2_sg_ops; vq->mem_ops = &vb2_dma_sg_memops; + vq->buf_struct_size = sizeof(struct mcam_vb_buffer); vq->io_modes = VB2_MMAP | VB2_USERPTR; cam->dma_setup = mcam_ctlr_dma_sg; cam->frame_complete = mcam_dma_sg_done; @@ -1316,7 +1434,15 @@ static int mcam_vidioc_try_fmt_vid_cap(struct file *filp, void *priv, ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt); mutex_unlock(&cam->s_mutex); v4l2_fill_pix_format(pix, &mbus_fmt); - pix->bytesperline = pix->width * f->bpp; + switch (f->pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + pix->bytesperline = pix->width * 3 / 2; + break; + default: + pix->bytesperline = pix->width * f->bpp; + break; + } pix->sizeimage = pix->height * pix->bytesperline; return ret; } diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 39c6786..e0e628c 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -249,6 +249,12 @@ int mccic_resume(struct mcam_camera *cam); #define REG_Y0BAR 0x00 #define REG_Y1BAR 0x04 #define REG_Y2BAR 0x08 +#define REG_U0BAR 0x0c +#define REG_U1BAR 0x10 +#define REG_U2BAR 0x14 +#define REG_V0BAR 0x18 +#define REG_V1BAR 0x1C +#define REG_V2BAR 0x20 /* * register definitions for MIPI support -- cgit v0.10.2 From 0a0b3fb42f78eded6a8d1249c545224242d8999e Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 3 Jul 2013 01:56:03 -0300 Subject: [media] marvell-ccic: add SOF / EOF pair check for marvell-ccic driver This patch adds the SOFx/EOFx pair check for marvell-ccic. When switching format, the last EOF may not arrive when stop streamning. And the EOF will be detected in the next start streaming. Must ensure clear the left over frame flags before every really start streaming. Signed-off-by: Albert Wang Signed-off-by: Libin Yang Acked-by: Jonathan Corbet Acked-by: Guennadi Liakhovetski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index e2ad68a..5184887 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -94,6 +94,9 @@ MODULE_PARM_DESC(buffer_mode, #define CF_CONFIG_NEEDED 4 /* Must configure hardware */ #define CF_SINGLE_BUFFER 5 /* Running with a single buffer */ #define CF_SG_RESTART 6 /* SG restart needed */ +#define CF_FRAME_SOF0 7 /* Frame 0 started */ +#define CF_FRAME_SOF1 8 +#define CF_FRAME_SOF2 9 #define sensor_call(cam, o, f, args...) \ v4l2_subdev_call(cam->sensor, o, f, ##args) @@ -260,8 +263,10 @@ static void mcam_reset_buffers(struct mcam_camera *cam) int i; cam->next_buf = -1; - for (i = 0; i < cam->nbufs; i++) + for (i = 0; i < cam->nbufs; i++) { clear_bit(i, &cam->flags); + clear_bit(CF_FRAME_SOF0 + i, &cam->flags); + } } static inline int mcam_needs_config(struct mcam_camera *cam) @@ -1122,6 +1127,7 @@ static void mcam_vb_wait_finish(struct vb2_queue *vq) static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count) { struct mcam_camera *cam = vb2_get_drv_priv(vq); + unsigned int frame; if (cam->state != S_IDLE) { INIT_LIST_HEAD(&cam->buffers); @@ -1139,6 +1145,14 @@ static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count) cam->state = S_BUFWAIT; return 0; } + + /* + * Ensure clear the left over frame flags + * before every really start streaming + */ + for (frame = 0; frame < cam->nbufs; frame++) + clear_bit(CF_FRAME_SOF0 + frame, &cam->flags); + return mcam_read_setup(cam); } @@ -1816,9 +1830,11 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs) * each time. */ for (frame = 0; frame < cam->nbufs; frame++) - if (irqs & (IRQ_EOF0 << frame)) { + if (irqs & (IRQ_EOF0 << frame) && + test_bit(CF_FRAME_SOF0 + frame, &cam->flags)) { mcam_frame_complete(cam, frame); handled = 1; + clear_bit(CF_FRAME_SOF0 + frame, &cam->flags); if (cam->buffer_mode == B_DMA_sg) break; } @@ -1827,9 +1843,15 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs) * code assumes that we won't get multiple frame interrupts * at once; may want to rethink that. */ - if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) { + for (frame = 0; frame < cam->nbufs; frame++) { + if (irqs & (IRQ_SOF0 << frame)) { + set_bit(CF_FRAME_SOF0 + frame, &cam->flags); + handled = IRQ_HANDLED; + } + } + + if (handled == IRQ_HANDLED) { set_bit(CF_DMA_ACTIVE, &cam->flags); - handled = 1; if (cam->buffer_mode == B_DMA_sg) mcam_ctlr_stop(cam); } -- cgit v0.10.2 From 578e99d6cd98c58b68d9c3ad22b4e0c1528a2826 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 3 Jul 2013 01:56:04 -0300 Subject: [media] marvell-ccic: switch to resource managed allocation and request This patch switchs to resource managed allocation and request in mmp-driver. It can remove free resource operations. Signed-off-by: Albert Wang Signed-off-by: Libin Yang Acked-by: Jonathan Corbet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 3665fbb..f06daa4 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -367,7 +367,7 @@ static int mmpcam_probe(struct platform_device *pdev) if (!pdata) return -ENODEV; - cam = kzalloc(sizeof(*cam), GFP_KERNEL); + cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL); if (cam == NULL) return -ENOMEM; cam->pdev = pdev; @@ -398,15 +398,11 @@ static int mmpcam_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "no iomem resource!\n"); - ret = -ENODEV; - goto out_free; - } - mcam->regs = ioremap(res->start, resource_size(res)); - if (mcam->regs == NULL) { - dev_err(&pdev->dev, "MMIO ioremap fail\n"); - ret = -ENODEV; - goto out_free; + return -ENODEV; } + mcam->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mcam->regs)) + return PTR_ERR(mcam->regs); mcam->regs_size = resource_size(res); /* * Power/clock memory is elsewhere; get it too. Perhaps this @@ -415,40 +411,37 @@ static int mmpcam_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (res == NULL) { dev_err(&pdev->dev, "no power resource!\n"); - ret = -ENODEV; - goto out_unmap1; - } - cam->power_regs = ioremap(res->start, resource_size(res)); - if (cam->power_regs == NULL) { - dev_err(&pdev->dev, "power MMIO ioremap fail\n"); - ret = -ENODEV; - goto out_unmap1; + return -ENODEV; } + cam->power_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(cam->power_regs)) + return PTR_ERR(cam->power_regs); /* * Find the i2c adapter. This assumes, of course, that the * i2c bus is already up and functioning. */ mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device); if (mcam->i2c_adapter == NULL) { - ret = -ENODEV; dev_err(&pdev->dev, "No i2c adapter\n"); - goto out_unmap2; + return -ENODEV; } /* * Sensor GPIO pins. */ - ret = gpio_request(pdata->sensor_power_gpio, "cam-power"); + ret = devm_gpio_request(&pdev->dev, pdata->sensor_power_gpio, + "cam-power"); if (ret) { dev_err(&pdev->dev, "Can't get sensor power gpio %d", pdata->sensor_power_gpio); - goto out_unmap2; + return ret; } gpio_direction_output(pdata->sensor_power_gpio, 0); - ret = gpio_request(pdata->sensor_reset_gpio, "cam-reset"); + ret = devm_gpio_request(&pdev->dev, pdata->sensor_reset_gpio, + "cam-reset"); if (ret) { dev_err(&pdev->dev, "Can't get sensor reset gpio %d", pdata->sensor_reset_gpio); - goto out_gpio; + return ret; } gpio_direction_output(pdata->sensor_reset_gpio, 0); @@ -459,10 +452,10 @@ static int mmpcam_probe(struct platform_device *pdev) */ ret = mmpcam_power_up(mcam); if (ret) - goto out_gpio2; + goto out_deinit_clk; ret = mccic_register(mcam); if (ret) - goto out_pwdn; + goto out_power_down; /* * Finally, set up our IRQ now that the core is ready to * deal with it. @@ -473,8 +466,8 @@ static int mmpcam_probe(struct platform_device *pdev) goto out_unregister; } cam->irq = res->start; - ret = request_irq(cam->irq, mmpcam_irq, IRQF_SHARED, - "mmp-camera", mcam); + ret = devm_request_irq(&pdev->dev, cam->irq, mmpcam_irq, IRQF_SHARED, + "mmp-camera", mcam); if (ret == 0) { mmpcam_add_device(cam); return 0; @@ -482,19 +475,10 @@ static int mmpcam_probe(struct platform_device *pdev) out_unregister: mccic_shutdown(mcam); -out_pwdn: +out_power_down: mmpcam_power_down(mcam); -out_gpio2: +out_deinit_clk: mcam_deinit_clk(mcam); - gpio_free(pdata->sensor_reset_gpio); -out_gpio: - gpio_free(pdata->sensor_power_gpio); -out_unmap2: - iounmap(cam->power_regs); -out_unmap1: - iounmap(mcam->regs); -out_free: - kfree(cam); return ret; } -- cgit v0.10.2 From 725d7b7139e990c802874a0c440f9914b41c11e6 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Sun, 26 May 2013 22:31:59 -0300 Subject: [media] drivers/media/radio/radio-maxiradio: Convert to module_pci_driver use module_pci_driver instead of init/exit, make code clean Signed-off-by: Libo Chen Acked-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c index bd4d3a7..1d1c9e1 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -200,15 +200,4 @@ static struct pci_driver maxiradio_driver = { .remove = maxiradio_remove, }; -static int __init maxiradio_init(void) -{ - return pci_register_driver(&maxiradio_driver); -} - -static void __exit maxiradio_exit(void) -{ - pci_unregister_driver(&maxiradio_driver); -} - -module_init(maxiradio_init); -module_exit(maxiradio_exit); +module_pci_driver(maxiradio_driver); -- cgit v0.10.2 From 3333447c6946f8a76548d1599686a2bbd11e9264 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Mon, 24 Jun 2013 15:57:36 -0300 Subject: [media] tlg2300: implement error handling in poseidon_probe() All poseidon init functions properly return error codes, but they are ignored by poseidon_probe(). The patch implements handling of error cases. 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 e07e4c6..ca5e1bc 100644 --- a/drivers/media/usb/tlg2300/pd-main.c +++ b/drivers/media/usb/tlg2300/pd-main.c @@ -436,12 +436,22 @@ static int poseidon_probe(struct usb_interface *interface, /* register v4l2 device */ ret = v4l2_device_register(&interface->dev, &pd->v4l2_dev); + if (ret) + goto err_v4l2; /* register devices in directory /dev */ ret = pd_video_init(pd); - poseidon_audio_init(pd); - poseidon_fm_init(pd); - pd_dvb_usb_device_init(pd); + if (ret) + goto err_video; + ret = poseidon_audio_init(pd); + if (ret) + goto err_audio; + ret = poseidon_fm_init(pd); + if (ret) + goto err_fm; + ret = pd_dvb_usb_device_init(pd); + if (ret) + goto err_dvb; INIT_LIST_HEAD(&pd->device_list); list_add_tail(&pd->device_list, &pd_device_list); @@ -459,6 +469,17 @@ static int poseidon_probe(struct usb_interface *interface, } #endif return 0; +err_dvb: + poseidon_fm_exit(pd); +err_fm: + poseidon_audio_free(pd); +err_audio: + pd_video_exit(pd); +err_video: + v4l2_device_unregister(&pd->v4l2_dev); +err_v4l2: + kfree(pd); + return ret; } static void poseidon_disconnect(struct usb_interface *interface) -- cgit v0.10.2 From ac22521cb2dc07e0b2193868c647dee5d737f85a Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Mon, 24 Jun 2013 15:57:37 -0300 Subject: [media] tlg2300: fix checking firmware in poseidon_probe() check_firmware() makes sure firmware is in a device. It returns zero on success and error code otherwise. Also it sets down_firmware flag to 1 if downloading occurs. The only caller poseidon_probe() checks down_firmware flag and returns 0 without any initialization if it is set. That looks very strange, so the patch removes down_firmware argument of check_firmware() and returns error code if check_firmware() fails in poseidon_probe(). Not tested on real hardware. 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 ca5e1bc..95f94e5 100644 --- a/drivers/media/usb/tlg2300/pd-main.c +++ b/drivers/media/usb/tlg2300/pd-main.c @@ -375,7 +375,7 @@ static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev) } #endif -static int check_firmware(struct usb_device *udev, int *down_firmware) +static int check_firmware(struct usb_device *udev) { void *buf; int ret; @@ -395,10 +395,8 @@ static int check_firmware(struct usb_device *udev, int *down_firmware) USB_CTRL_GET_TIMEOUT); kfree(buf); - if (ret < 0) { - *down_firmware = 1; + if (ret < 0) return firmware_download(udev); - } return 0; } @@ -411,9 +409,9 @@ static int poseidon_probe(struct usb_interface *interface, int new_one = 0; /* download firmware */ - check_firmware(udev, &ret); + ret = check_firmware(udev); if (ret) - return 0; + return ret; /* Do I recovery from the hibernate ? */ pd = find_old_poseidon(udev); -- cgit v0.10.2 From 2206112b157d0ba736bead38eaf04b4ade24ece7 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Fri, 12 Jul 2013 05:03:03 -0300 Subject: [media] usbtv: Add S-Video input support Alongside already existing Composite input. Signed-off-by: Lubomir Rintel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/usbtv/usbtv.c b/drivers/media/usb/usbtv/usbtv.c index bf43f87..b164e54 100644 --- a/drivers/media/usb/usbtv/usbtv.c +++ b/drivers/media/usb/usbtv/usbtv.c @@ -90,17 +90,78 @@ struct usbtv { * out when a new one begins. */ u32 frame_id; + enum { + USBTV_COMPOSITE_INPUT, + USBTV_SVIDEO_INPUT, + } input; int iso_size; unsigned int sequence; struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS]; }; -static int usbtv_setup_capture(struct usbtv *usbtv) +static int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size) { int ret; int pipe = usb_rcvctrlpipe(usbtv->udev, 0); int i; - static const u16 protoregs[][2] = { + + for (i = 0; i < size; i++) { + u16 index = regs[i][0]; + u16 value = regs[i][1]; + + ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static int usbtv_select_input(struct usbtv *usbtv, int input) +{ + int ret; + + static const u16 composite[][2] = { + { USBTV_BASE + 0x0105, 0x0060 }, + { USBTV_BASE + 0x011f, 0x00f2 }, + { USBTV_BASE + 0x0127, 0x0060 }, + { USBTV_BASE + 0x00ae, 0x0010 }, + { USBTV_BASE + 0x0284, 0x00aa }, + { USBTV_BASE + 0x0239, 0x0060 }, + }; + + static const u16 svideo[][2] = { + { USBTV_BASE + 0x0105, 0x0010 }, + { USBTV_BASE + 0x011f, 0x00ff }, + { USBTV_BASE + 0x0127, 0x0060 }, + { USBTV_BASE + 0x00ae, 0x0030 }, + { USBTV_BASE + 0x0284, 0x0088 }, + { USBTV_BASE + 0x0239, 0x0060 }, + }; + + switch (input) { + case USBTV_COMPOSITE_INPUT: + ret = usbtv_set_regs(usbtv, composite, ARRAY_SIZE(composite)); + break; + case USBTV_SVIDEO_INPUT: + ret = usbtv_set_regs(usbtv, svideo, ARRAY_SIZE(svideo)); + break; + default: + ret = -EINVAL; + } + + if (!ret) + usbtv->input = input; + + return ret; +} + +static int usbtv_setup_capture(struct usbtv *usbtv) +{ + int ret; + static const u16 setup[][2] = { /* These seem to enable the device. */ { USBTV_BASE + 0x0008, 0x0001 }, { USBTV_BASE + 0x01d0, 0x00ff }, @@ -188,16 +249,13 @@ static int usbtv_setup_capture(struct usbtv *usbtv) { USBTV_BASE + 0x024f, 0x0002 }, }; - for (i = 0; i < ARRAY_SIZE(protoregs); i++) { - u16 index = protoregs[i][0]; - u16 value = protoregs[i][1]; + ret = usbtv_set_regs(usbtv, setup, ARRAY_SIZE(setup)); + if (ret) + return ret; - ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 0); - if (ret < 0) - return ret; - } + ret = usbtv_select_input(usbtv, usbtv->input); + if (ret) + return ret; return 0; } @@ -418,10 +476,17 @@ static int usbtv_querycap(struct file *file, void *priv, static int usbtv_enum_input(struct file *file, void *priv, struct v4l2_input *i) { - if (i->index > 0) + switch (i->index) { + case USBTV_COMPOSITE_INPUT: + strlcpy(i->name, "Composite", sizeof(i->name)); + break; + case USBTV_SVIDEO_INPUT: + strlcpy(i->name, "S-Video", sizeof(i->name)); + break; + default: return -EINVAL; + } - strlcpy(i->name, "Composite", sizeof(i->name)); i->type = V4L2_INPUT_TYPE_CAMERA; i->std = V4L2_STD_525_60; return 0; @@ -461,15 +526,15 @@ static int usbtv_g_std(struct file *file, void *priv, v4l2_std_id *norm) static int usbtv_g_input(struct file *file, void *priv, unsigned int *i) { - *i = 0; + struct usbtv *usbtv = video_drvdata(file); + *i = usbtv->input; return 0; } static int usbtv_s_input(struct file *file, void *priv, unsigned int i) { - if (i > 0) - return -EINVAL; - return 0; + struct usbtv *usbtv = video_drvdata(file); + return usbtv_select_input(usbtv, i); } static int usbtv_s_std(struct file *file, void *priv, v4l2_std_id norm) -- cgit v0.10.2 From 2fa8de191cd96658b881b5425e59d10d3d48281e Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Mon, 15 Jul 2013 15:12:21 -0300 Subject: [media] ml86v7667: override default field interlace order ML86V7667 always transmits top field first for both PAL and NTSC -- that makes application incorrectly treat interlaced fields when relying on the standard. Hence we must set V4L2_FIELD_INTERLACED_TB format explicitly. [Sergei: added a comment.] Reported-by: Katsuya MATSUBARA Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Tested-by: Katsuya MATSUBARA Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index efdc873..dd1e6c9 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -209,7 +209,8 @@ static int ml86v7667_mbus_fmt(struct v4l2_subdev *sd, fmt->code = V4L2_MBUS_FMT_YUYV8_2X8; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - fmt->field = V4L2_FIELD_INTERLACED; + /* The top field is always transferred first by the chip */ + fmt->field = V4L2_FIELD_INTERLACED_TB; fmt->width = 720; fmt->height = priv->std & V4L2_STD_525_60 ? 480 : 576; -- cgit v0.10.2 From fb8d61c90fdef217c41a71efcba4c3cc7fa925f4 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 16 Jul 2013 22:01:12 -0300 Subject: [media] usbtv: remove unused including Remove including that don't need it. Signed-off-by: Wei Yongjun Acked-by: Lubomir Rintel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/usbtv/usbtv.c b/drivers/media/usb/usbtv/usbtv.c index b164e54..0952316 100644 --- a/drivers/media/usb/usbtv/usbtv.c +++ b/drivers/media/usb/usbtv/usbtv.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include -- cgit v0.10.2 From 9928ac7cddd5db4cdf3121f1992192adb41df9c3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 17 Jul 2013 04:43:22 -0300 Subject: [media] stk1160: Allow to change input while streaming Remove the check as there's no reason to prevent the input from being set when the device is streaming. This allows surveillance applications (such as motion, zoneminder, etc.) to configure the input while streaming. Reported-by: Sergey 'Jin' Bostandzhyan Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 876fc26..ee46d82 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -440,9 +440,6 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct stk1160 *dev = video_drvdata(file); - if (vb2_is_busy(&dev->vb_vidq)) - return -EBUSY; - if (i > STK1160_MAX_INPUT) return -EINVAL; -- cgit v0.10.2 From b3ba8fa6b89bd99efe75080bfd8f3168781ae7de Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 18 Jul 2013 09:01:23 -0300 Subject: [media] media: stk1160: Ignore unchanged standard set This commit adds an early check to vidioc_s_std() to detect if the new and current standards are equal, and exit with success in that case. This is needed to prevent userspace applications that might attempt to re-set the same standard from failing if that's done when streaming has started. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index ee46d82..c45c988 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -379,6 +379,9 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) struct stk1160 *dev = video_drvdata(file); struct vb2_queue *q = &dev->vb_vidq; + if (dev->norm == norm) + return 0; + if (vb2_is_busy(q)) return -EBUSY; -- cgit v0.10.2 From 8fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Tue, 14 May 2013 16:54:43 -0300 Subject: [media] tea575x-tuner: move HW init to a separate function Move HW initialization to separate function to allow using the code without the v4l parts. This is needed for use in the bttv driver. Signed-off-by: Ondrej Zary Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h index 098c4de..2d4fa59 100644 --- a/include/sound/tea575x-tuner.h +++ b/include/sound/tea575x-tuner.h @@ -71,6 +71,7 @@ struct snd_tea575x { int (*ext_init)(struct snd_tea575x *tea); }; +int snd_tea575x_hw_init(struct snd_tea575x *tea); int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner); void snd_tea575x_exit(struct snd_tea575x *tea); void snd_tea575x_set_freq(struct snd_tea575x *tea); diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 8a36a1d..46ec4dff 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -486,13 +486,9 @@ static const struct v4l2_ctrl_ops tea575x_ctrl_ops = { .s_ctrl = tea575x_s_ctrl, }; -/* - * initialize all the tea575x chips - */ -int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner) -{ - int retval; +int snd_tea575x_hw_init(struct snd_tea575x *tea) +{ tea->mute = true; /* Not all devices can or know how to read the data back. @@ -507,6 +503,17 @@ int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner) tea->freq = 90500 * 16; /* 90.5Mhz default */ snd_tea575x_set_freq(tea); + return 0; +} +EXPORT_SYMBOL(snd_tea575x_hw_init); + +int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner) +{ + int retval = snd_tea575x_hw_init(tea); + + if (retval) + return retval; + tea->vd = tea575x_radio; video_set_drvdata(&tea->vd, tea); mutex_init(&tea->mutex); -- cgit v0.10.2 From cd2e34ead8bf530c45e0446dc4bbbd9e35f28af9 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Tue, 14 May 2013 16:54:44 -0300 Subject: [media] bttv: stop abusing mbox_we for sw_status Kodicom 4400R and Geovision GV-800 code in bttv driver abuses mbox_we (int) in struct bttv as char *. Remove this hack and add a proper sw_status array to struct bttv instead. This is a a preparation to remove mbox_we. Signed-off-by: Ondrej Zary Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c index e564aac..d85cb0a 100644 --- a/drivers/media/pci/bt8xx/bttv-cards.c +++ b/drivers/media/pci/bt8xx/bttv-cards.c @@ -4441,9 +4441,7 @@ static void tibetCS16_init(struct bttv *btv) * is {3, 0, 2, 1}, i.e. the first controller to be detected is logical * unit 3, the second (which is the master) is logical unit 0, etc. * We need to maintain the status of the analog switch (which of the 16 - * cameras is connected to which of the 4 controllers). Rather than - * add to the bttv structure for this, we use the data reserved for - * the mbox (unused for this card type). + * cameras is connected to which of the 4 controllers) in sw_status array. */ /* @@ -4478,7 +4476,6 @@ static void kodicom4400r_write(struct bttv *btv, */ static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input) { - char *sw_status; int xaddr, yaddr; struct bttv *mctlr; static unsigned char map[4] = {3, 0, 2, 1}; @@ -4489,14 +4486,13 @@ static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input) } yaddr = (btv->c.nr - mctlr->c.nr + 1) & 3; /* the '&' is for safety */ yaddr = map[yaddr]; - sw_status = (char *)(&mctlr->mbox_we); xaddr = input & 0xf; /* Check if the controller/camera pair has changed, else ignore */ - if (sw_status[yaddr] != xaddr) + if (mctlr->sw_status[yaddr] != xaddr) { /* "open" the old switch, "close" the new one, save the new */ - kodicom4400r_write(mctlr, sw_status[yaddr], yaddr, 0); - sw_status[yaddr] = xaddr; + kodicom4400r_write(mctlr, mctlr->sw_status[yaddr], yaddr, 0); + mctlr->sw_status[yaddr] = xaddr; kodicom4400r_write(mctlr, xaddr, yaddr, 1); } } @@ -4509,7 +4505,6 @@ static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input) */ static void kodicom4400r_init(struct bttv *btv) { - char *sw_status = (char *)(&btv->mbox_we); int ix; gpio_inout(0x0003ff, 0x0003ff); @@ -4517,7 +4512,7 @@ static void kodicom4400r_init(struct bttv *btv) gpio_write(0); /* Preset camera 0 to the 4 controllers */ for (ix = 0; ix < 4; ix++) { - sw_status[ix] = ix; + btv->sw_status[ix] = ix; kodicom4400r_write(btv, ix, ix, 1); } /* @@ -4794,7 +4789,6 @@ static void gv800s_write(struct bttv *btv, static void gv800s_muxsel(struct bttv *btv, unsigned int input) { struct bttv *mctlr; - char *sw_status; int xaddr, yaddr; static unsigned int map[4][4] = { { 0x0, 0x4, 0xa, 0x6 }, { 0x1, 0x5, 0xb, 0x7 }, @@ -4807,14 +4801,13 @@ static void gv800s_muxsel(struct bttv *btv, unsigned int input) return; } yaddr = (btv->c.nr - mctlr->c.nr) & 3; - sw_status = (char *)(&mctlr->mbox_we); xaddr = map[yaddr][input] & 0xf; /* Check if the controller/camera pair has changed, ignore otherwise */ - if (sw_status[yaddr] != xaddr) { + if (mctlr->sw_status[yaddr] != xaddr) { /* disable the old switch, enable the new one and save status */ - gv800s_write(mctlr, sw_status[yaddr], yaddr, 0); - sw_status[yaddr] = xaddr; + gv800s_write(mctlr, mctlr->sw_status[yaddr], yaddr, 0); + mctlr->sw_status[yaddr] = xaddr; gv800s_write(mctlr, xaddr, yaddr, 1); } } @@ -4822,7 +4815,6 @@ static void gv800s_muxsel(struct bttv *btv, unsigned int input) /* GeoVision GV-800(S) "master" chip init */ static void gv800s_init(struct bttv *btv) { - char *sw_status = (char *)(&btv->mbox_we); int ix; gpio_inout(0xf107f, 0xf107f); @@ -4831,7 +4823,7 @@ static void gv800s_init(struct bttv *btv) /* Preset camera 0 to the 4 controllers */ for (ix = 0; ix < 4; ix++) { - sw_status[ix] = ix; + btv->sw_status[ix] = ix; gv800s_write(btv, ix, ix, 1); } diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h index 9c1cc2c..6eefb59 100644 --- a/drivers/media/pci/bt8xx/bttvp.h +++ b/drivers/media/pci/bt8xx/bttvp.h @@ -459,6 +459,9 @@ struct bttv { int mbox_iow; int mbox_csel; + /* switch status for multi-controller cards */ + char sw_status[4]; + /* risc memory management data - must acquire s_lock before changing these - only the irq handler is supported to touch top + bottom + vcurr */ -- cgit v0.10.2 From eb27fafef69568a82c46c27a1761f037e4272b87 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Fri, 19 Jul 2013 13:46:17 -0300 Subject: [media] radio-aztech: Convert to generic lm7000 implementation Aztech radio card is based on LM7001 chip which is (software) compatible with LM7000. So remove the LM7000-specific code from the driver and use generic code. Also found that the card does not have A0..A2 ISA pins connected, which means that the region size is 8 bytes. Signed-off-by: Ondrej Zary Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c index 177bcbd..2f5671f 100644 --- a/drivers/media/radio/radio-aztech.c +++ b/drivers/media/radio/radio-aztech.c @@ -26,6 +26,7 @@ #include #include #include "radio-isa.h" +#include "lm7000.h" MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); MODULE_DESCRIPTION("A driver for the Aztech radio card."); @@ -54,18 +55,29 @@ struct aztech { int curvol; }; -static void send_0_byte(struct aztech *az) -{ - udelay(radio_wait_time); - outb_p(2 + az->curvol, az->isa.io); - outb_p(64 + 2 + az->curvol, az->isa.io); -} +/* bit definitions for register read */ +#define AZTECH_BIT_NOT_TUNED (1 << 0) +#define AZTECH_BIT_MONO (1 << 1) +/* bit definitions for register write */ +#define AZTECH_BIT_TUN_CE (1 << 1) +#define AZTECH_BIT_TUN_CLK (1 << 6) +#define AZTECH_BIT_TUN_DATA (1 << 7) +/* bits 0 and 2 are volume control, bits 3..5 are not connected */ -static void send_1_byte(struct aztech *az) +static void aztech_set_pins(void *handle, u8 pins) { - udelay(radio_wait_time); - outb_p(128 + 2 + az->curvol, az->isa.io); - outb_p(128 + 64 + 2 + az->curvol, az->isa.io); + struct radio_isa_card *isa = handle; + struct aztech *az = container_of(isa, struct aztech, isa); + u8 bits = az->curvol; + + if (pins & LM7000_DATA) + bits |= AZTECH_BIT_TUN_DATA; + if (pins & LM7000_CLK) + bits |= AZTECH_BIT_TUN_CLK; + if (pins & LM7000_CE) + bits |= AZTECH_BIT_TUN_CE; + + outb_p(bits, az->isa.io); } static struct radio_isa_card *aztech_alloc(void) @@ -77,39 +89,7 @@ static struct radio_isa_card *aztech_alloc(void) static int aztech_s_frequency(struct radio_isa_card *isa, u32 freq) { - struct aztech *az = container_of(isa, struct aztech, isa); - int i; - - freq += 171200; /* Add 10.7 MHz IF */ - freq /= 800; /* Convert to 50 kHz units */ - - send_0_byte(az); /* 0: LSB of frequency */ - - for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ - if (freq & (1 << i)) - send_1_byte(az); - else - send_0_byte(az); - - send_0_byte(az); /* 14: test bit - always 0 */ - send_0_byte(az); /* 15: test bit - always 0 */ - send_0_byte(az); /* 16: band data 0 - always 0 */ - if (isa->stereo) /* 17: stereo (1 to enable) */ - send_1_byte(az); - else - send_0_byte(az); - - send_1_byte(az); /* 18: band data 1 - unknown */ - send_0_byte(az); /* 19: time base - always 0 */ - send_0_byte(az); /* 20: spacing (0 = 25 kHz) */ - send_1_byte(az); /* 21: spacing (1 = 25 kHz) */ - send_0_byte(az); /* 22: spacing (0 = 25 kHz) */ - send_1_byte(az); /* 23: AM/FM (FM = 1, always) */ - - /* latch frequency */ - - udelay(radio_wait_time); - outb_p(128 + 64 + az->curvol, az->isa.io); + lm7000_set_freq(freq, isa, aztech_set_pins); return 0; } @@ -165,7 +145,7 @@ static struct radio_isa_driver aztech_driver = { .radio_nr_params = radio_nr, .io_ports = aztech_ioports, .num_of_io_ports = ARRAY_SIZE(aztech_ioports), - .region_size = 2, + .region_size = 8, .card = "Aztech Radio", .ops = &aztech_ops, .has_stereo = true, -- cgit v0.10.2 From 1c9f11ed21816290a7cea36fc2dc3b16d5590340 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Fri, 19 Jul 2013 13:46:18 -0300 Subject: [media] radio-aztech: Implement signal strength detection and fix stereo detection Current stereo detection code is wrong - it reads TUNED bit instead of STEREO bit. Fix that and implement signal strength detection too. Also remove useless s_stereo functionn. Signed-off-by: Ondrej Zary Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c index 2f5671f..705dd6f 100644 --- a/drivers/media/radio/radio-aztech.c +++ b/drivers/media/radio/radio-aztech.c @@ -94,21 +94,16 @@ static int aztech_s_frequency(struct radio_isa_card *isa, u32 freq) return 0; } -/* thanks to Michael Dwyer for giving me a dose of clues in - * the signal strength department.. - * - * This card has a stereo bit - bit 0 set = mono, not set = stereo - */ static u32 aztech_g_rxsubchans(struct radio_isa_card *isa) { - if (inb(isa->io) & 1) + if (inb(isa->io) & AZTECH_BIT_MONO) return V4L2_TUNER_SUB_MONO; return V4L2_TUNER_SUB_STEREO; } -static int aztech_s_stereo(struct radio_isa_card *isa, bool stereo) +static u32 aztech_g_signal(struct radio_isa_card *isa) { - return aztech_s_frequency(isa, isa->freq); + return (inb(isa->io) & AZTECH_BIT_NOT_TUNED) ? 0 : 0xffff; } static int aztech_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) @@ -126,8 +121,8 @@ static const struct radio_isa_ops aztech_ops = { .alloc = aztech_alloc, .s_mute_volume = aztech_s_mute_volume, .s_frequency = aztech_s_frequency, - .s_stereo = aztech_s_stereo, .g_rxsubchans = aztech_g_rxsubchans, + .g_signal = aztech_g_signal, }; static const int aztech_ioports[] = { 0x350, 0x358 }; -- cgit v0.10.2 From 51dd4d70fc59564454a4dcb90d6d46d39a4a97ef Mon Sep 17 00:00:00 2001 From: Alban Browaeys Date: Tue, 16 Jul 2013 19:06:46 -0300 Subject: [media] em28xx: Fix vidioc fmt vid cap v4l2 compliance Set fmt.pix.priv to zero in vidioc_try_fmt_vid_cap. Catched by v4l2-compliance. Signed-off-by: Alban Browaeys [hans.verkuil@cisco.com: dropping unnecessary change to g_fmt_vid_cap] Signed-off-by: Hans Verkuil 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 1a577ed..9d10334 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1008,6 +1008,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, else f->fmt.pix.field = dev->interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; + f->fmt.pix.priv = 0; return 0; } -- cgit v0.10.2 From 1444fbf268c4dd8c77790ea28bf7560b815c5b28 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 26 Jun 2013 10:37:36 -0300 Subject: [media] staging: lirc: clean error handling in probe() We have reorganized the error handling into a simpler and more canonical format. Additionally we removed extra empty lines, switched to devm_kzalloc(), and substitute 'minor' by 'ret' in the igorplugusb_remote_probe() function. Signed-off-by: Dan Carpenter Signed-off-by: Andy Shevchenko Acked-by: Dan Carpenter Signed-off-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 2faa391..28c8b0b 100644 --- a/drivers/staging/media/lirc/lirc_igorplugusb.c +++ b/drivers/staging/media/lirc/lirc_igorplugusb.c @@ -240,10 +240,6 @@ static int unregister_from_lirc(struct igorplug *ir) dprintk(DRIVER_NAME "[%d]: calling lirc_unregister_driver\n", devnum); lirc_unregister_driver(d->minor); - kfree(d); - ir->d = NULL; - kfree(ir); - return devnum; } @@ -377,20 +373,16 @@ static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf) return -ENODATA; } - - static int igorplugusb_remote_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *dev = NULL; + struct usb_device *dev; struct usb_host_interface *idesc = NULL; struct usb_endpoint_descriptor *ep; struct igorplug *ir = NULL; struct lirc_driver *driver = NULL; int devnum, pipe, maxp; - int minor = 0; char buf[63], name[128] = ""; - int mem_failure = 0; int ret; dprintk(DRIVER_NAME ": usb probe called.\n"); @@ -416,24 +408,18 @@ static int igorplugusb_remote_probe(struct usb_interface *intf, dprintk(DRIVER_NAME "[%d]: bytes_in_key=%zu maxp=%d\n", devnum, CODE_LENGTH, maxp); - mem_failure = 0; - ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL); - if (!ir) { - mem_failure = 1; - goto mem_failure_switch; - } - driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); - if (!driver) { - mem_failure = 2; - goto mem_failure_switch; - } + ir = devm_kzalloc(&intf->dev, sizeof(*ir), GFP_KERNEL); + if (!ir) + return -ENOMEM; + + driver = devm_kzalloc(&intf->dev, sizeof(*driver), GFP_KERNEL); + if (!driver) + return -ENOMEM; ir->buf_in = usb_alloc_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN, GFP_ATOMIC, &ir->dma_in); - if (!ir->buf_in) { - mem_failure = 3; - goto mem_failure_switch; - } + if (!ir->buf_in) + return -ENOMEM; strcpy(driver->name, DRIVER_NAME " "); driver->minor = -1; @@ -449,27 +435,14 @@ static int igorplugusb_remote_probe(struct usb_interface *intf, driver->dev = &intf->dev; driver->owner = THIS_MODULE; - minor = lirc_register_driver(driver); - if (minor < 0) - mem_failure = 9; - -mem_failure_switch: - - switch (mem_failure) { - case 9: + ret = lirc_register_driver(driver); + if (ret < 0) { usb_free_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN, ir->buf_in, ir->dma_in); - case 3: - kfree(driver); - case 2: - kfree(ir); - case 1: - printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n", - devnum, mem_failure); - return -ENOMEM; + return ret; } - driver->minor = minor; + driver->minor = ret; ir->d = driver; ir->devnum = devnum; ir->usbdev = dev; @@ -502,7 +475,6 @@ mem_failure_switch: return 0; } - static void igorplugusb_remote_disconnect(struct usb_interface *intf) { struct usb_device *usbdev = interface_to_usbdev(intf); -- cgit v0.10.2 From 1de6ebba2c2759da4d8ccb167336a4d136d08471 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Sun, 7 Jul 2013 20:22:45 -0300 Subject: [media] ene_ir: Fix interrupt line passthrough to hardware While we can delay IRQ intialization, we need the interrupt number right away because unusually hardware have programable interrupt number, and thus we give it the number that was allocated by BIOS Signed-off-by: Maxim Levitsky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index ed184f6..4214311 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1022,6 +1022,8 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) spin_lock_init(&dev->hw_lock); dev->hw_io = pnp_port_start(pnp_dev, 0); + dev->irq = pnp_irq(pnp_dev, 0); + pnp_set_drvdata(pnp_dev, dev); dev->pnp_dev = pnp_dev; @@ -1085,7 +1087,6 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) goto exit_unregister_device; } - dev->irq = pnp_irq(pnp_dev, 0); if (request_irq(dev->irq, ene_isr, IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) { goto exit_release_hw_io; -- cgit v0.10.2 From b5daad2f13c90ef3175d201450543fd0a1627bdd Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Sun, 7 Jul 2013 20:22:46 -0300 Subject: [media] ene_ir: disable the device if wake is disabled It doesn't hurt and on my notebook despite clearing the wake flag the remote still wakes up the system. This way it doesn't. Signed-off-by: Maxim Levitsky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index 4214311..c1444f8 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -476,7 +476,7 @@ select_timeout: } /* Enable the device for receive */ -static void ene_rx_enable(struct ene_device *dev) +static void ene_rx_enable_hw(struct ene_device *dev) { u8 reg_value; @@ -504,11 +504,17 @@ static void ene_rx_enable(struct ene_device *dev) /* enter idle mode */ ir_raw_event_set_idle(dev->rdev, true); +} + +/* Enable the device for receive - wrapper to track the state*/ +static void ene_rx_enable(struct ene_device *dev) +{ + ene_rx_enable_hw(dev); dev->rx_enabled = true; } /* Disable the device receiver */ -static void ene_rx_disable(struct ene_device *dev) +static void ene_rx_disable_hw(struct ene_device *dev) { /* disable inputs */ ene_rx_enable_cir_engine(dev, false); @@ -516,8 +522,13 @@ static void ene_rx_disable(struct ene_device *dev) /* disable hardware IRQ and firmware flag */ ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_ENABLE | ENE_FW1_IRQ); - ir_raw_event_set_idle(dev->rdev, true); +} + +/* Disable the device receiver - wrapper to track the state */ +static void ene_rx_disable(struct ene_device *dev) +{ + ene_rx_disable_hw(dev); dev->rx_enabled = false; } @@ -1124,9 +1135,8 @@ static void ene_remove(struct pnp_dev *pnp_dev) } /* enable wake on IR (wakes on specific button on original remote) */ -static void ene_enable_wake(struct ene_device *dev, int enable) +static void ene_enable_wake(struct ene_device *dev, bool enable) { - enable = enable && device_may_wakeup(&dev->pnp_dev->dev); dbg("wake on IR %s", enable ? "enabled" : "disabled"); ene_set_clear_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, enable); } @@ -1135,9 +1145,12 @@ static void ene_enable_wake(struct ene_device *dev, int enable) static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state) { struct ene_device *dev = pnp_get_drvdata(pnp_dev); - ene_enable_wake(dev, true); + bool wake = device_may_wakeup(&dev->pnp_dev->dev); + + if (!wake && dev->rx_enabled) + ene_rx_disable_hw(dev); - /* TODO: add support for wake pattern */ + ene_enable_wake(dev, wake); return 0; } -- cgit v0.10.2 From 408ed9924c0d56d07e0888f6ae560a534ce5c18f Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Sun, 7 Jul 2013 20:22:47 -0300 Subject: [media] ene_ir: don't use pr_debug after all This way to only way to get debug info is to use dynamic debug, but I left debugging prints to debug hardware issues, and so I want this to be enabled by module param. Signed-off-by: Maxim Levitsky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ene_ir.h b/drivers/media/rc/ene_ir.h index 6f978e8..a7911e3 100644 --- a/drivers/media/rc/ene_ir.h +++ b/drivers/media/rc/ene_ir.h @@ -185,7 +185,7 @@ #define __dbg(level, format, ...) \ do { \ if (debug >= level) \ - pr_debug(format "\n", ## __VA_ARGS__); \ + pr_info(format "\n", ## __VA_ARGS__); \ } while (0) #define dbg(format, ...) __dbg(1, format, ## __VA_ARGS__) -- cgit v0.10.2 From 3c2ade017a7e10470aa8112f8a4bfdb2ee30f97f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 12:08:08 -0300 Subject: [media] V4L: Drop bus_type check in v4l2-async match functions These match_* functions are internal callbacks and are always invoked only after checking asd->bus_type. So drop redundant checks in match_i2c() and match_platform() functions. Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index aae2417..ff87c29 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -27,7 +27,6 @@ static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) #if IS_ENABLED(CONFIG_I2C) struct i2c_client *client = i2c_verify_client(dev); return client && - asd->bus_type == V4L2_ASYNC_BUS_I2C && asd->match.i2c.adapter_id == client->adapter->nr && asd->match.i2c.address == client->addr; #else @@ -37,8 +36,7 @@ static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) static bool match_platform(struct device *dev, struct v4l2_async_subdev *asd) { - return asd->bus_type == V4L2_ASYNC_BUS_PLATFORM && - !strcmp(asd->match.platform.name, dev_name(dev)); + return !strcmp(asd->match.platform.name, dev_name(dev)); } static LIST_HEAD(subdev_list); -- cgit v0.10.2 From cfca7644d7959e1f50cb132306d1557bf6c2da57 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 12:14:46 -0300 Subject: [media] V4L: Rename v4l2_async_bus_* to v4l2_async_match_* enum v4l2_async_bus_type also selects a method subdevs are matched in the notification handlers, rename it to v4l2_async_match_type so V4L2_ASYNC_MATCH_OF entry can be further added for matching by device tree node pointer. Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index f2de006..dae9716 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1837,9 +1837,9 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) for (j = 0; pcdev->pdata->asd_sizes[j]; j++) { for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) { dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n", - __func__, i, (*asd)->bus_type); - if ((*asd)->bus_type == V4L2_ASYNC_BUS_PLATFORM && - !strncmp(name, (*asd)->match.platform.name, + __func__, i, (*asd)->match_type); + if ((*asd)->match_type == V4L2_ASYNC_MATCH_DEVNAME && + !strncmp(name, (*asd)->match.device_name.name, sizeof(name) - 1)) { pcdev->csi2_asd = *asd; break; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 2dd0e52..8af572b 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1475,7 +1475,7 @@ static int scan_async_group(struct soc_camera_host *ici, break; } - if (i == size || asd[i]->bus_type != V4L2_ASYNC_BUS_I2C) { + if (i == size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) { /* All useless */ dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n"); return -ENODEV; diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index ff87c29..86934ca 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -34,9 +34,9 @@ static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) #endif } -static bool match_platform(struct device *dev, struct v4l2_async_subdev *asd) +static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd) { - return !strcmp(asd->match.platform.name, dev_name(dev)); + return !strcmp(asd->match.device_name.name, dev_name(dev)); } static LIST_HEAD(subdev_list); @@ -53,17 +53,17 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * list_for_each_entry(asd, ¬ifier->waiting, list) { /* bus_type has been verified valid before */ - switch (asd->bus_type) { - case V4L2_ASYNC_BUS_CUSTOM: + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_CUSTOM: match = asd->match.custom.match; if (!match) /* Match always */ return asd; break; - case V4L2_ASYNC_BUS_PLATFORM: - match = match_platform; + case V4L2_ASYNC_MATCH_DEVNAME: + match = match_devname; break; - case V4L2_ASYNC_BUS_I2C: + case V4L2_ASYNC_MATCH_I2C: match = match_i2c; break; default: @@ -141,15 +141,15 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, for (i = 0; i < notifier->num_subdevs; i++) { asd = notifier->subdev[i]; - switch (asd->bus_type) { - case V4L2_ASYNC_BUS_CUSTOM: - case V4L2_ASYNC_BUS_PLATFORM: - case V4L2_ASYNC_BUS_I2C: + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_CUSTOM: + case V4L2_ASYNC_MATCH_DEVNAME: + case V4L2_ASYNC_MATCH_I2C: break; default: dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL, - "Invalid bus-type %u on %p\n", - asd->bus_type, asd); + "Invalid match type %u on %p\n", + asd->match_type, asd); return -EINVAL; } list_add_tail(&asd->list, ¬ifier->waiting); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index c3ec6ac..33e3b2a 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -22,10 +22,10 @@ struct v4l2_async_notifier; /* A random max subdevice number, used to allocate an array on stack */ #define V4L2_MAX_SUBDEVS 128U -enum v4l2_async_bus_type { - V4L2_ASYNC_BUS_CUSTOM, - V4L2_ASYNC_BUS_PLATFORM, - V4L2_ASYNC_BUS_I2C, +enum v4l2_async_match_type { + V4L2_ASYNC_MATCH_CUSTOM, + V4L2_ASYNC_MATCH_DEVNAME, + V4L2_ASYNC_MATCH_I2C, }; /** @@ -36,11 +36,11 @@ enum v4l2_async_bus_type { * probed, to a notifier->waiting list */ struct v4l2_async_subdev { - enum v4l2_async_bus_type bus_type; + enum v4l2_async_match_type match_type; union { struct { const char *name; - } platform; + } device_name; struct { int adapter_id; unsigned short address; -- cgit v0.10.2 From e7359f8e660882fb2168609638163561eb2f50f0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 12:21:29 -0300 Subject: [media] V4L: Add V4L2_ASYNC_MATCH_OF subdev matching type Add support for matching by device_node pointer. This allows the notifier user to simply pass a list of device_node pointers corresponding to sub-devices. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 86934ca..9f91013 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -39,6 +39,11 @@ static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd) return !strcmp(asd->match.device_name.name, dev_name(dev)); } +static bool match_of(struct device *dev, struct v4l2_async_subdev *asd) +{ + return dev->of_node == asd->match.of.node; +} + static LIST_HEAD(subdev_list); static LIST_HEAD(notifier_list); static DEFINE_MUTEX(list_lock); @@ -66,6 +71,9 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * case V4L2_ASYNC_MATCH_I2C: match = match_i2c; break; + case V4L2_ASYNC_MATCH_OF: + match = match_of; + break; default: /* Cannot happen, unless someone breaks us */ WARN_ON(true); @@ -145,6 +153,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, case V4L2_ASYNC_MATCH_CUSTOM: case V4L2_ASYNC_MATCH_DEVNAME: case V4L2_ASYNC_MATCH_I2C: + case V4L2_ASYNC_MATCH_OF: break; default: dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL, diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 33e3b2a..82b2981 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -15,6 +15,7 @@ #include struct device; +struct device_node; struct v4l2_device; struct v4l2_subdev; struct v4l2_async_notifier; @@ -26,6 +27,7 @@ enum v4l2_async_match_type { V4L2_ASYNC_MATCH_CUSTOM, V4L2_ASYNC_MATCH_DEVNAME, V4L2_ASYNC_MATCH_I2C, + V4L2_ASYNC_MATCH_OF, }; /** @@ -39,6 +41,9 @@ struct v4l2_async_subdev { enum v4l2_async_match_type match_type; union { struct { + const struct device_node *node; + } of; + struct { const char *name; } device_name; struct { -- cgit v0.10.2 From e8419d0890efaccb93f9f274d9ba0aae91210e8c Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 12:31:10 -0300 Subject: [media] V4L: Rename subdev field of struct v4l2_async_notifier This is a purely cosmetic change. Since the 'subdev' member points to an array of subdevs make it more explicit by renaming to the plural form. Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski 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 b11d7a7..7fbde6d 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -2168,7 +2168,7 @@ static __init int vpif_probe(struct platform_device *pdev) } vpif_probe_complete(); } else { - vpif_obj.notifier.subdev = vpif_obj.config->asd; + vpif_obj.notifier.subdevs = vpif_obj.config->asd; vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; vpif_obj.notifier.bound = vpif_async_bound; vpif_obj.notifier.complete = vpif_async_complete; diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index c2ff067..6336dfc 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1832,7 +1832,7 @@ static __init int vpif_probe(struct platform_device *pdev) } vpif_probe_complete(); } else { - vpif_obj.notifier.subdev = vpif_obj.config->asd; + vpif_obj.notifier.subdevs = vpif_obj.config->asd; vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; vpif_obj.notifier.bound = vpif_async_bound; vpif_obj.notifier.complete = vpif_async_complete; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 8af572b..4b42572 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1501,7 +1501,7 @@ static int scan_async_group(struct soc_camera_host *ici, return -ENOMEM; } - sasc->notifier.subdev = asd; + sasc->notifier.subdevs = asd; sasc->notifier.num_subdevs = size; sasc->notifier.bound = soc_camera_async_bound; sasc->notifier.unbind = soc_camera_async_unbind; diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 9f91013..ed31a65 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -147,7 +147,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, INIT_LIST_HEAD(¬ifier->done); for (i = 0; i < notifier->num_subdevs; i++) { - asd = notifier->subdev[i]; + asd = notifier->subdevs[i]; switch (asd->match_type) { case V4L2_ASYNC_MATCH_CUSTOM: diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 82b2981..8fac8ea 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -77,7 +77,7 @@ struct v4l2_async_subdev_list { /** * v4l2_async_notifier - v4l2_device notifier data * @num_subdevs:number of subdevices - * @subdev: array of pointers to subdevice descriptors + * @subdevs: array of pointers to subdevice descriptors * @v4l2_dev: pointer to struct v4l2_device * @waiting: list of struct v4l2_async_subdev, waiting for their drivers * @done: list of struct v4l2_async_subdev_list, already probed @@ -88,7 +88,7 @@ struct v4l2_async_subdev_list { */ struct v4l2_async_notifier { unsigned int num_subdevs; - struct v4l2_async_subdev **subdev; + struct v4l2_async_subdev **subdevs; struct v4l2_device *v4l2_dev; struct list_head waiting; struct list_head done; -- cgit v0.10.2 From b426b3a660c85faf6e1ca1c92c6d43577022aa3b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 22 Jul 2013 08:01:33 -0300 Subject: [media] V4L: Merge struct v4l2_async_subdev_list with struct v4l2_subdev By integrating the v4l2-async API internals a bit more with the core overall the v4l2-async code becomes a bit simpler and easier to follow. Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index ed31a65..b350ab9 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -49,12 +49,10 @@ static LIST_HEAD(notifier_list); static DEFINE_MUTEX(list_lock); static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier, - struct v4l2_async_subdev_list *asdl) + struct v4l2_subdev *sd) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); struct v4l2_async_subdev *asd; - bool (*match)(struct device *, - struct v4l2_async_subdev *); + bool (*match)(struct device *, struct v4l2_async_subdev *); list_for_each_entry(asd, ¬ifier->waiting, list) { /* bus_type has been verified valid before */ @@ -89,16 +87,15 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * } static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, - struct v4l2_async_subdev_list *asdl, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); int ret; /* Remove from the waiting list */ list_del(&asd->list); - asdl->asd = asd; - asdl->notifier = notifier; + sd->asd = asd; + sd->notifier = notifier; if (notifier->bound) { ret = notifier->bound(notifier, sd, asd); @@ -106,7 +103,7 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, return ret; } /* Move from the global subdevice list to notifier's done */ - list_move(&asdl->list, ¬ifier->done); + list_move(&sd->async_list, ¬ifier->done); ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd); if (ret < 0) { @@ -121,21 +118,19 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, return 0; } -static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl) +static void v4l2_async_cleanup(struct v4l2_subdev *sd) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); - v4l2_device_unregister_subdev(sd); - /* Subdevice driver will reprobe and put asdl back onto the list */ - list_del_init(&asdl->list); - asdl->asd = NULL; + /* Subdevice driver will reprobe and put the subdev back onto the list */ + list_del_init(&sd->async_list); + sd->asd = NULL; sd->dev = NULL; } int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, struct v4l2_async_notifier *notifier) { - struct v4l2_async_subdev_list *asdl, *tmp; + struct v4l2_subdev *sd, *tmp; struct v4l2_async_subdev *asd; int i; @@ -169,14 +164,14 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, /* Keep also completed notifiers on the list */ list_add(¬ifier->list, ¬ifier_list); - list_for_each_entry_safe(asdl, tmp, &subdev_list, list) { + list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) { int ret; - asd = v4l2_async_belongs(notifier, asdl); + asd = v4l2_async_belongs(notifier, sd); if (!asd) continue; - ret = v4l2_async_test_notify(notifier, asdl, asd); + ret = v4l2_async_test_notify(notifier, sd, asd); if (ret < 0) { mutex_unlock(&list_lock); return ret; @@ -191,7 +186,7 @@ EXPORT_SYMBOL(v4l2_async_notifier_register); void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) { - struct v4l2_async_subdev_list *asdl, *tmp; + struct v4l2_subdev *sd, *tmp; unsigned int notif_n_subdev = notifier->num_subdevs; unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS); struct device *dev[n_subdev]; @@ -201,18 +196,16 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) list_del(¬ifier->list); - list_for_each_entry_safe(asdl, tmp, ¬ifier->done, list) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); - + list_for_each_entry_safe(sd, tmp, ¬ifier->done, list) { dev[i] = get_device(sd->dev); - v4l2_async_cleanup(asdl); + v4l2_async_cleanup(sd); /* If we handled USB devices, we'd have to lock the parent too */ device_release_driver(dev[i++]); if (notifier->unbind) - notifier->unbind(notifier, sd, sd->asdl.asd); + notifier->unbind(notifier, sd, sd->asd); } mutex_unlock(&list_lock); @@ -241,24 +234,23 @@ EXPORT_SYMBOL(v4l2_async_notifier_unregister); int v4l2_async_register_subdev(struct v4l2_subdev *sd) { - struct v4l2_async_subdev_list *asdl = &sd->asdl; struct v4l2_async_notifier *notifier; mutex_lock(&list_lock); - INIT_LIST_HEAD(&asdl->list); + INIT_LIST_HEAD(&sd->async_list); list_for_each_entry(notifier, ¬ifier_list, list) { - struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl); + struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd); if (asd) { - int ret = v4l2_async_test_notify(notifier, asdl, asd); + int ret = v4l2_async_test_notify(notifier, sd, asd); mutex_unlock(&list_lock); return ret; } } /* None matched, wait for hot-plugging */ - list_add(&asdl->list, &subdev_list); + list_add(&sd->async_list, &subdev_list); mutex_unlock(&list_lock); @@ -268,23 +260,22 @@ EXPORT_SYMBOL(v4l2_async_register_subdev); void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) { - struct v4l2_async_subdev_list *asdl = &sd->asdl; - struct v4l2_async_notifier *notifier = asdl->notifier; + struct v4l2_async_notifier *notifier = sd->notifier; - if (!asdl->asd) { - if (!list_empty(&asdl->list)) - v4l2_async_cleanup(asdl); + if (!sd->asd) { + if (!list_empty(&sd->async_list)) + v4l2_async_cleanup(sd); return; } mutex_lock(&list_lock); - list_add(&asdl->asd->list, ¬ifier->waiting); + list_add(&sd->asd->list, ¬ifier->waiting); - v4l2_async_cleanup(asdl); + v4l2_async_cleanup(sd); if (notifier->unbind) - notifier->unbind(notifier, sd, sd->asdl.asd); + notifier->unbind(notifier, sd, sd->asd); mutex_unlock(&list_lock); } diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 8fac8ea..7683569 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -62,25 +62,12 @@ struct v4l2_async_subdev { }; /** - * v4l2_async_subdev_list - provided by subdevices - * @list: links struct v4l2_async_subdev_list objects to a global list - * before probing, and onto notifier->done after probing - * @asd: pointer to respective struct v4l2_async_subdev - * @notifier: pointer to managing notifier - */ -struct v4l2_async_subdev_list { - struct list_head list; - struct v4l2_async_subdev *asd; - struct v4l2_async_notifier *notifier; -}; - -/** * v4l2_async_notifier - v4l2_device notifier data * @num_subdevs:number of subdevices * @subdevs: array of pointers to subdevice descriptors * @v4l2_dev: pointer to struct v4l2_device * @waiting: list of struct v4l2_async_subdev, waiting for their drivers - * @done: list of struct v4l2_async_subdev_list, already probed + * @done: list of struct v4l2_subdev, already probed * @list: member in a global list of notifiers * @bound: a subdevice driver has successfully probed one of subdevices * @complete: all subdevices have been probed successfully diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 3250cc5..bfda0fe 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -586,15 +586,14 @@ struct v4l2_subdev { struct video_device *devnode; /* pointer to the physical device, if any */ struct device *dev; - struct v4l2_async_subdev_list asdl; + /* Links this subdev to a global subdev_list or @notifier->done list. */ + struct list_head async_list; + /* Pointer to respective struct v4l2_async_subdev. */ + struct v4l2_async_subdev *asd; + /* Pointer to the managing notifier. */ + struct v4l2_async_notifier *notifier; }; -static inline struct v4l2_subdev *v4l2_async_to_subdev( - struct v4l2_async_subdev_list *asdl) -{ - return container_of(asdl, struct v4l2_subdev, asdl); -} - #define media_entity_to_v4l2_subdev(ent) \ container_of(ent, struct v4l2_subdev, entity) #define vdev_to_v4l2_subdev(vdev) \ -- cgit v0.10.2 From fde4796825de84c5114500e6a09ea8b346b93c6b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 11 Jul 2013 05:20:15 -0300 Subject: [media] DocBook: Fix typo in V4L2_CID_JPEG_COMPRESSION_QUALITY reference Replace the erroneous V4L2_CID_JPEG_IMAGE_QUALITY control name with V4L2_CID_JPEG_COMPRESSION_QUALITY. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-g-jpegcomp.xml b/Documentation/DocBook/media/v4l/vidioc-g-jpegcomp.xml index 4874849..098ff48 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-jpegcomp.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-jpegcomp.xml @@ -92,8 +92,8 @@ to add them. int quality Deprecated. If - V4L2_CID_JPEG_IMAGE_QUALITY control is exposed by - a driver applications should use it instead and ignore this field. + V4L2_CID_JPEG_COMPRESSION_QUALITY control is exposed + by a driver applications should use it instead and ignore this field. -- cgit v0.10.2 From 6026ce07dca045a9edf4fb651a805b4574186d80 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 8 Jul 2013 17:33:08 -0300 Subject: [media] redrat3: errors on unplug In an usb disconnect handler, the urbs have already been cancelled so the attempt to disable the IR receiver just results in errors: [ 899.638862] redrat3 7-2:1.0: redrat3_send_cmd: Error sending rr3 cmd res -110, data 0 [ 899.638870] redrat3 7-2:1.0: redrat3_disable_detector: detector status: 251, should be 0 Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 12167a6..3749443 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -206,8 +206,6 @@ struct redrat3_dev { struct timer_list rx_timeout; u32 hw_timeout; - /* is the detector enabled*/ - bool det_enabled; /* Is the device currently transmitting?*/ bool transmitting; @@ -472,32 +470,11 @@ static int redrat3_enable_detector(struct redrat3_dev *rr3) return -EIO; } - rr3->det_enabled = true; redrat3_issue_async(rr3); return 0; } -/* Disables the rr3 long range detector */ -static void redrat3_disable_detector(struct redrat3_dev *rr3) -{ - struct device *dev = rr3->dev; - u8 ret; - - rr3_ftr(dev, "Entering %s\n", __func__); - - ret = redrat3_send_cmd(RR3_RC_DET_DISABLE, rr3); - if (ret != 0) - dev_err(dev, "%s: failure!\n", __func__); - - ret = redrat3_send_cmd(RR3_RC_DET_STATUS, rr3); - if (ret != 0) - dev_warn(dev, "%s: detector status: %d, should be 0\n", - __func__, ret); - - rr3->det_enabled = false; -} - static inline void redrat3_delete(struct redrat3_dev *rr3, struct usb_device *udev) { @@ -788,7 +765,6 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, count = min_t(unsigned, count, RR3_MAX_SIG_SIZE - RR3_TX_TRAILER_LEN); /* rr3 will disable rc detector on transmit */ - rr3->det_enabled = false; rr3->transmitting = true; sample_lens = kzalloc(sizeof(int) * RR3_DRIVER_MAXLENS, GFP_KERNEL); @@ -868,7 +844,6 @@ out: rr3->transmitting = false; /* rr3 re-enables rc detector because it was enabled before */ - rr3->det_enabled = true; return ret; } @@ -1048,8 +1023,6 @@ static void redrat3_dev_disconnect(struct usb_interface *intf) if (!rr3) return; - redrat3_disable_detector(rr3); - usb_set_intfdata(intf, NULL); rc_unregister_device(rr3->rc); del_timer_sync(&rr3->rx_timeout); -- cgit v0.10.2 From 58a61f99620d635fcdaf84c9b1818b79e11d28c5 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 8 Jul 2013 17:33:10 -0300 Subject: [media] rc: allowed_protos now is a bit field This one must have missed the conversion "c003ab1b [media] rc-core: add separate defines for protocol bitmaps and numbers". Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index c2b635d..0306cb7 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -1212,7 +1212,7 @@ static struct dvb_usb_device_properties vp7049_properties = { .rc_interval = 150, .rc_codes = RC_MAP_TWINHAN_VP1027_DVBS, .rc_query = m920x_rc_core_query, - .allowed_protos = RC_TYPE_UNKNOWN, + .allowed_protos = RC_BIT_UNKNOWN, }, .size_of_priv = sizeof(struct m920x_state), -- cgit v0.10.2 From 25379bf8bc4d4e83bd74d823048b85a95ae5a521 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 8 Jul 2013 17:33:11 -0300 Subject: [media] lirc: validate transmission ir data The lirc interface allows 255 u32 spaces and pulses, which are usec. If the driver can handle this (e.g. winbond-cir) you can produce hours of meaningless IR data and there is no method of interrupting it. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index e456126..e5be920 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -140,11 +140,20 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, goto out; } + for (i = 0; i < count; i++) { + if (txbuf[i] > IR_MAX_DURATION / 1000 - duration || !txbuf[i]) { + ret = -EINVAL; + goto out; + } + + duration += txbuf[i]; + } + ret = dev->tx_ir(dev, txbuf, count); if (ret < 0) goto out; - for (i = 0; i < ret; i++) + for (duration = i = 0; i < ret; i++) duration += txbuf[i]; ret *= sizeof(unsigned int); -- cgit v0.10.2 From 671ea6707b3fb051ec1bae8e7aa0a91f90e178e0 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 8 Jul 2013 17:33:09 -0300 Subject: [media] lirc: make transmit interface consistent All lirc drivers that can transmit, return EINVAL when they are passed more than IR data than they can send. That is, except for two drivers which I touched. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/lirc_device_interface.xml b/Documentation/DocBook/media/v4l/lirc_device_interface.xml index 8d7eb6b..34cada2 100644 --- a/Documentation/DocBook/media/v4l/lirc_device_interface.xml +++ b/Documentation/DocBook/media/v4l/lirc_device_interface.xml @@ -46,7 +46,9 @@ describing an IR signal are read from the chardev. values. Pulses and spaces are only marked implicitly by their position. The data must start and end with a pulse, therefore, the data must always include an uneven number of samples. The write function must block until the data has -been transmitted by the hardware. +been transmitted by the hardware. If more data is provided than the hardware +can send, the driver returns EINVAL. +
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index a4ab2e6..19632b1 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -364,8 +364,8 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count) periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000); bytes = DIV_ROUND_UP(periods, 127); if (size + bytes > ir->bufsize) { - count = i; - break; + rc = -EINVAL; + goto out; } while (periods > 127) { ir->packet->payload[size++] = 127 | space; diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 3749443..0042367 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -762,7 +762,8 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, return -EAGAIN; } - count = min_t(unsigned, count, RR3_MAX_SIG_SIZE - RR3_TX_TRAILER_LEN); + if (count > RR3_MAX_SIG_SIZE - RR3_TX_TRAILER_LEN) + return -EINVAL; /* rr3 will disable rc detector on transmit */ rr3->transmitting = true; @@ -801,8 +802,8 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, &irdata->lens[curlencheck]); curlencheck++; } else { - count = i - 1; - break; + ret = -EINVAL; + goto out; } } irdata->sigdata[i] = lencheck; -- cgit v0.10.2 From b43ea8068d2090cb1e44632c8a938ab40d2c7419 Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Wed, 17 Jul 2013 14:28:16 -0300 Subject: [media] cx23885: Fix TeVii S471 regression since introduction of ts2020 Patch to make TeVii S471 cards use the ts2020 tuner, since ds3000 driver no longer contains tuning code. Signed-off-by: Johannes Koch 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 9c5ed10..bb291c6 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -1249,6 +1249,10 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend = dvb_attach(ds3000_attach, &tevii_ds3000_config, &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(ts2020_attach, fe0->dvb.frontend, + &tevii_ts2020_config, &i2c_bus->i2c_adap); + } break; case CX23885_BOARD_PROF_8000: i2c_bus = &dev->i2c_bus[0]; -- cgit v0.10.2 From 8358e76c14ab4103f27bf2258fbf6dae4cceac8b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 21 Jun 2013 03:55:32 -0300 Subject: [media] coda: dynamic IRAM setup for decoder This sets up IRAM areas used as temporary memory for the different hardware units depending on the frame 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 424dd5f..8575b71 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -1221,6 +1221,7 @@ static void coda_setup_iram(struct coda_ctx *ctx) int ipacdc_size; int bitram_size; int dbk_size; + int ovl_size; int mb_width; int me_size; int size; @@ -1282,7 +1283,47 @@ static void coda_setup_iram(struct coda_ctx *ctx) size -= ipacdc_size; } - /* OVL disabled for encoder */ + /* 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 { + goto out; + } + + 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 { + goto out; + } + + 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 { + goto out; + } + + ovl_size = round_up(80 * mb_width, 1024); } out: @@ -1309,7 +1350,12 @@ out: if (dev->devtype->product == CODA_7541) { /* TODO - Enabling these causes picture errors on CODA7541 */ - if (ctx->inst_type == CODA_INST_ENCODER) { + if (ctx->inst_type == CODA_INST_DECODER) { + /* fw 1.4.50 */ + iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | + CODA7_USE_IP_ENABLE); + } else { + /* fw 13.4.29 */ iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE | CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_IP_ENABLE | -- cgit v0.10.2 From 477c1cfe8f8050ce716b3d89be17b33a4a80fc9d Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 21 Jun 2013 03:55:33 -0300 Subject: [media] coda: split encoder specific parts out of device_run and irq_handler Add coda_prepare_encode() and coda_finish_encode() functions. They are called from coda_device_run() and coda_irq_handler(), respectively, before and after the hardware picture run. This should make the following decoder support patch easier to read, which will add the coda_prepare/finish_decode() equivalents. 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 8575b71..2c37e3d 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -802,9 +802,8 @@ static void coda_fill_bitstream(struct coda_ctx *ctx) /* * Mem-to-mem operations. */ -static void coda_device_run(void *m2m_priv) +static void coda_prepare_encode(struct coda_ctx *ctx) { - struct coda_ctx *ctx = m2m_priv; struct coda_q_data *q_data_src, *q_data_dst; struct vb2_buffer *src_buf, *dst_buf; struct coda_dev *dev = ctx->dev; @@ -814,8 +813,6 @@ static void coda_device_run(void *m2m_priv) u32 pic_stream_buffer_addr, pic_stream_buffer_size; u32 dst_fourcc; - mutex_lock(&dev->coda_mutex); - src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); @@ -926,6 +923,16 @@ static void coda_device_run(void *m2m_priv) 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); +} + +static void coda_device_run(void *m2m_priv) +{ + struct coda_ctx *ctx = m2m_priv; + struct coda_dev *dev = ctx->dev; + + mutex_lock(&dev->coda_mutex); + + coda_prepare_encode(ctx); if (dev->devtype->product == CODA_7541) { coda_write(dev, CODA7_USE_BIT_ENABLE | CODA7_USE_HOST_BIT_ENABLE | @@ -2034,39 +2041,11 @@ static const struct v4l2_file_operations coda_fops = { .mmap = coda_mmap, }; -static irqreturn_t coda_irq_handler(int irq, void *data) +static void coda_encode_finish(struct coda_ctx *ctx) { struct vb2_buffer *src_buf, *dst_buf; - struct coda_dev *dev = data; + struct coda_dev *dev = ctx->dev; u32 wr_ptr, start_ptr; - 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, - CODA_REG_BIT_INT_CLEAR); - - ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); - if (ctx == NULL) { - v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n"); - mutex_unlock(&dev->coda_mutex); - return IRQ_HANDLED; - } - - if (ctx->aborting) { - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "task has been aborted\n"); - mutex_unlock(&dev->coda_mutex); - return IRQ_HANDLED; - } - - if (coda_isbusy(ctx->dev)) { - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "coda is still busy!!!!\n"); - return IRQ_NONE; - } src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); @@ -2115,6 +2094,41 @@ static irqreturn_t coda_irq_handler(int irq, void *data) dst_buf->v4l2_buf.sequence, (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ? "KEYFRAME" : "PFRAME"); +} + +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, + CODA_REG_BIT_INT_CLEAR); + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (ctx == NULL) { + v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n"); + mutex_unlock(&dev->coda_mutex); + return IRQ_HANDLED; + } + + if (ctx->aborting) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "task has been aborted\n"); + mutex_unlock(&dev->coda_mutex); + return IRQ_HANDLED; + } + + if (coda_isbusy(ctx->dev)) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "coda is still busy!!!!\n"); + return IRQ_NONE; + } + + coda_encode_finish(ctx); mutex_unlock(&dev->coda_mutex); -- cgit v0.10.2 From 918c66fd41268ec34bba0a82f4221ddc18e9501b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 27 Jun 2013 06:59:01 -0300 Subject: [media] coda: add CODA7541 decoding support This patch enables decoding of h.264 and mpeg4 streams on CODA7541. Queued output buffers are immediately copied into the bitstream ringbuffer. A device_run can be scheduled whenever there is either enough compressed bitstream data, or the CODA is in stream end mode. Each successful device_run, data is read from the bitstream ringbuffer and a frame is decoded into a free internal framebuffer. Depending on reordering, a possibly previously decoded frame is marked as display frame, and at the same time the display frame from the previous run is copied out into a capture buffer by the rotator hardware. The dequeued capture buffers are counted to send the EOS signal to userspace with the last frame. When userspace sends the decoder stop command or enqueues an empty output buffer, the stream end flag is set to allow decoding the remaining frames in the bitstream ringbuffer. The enum_fmt/try_fmt functions return fixed capture buffer sizes while the output queue is streaming, to allow better autonegotiation in userspace. A per-context buffer mutex is used to lock the picture run against buffer dequeueing: if a job gets queued, then streamoff dequeues the last buffer, and then device_run is called, bail out. For that the interrupt handler has to be threaded. 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 2c37e3d..73460de 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -47,9 +48,11 @@ #define CODA_PARA_BUF_SIZE (10 * 1024) #define CODA_ISRAM_SIZE (2048 * 2) #define CODADX6_IRAM_SIZE 0xb000 -#define CODA7_IRAM_SIZE 0x14000 /* 81920 bytes */ +#define CODA7_IRAM_SIZE 0x14000 -#define CODA_MAX_FRAMEBUFFERS 2 +#define CODA7_PS_BUF_SIZE 0x28000 + +#define CODA_MAX_FRAMEBUFFERS 8 #define MAX_W 8192 #define MAX_H 8192 @@ -178,12 +181,16 @@ struct coda_iram_info { struct coda_ctx { struct coda_dev *dev; + struct mutex buffer_mutex; struct list_head list; + struct work_struct skip_run; int aborting; + int initialized; int streamon_out; int streamon_cap; u32 isequence; u32 qsequence; + u32 osequence; struct coda_q_data q_data[2]; enum coda_inst_type inst_type; struct coda_codec *codec; @@ -193,12 +200,16 @@ struct coda_ctx { struct v4l2_ctrl_handler ctrls; struct v4l2_fh fh; int gopcounter; + int runcounter; char vpu_header[3][64]; int vpu_header_size[3]; struct kfifo bitstream_fifo; struct mutex bitstream_mutex; struct coda_aux_buf bitstream; + bool prescan_failed; struct coda_aux_buf parabuf; + struct coda_aux_buf psbuf; + struct coda_aux_buf slicebuf; struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS]; struct coda_aux_buf workbuf; int num_internal_frames; @@ -206,6 +217,8 @@ struct coda_ctx { int reg_idx; struct coda_iram_info iram_info; u32 bit_stream_param; + u32 frm_dis_flg; + int display_idx; }; static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff, @@ -257,6 +270,8 @@ static void coda_command_async(struct coda_ctx *ctx, int cmd) /* 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->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); } @@ -331,6 +346,8 @@ static struct coda_codec codadx6_codecs[] = { static struct coda_codec coda7_codecs[] = { CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720), CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720), + CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1080), + CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1080), }; static bool coda_format_is_yuv(u32 fourcc) @@ -399,7 +416,7 @@ static int vidioc_querycap(struct file *file, void *priv, } static int enum_fmt(void *priv, struct v4l2_fmtdesc *f, - enum v4l2_buf_type type) + enum v4l2_buf_type type, int src_fourcc) { struct coda_ctx *ctx = fh_to_ctx(priv); struct coda_codec *codecs = ctx->dev->devtype->codecs; @@ -411,7 +428,8 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f, for (i = 0; i < num_formats; i++) { /* Both uncompressed formats are always supported */ - if (coda_format_is_yuv(formats[i].fourcc)) { + if (coda_format_is_yuv(formats[i].fourcc) && + !coda_format_is_yuv(src_fourcc)) { if (num == f->index) break; ++num; @@ -419,8 +437,10 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f, } /* Compressed formats may be supported, check the codec list */ for (k = 0; k < num_codecs; k++) { + /* if src_fourcc is set, only consider matching codecs */ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - formats[i].fourcc == codecs[k].dst_fourcc) + formats[i].fourcc == codecs[k].dst_fourcc && + (!src_fourcc || src_fourcc == codecs[k].src_fourcc)) break; if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT && formats[i].fourcc == codecs[k].src_fourcc) @@ -447,13 +467,26 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE); + struct coda_ctx *ctx = fh_to_ctx(priv); + struct vb2_queue *src_vq; + 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); + if (vb2_is_streaming(src_vq)) { + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE, + q_data_src->fourcc); + } + + return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0); } static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT); + return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0); } static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) @@ -526,15 +559,45 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct coda_ctx *ctx = fh_to_ctx(priv); - struct coda_codec *codec = NULL; + struct coda_codec *codec; + struct vb2_queue *src_vq; + int ret; + + /* + * 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); + if (vb2_is_streaming(src_vq)) { + struct coda_q_data *q_data_src; - /* Determine codec by the encoded format */ - codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, - f->fmt.pix.pixelformat); + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + codec = coda_find_codec(ctx->dev, q_data_src->fourcc, + f->fmt.pix.pixelformat); + if (!codec) + return -EINVAL; + } else { + /* Otherwise determine codec by encoded format, if possible */ + codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, + f->fmt.pix.pixelformat); + } f->fmt.pix.colorspace = ctx->colorspace; - return vidioc_try_fmt(codec, f); + ret = vidioc_try_fmt(codec, f); + if (ret < 0) + return ret; + + /* 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.height = round_up(f->fmt.pix.height, 16); + f->fmt.pix.bytesperline = f->fmt.pix.width; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height * 3 / 2; + } + + return 0; } static int vidioc_try_fmt_vid_out(struct file *file, void *priv, @@ -644,11 +707,35 @@ static int vidioc_expbuf(struct file *file, void *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) +{ + struct vb2_queue *src_vq; + + src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) && + (buf->sequence == (ctx->qsequence - 1))); +} + static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { struct coda_ctx *ctx = fh_to_ctx(priv); + int ret; + + ret = v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); + /* If this is the last capture buffer, emit an end-of-stream event */ + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + coda_buf_is_end_of_stream(ctx, buf)) { + const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + + v4l2_event_queue_fh(&ctx->fh, &eos_event); + } + + return ret; } static int vidioc_create_bufs(struct file *file, void *priv, @@ -671,8 +758,53 @@ static int vidioc_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 v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); + return ret; +} + +static int vidioc_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + + if (dc->cmd != V4L2_DEC_CMD_STOP) + return -EINVAL; + + if ((dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK) || + (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY)) + return -EINVAL; + + if (dc->stop.pts != 0) + return -EINVAL; + + if (ctx->inst_type != CODA_INST_DECODER) + return -EINVAL; + + /* Set the strem-end flag on this context */ + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + + return 0; +} + +static int vidioc_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + default: + return v4l2_ctrl_subscribe_event(fh, sub); + } } static const struct v4l2_ioctl_ops coda_ioctl_ops = { @@ -698,8 +830,22 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, + + .vidioc_decoder_cmd = vidioc_decoder_cmd, + + .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; +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); @@ -780,6 +926,8 @@ 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; + return true; } @@ -802,6 +950,84 @@ static void coda_fill_bitstream(struct coda_ctx *ctx) /* * Mem-to-mem operations. */ +static int coda_prepare_decode(struct coda_ctx *ctx) +{ + struct vb2_buffer *dst_buf; + struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_dst; + u32 stridey, height; + u32 picture_y, picture_cb, picture_cr; + + dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + if (ctx->params.rot_mode & CODA_ROT_90) { + stridey = q_data_dst->height; + height = q_data_dst->width; + } else { + stridey = q_data_dst->width; + height = q_data_dst->height; + } + + /* Try to copy source buffer contents into the bitstream ringbuffer */ + mutex_lock(&ctx->bitstream_mutex); + coda_fill_bitstream(ctx); + mutex_unlock(&ctx->bitstream_mutex); + + if (coda_get_bitstream_payload(ctx) < 512 && + (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "bitstream payload: %d, skipping\n", + coda_get_bitstream_payload(ctx)); + schedule_work(&ctx->skip_run); + return -EAGAIN; + } + + /* Run coda_start_decoding (again) if not yet initialized */ + if (!ctx->initialized) { + int ret = coda_start_decoding(ctx); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to start decoding\n"); + schedule_work(&ctx->skip_run); + return -EAGAIN; + } else { + ctx->initialized = 1; + } + } + + /* Set rotator output */ + picture_y = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + if (q_data_dst->fourcc == V4L2_PIX_FMT_YVU420) { + /* Switch Cr and Cb for YVU420 format */ + picture_cr = picture_y + stridey * height; + picture_cb = picture_cr + stridey / 2 * height / 2; + } else { + 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); + coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode, + CODA_CMD_DEC_PIC_ROT_MODE); + + switch (dev->devtype->product) { + case CODA_DX6: + /* TBD */ + case CODA_7541: + coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION); + break; + } + + coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM); + + coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START); + coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE); + + return 0; +} + static void coda_prepare_encode(struct coda_ctx *ctx) { struct coda_q_data *q_data_src, *q_data_dst; @@ -819,9 +1045,9 @@ static void coda_prepare_encode(struct coda_ctx *ctx) q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); dst_fourcc = q_data_dst->fourcc; - src_buf->v4l2_buf.sequence = ctx->isequence; - dst_buf->v4l2_buf.sequence = ctx->isequence; - ctx->isequence++; + src_buf->v4l2_buf.sequence = ctx->osequence; + dst_buf->v4l2_buf.sequence = ctx->osequence; + ctx->osequence++; /* * Workaround coda firmware BUG that only marks the first @@ -929,15 +1155,36 @@ static void coda_device_run(void *m2m_priv) { struct coda_ctx *ctx = m2m_priv; struct coda_dev *dev = ctx->dev; + int ret; - mutex_lock(&dev->coda_mutex); + mutex_lock(&ctx->buffer_mutex); - coda_prepare_encode(ctx); + /* + * 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; + } - if (dev->devtype->product == CODA_7541) { - coda_write(dev, CODA7_USE_BIT_ENABLE | CODA7_USE_HOST_BIT_ENABLE | - CODA7_USE_ME_ENABLE | CODA7_USE_HOST_ME_ENABLE, - CODA7_REG_BIT_AXI_SRAM_USE); + mutex_lock(&dev->coda_mutex); + + if (ctx->inst_type == CODA_INST_DECODER) { + ret = coda_prepare_decode(ctx); + if (ret < 0) { + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); + /* job_finish scheduled by prepare_decode */ + return; + } + } else { + coda_prepare_encode(ctx); } if (dev->devtype->product != CODA_DX6) @@ -947,6 +1194,8 @@ static void coda_device_run(void *m2m_priv) /* 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); } @@ -972,6 +1221,16 @@ static int coda_job_ready(void *m2m_priv) return 0; } + if (ctx->prescan_failed || + ((ctx->inst_type == CODA_INST_DECODER) && + (coda_get_bitstream_payload(ctx) < 512) && + !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "%d: not ready: not enough bitstream data.\n", + ctx->idx); + return 0; + } + if (ctx->aborting) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "not ready: aborting\n"); @@ -1087,7 +1346,29 @@ 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); - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + struct coda_q_data *q_data; + + q_data = get_q_data(ctx, vb->vb2_queue->type); + + /* + * In the decoder case, immediately try to copy the buffer into the + * bitstream ringbuffer and mark it as ready to be dequeued. + */ + if (q_data->fourcc == V4L2_PIX_FMT_H264 && + vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + /* + * For backwards compatiblity, queuing an empty buffer marks + * the stream end + */ + if (vb2_get_plane_payload(vb, 0) == 0) + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + mutex_lock(&ctx->bitstream_mutex); + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + coda_fill_bitstream(ctx); + mutex_unlock(&ctx->bitstream_mutex); + } else { + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + } } static void coda_wait_prepare(struct vb2_queue *q) @@ -1375,6 +1656,8 @@ static void coda_free_context_buffers(struct coda_ctx *ctx) { struct coda_dev *dev = ctx->dev; + coda_free_aux_buf(dev, &ctx->slicebuf); + coda_free_aux_buf(dev, &ctx->psbuf); if (dev->devtype->product != CODA_DX6) coda_free_aux_buf(dev, &ctx->workbuf); } @@ -1394,12 +1677,40 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, return 0; } + if (ctx->psbuf.vaddr) { + v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n"); + return -EBUSY; + } + if (ctx->slicebuf.vaddr) { + v4l2_err(&dev->v4l2_dev, "slicebuf still allocated\n"); + return -EBUSY; + } if (ctx->workbuf.vaddr) { v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n"); ret = -EBUSY; return -ENOMEM; } + if (q_data->fourcc == V4L2_PIX_FMT_H264) { + /* 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); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte slice buffer", + ctx->slicebuf.size); + return ret; + } + } + + if (dev->devtype->product == CODA_7541) { + ret = coda_alloc_context_buf(ctx, &ctx->psbuf, CODA7_PS_BUF_SIZE); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to allocate psmem buffer"); + goto err; + } + } + ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte context buffer", @@ -1414,6 +1725,148 @@ err: return ret; } +static int coda_start_decoding(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src, *q_data_dst; + u32 bitstream_buf, bitstream_size; + struct coda_dev *dev = ctx->dev; + int width, height; + u32 src_fourcc; + u32 val; + int ret; + + /* Start decoding */ + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + bitstream_buf = ctx->bitstream.paddr; + bitstream_size = ctx->bitstream.size; + src_fourcc = q_data_src->fourcc; + + coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); + + /* Update coda bitstream read and write pointers from kfifo */ + coda_kfifo_sync_to_device_full(ctx); + + ctx->display_idx = -1; + ctx->frm_dis_flg = 0; + coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + + coda_write(dev, CODA_BIT_DEC_SEQ_INIT_ESCAPE, + CODA_REG_BIT_BIT_STREAM_PARAM); + + 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) + 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 (src_fourcc == V4L2_PIX_FMT_H264) { + if (dev->devtype->product == CODA_7541) { + coda_write(dev, ctx->psbuf.paddr, + CODA_CMD_DEC_SEQ_PS_BB_START); + coda_write(dev, (CODA7_PS_BUF_SIZE / 1024), + CODA_CMD_DEC_SEQ_PS_BB_SIZE); + } + } + + if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) { + v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); + coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); + return -ETIMEDOUT; + } + + /* Update kfifo out pointer from coda bitstream read pointer */ + coda_kfifo_sync_from_device(ctx); + + coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); + + if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) { + v4l2_err(&dev->v4l2_dev, + "CODA_COMMAND_SEQ_INIT failed, error code = %d\n", + coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON)); + return -EAGAIN; + } + + val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE); + if (dev->devtype->product == CODA_DX6) { + width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK; + height = val & CODADX6_PICHEIGHT_MASK; + } else { + width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK; + height = val & CODA7_PICHEIGHT_MASK; + } + + if (width > q_data_dst->width || height > q_data_dst->height) { + v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n", + width, height, q_data_dst->width, q_data_dst->height); + return -EINVAL; + } + + width = round_up(width, 16); + height = round_up(height, 16); + + 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; + if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) { + v4l2_err(&dev->v4l2_dev, + "not enough framebuffers to decode (%d < %d)\n", + CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames); + return -EINVAL; + } + + ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc); + if (ret < 0) + return ret; + + /* Tell the decoder how many frame buffers we allocated. */ + coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); + coda_write(dev, width, CODA_CMD_SET_FRAME_BUF_STRIDE); + + if (dev->devtype->product != CODA_DX6) { + /* Set secondary AXI IRAM */ + coda_setup_iram(ctx); + + coda_write(dev, ctx->iram_info.buf_bit_use, + CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); + coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use, + CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_y_use, + CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); + coda_write(dev, ctx->iram_info.buf_dbk_c_use, + CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); + coda_write(dev, ctx->iram_info.buf_ovl_use, + CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); + } + + if (src_fourcc == V4L2_PIX_FMT_H264) { + coda_write(dev, ctx->slicebuf.paddr, + CODA_CMD_SET_FRAME_SLICE_BB_START); + coda_write(dev, ctx->slicebuf.size / 1024, + CODA_CMD_SET_FRAME_SLICE_BB_SIZE); + } + + if (dev->devtype->product == CODA_7541) { + 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); + } + + if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) { + v4l2_err(&ctx->dev->v4l2_dev, + "CODA_COMMAND_SET_FRAME_BUF timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf, int header_code, u8 *header, int *size) { @@ -1448,26 +1901,36 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) u32 value; int ret = 0; - if (count < 1) - return -EINVAL; + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (q_data_src->fourcc == V4L2_PIX_FMT_H264) { + if (coda_get_bitstream_payload(ctx) < 512) + return -EINVAL; + } else { + if (count < 1) + return -EINVAL; + } - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ctx->streamon_out = 1; - else - ctx->streamon_cap = 1; - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (ctx->streamon_out) { if (coda_format_is_yuv(q_data_src->fourcc)) ctx->inst_type = CODA_INST_ENCODER; else ctx->inst_type = CODA_INST_DECODER; + } else { + if (count < 1) + return -EINVAL; + + ctx->streamon_cap = 1; } /* Don't start the coda unless both queues are on */ if (!(ctx->streamon_out & ctx->streamon_cap)) return 0; + /* Allow device_run with no buffers queued and after streamoff */ + 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); @@ -1487,6 +1950,20 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) if (ret < 0) return ret; + if (ctx->inst_type == CODA_INST_DECODER) { + mutex_lock(&dev->coda_mutex); + ret = coda_start_decoding(ctx); + mutex_unlock(&dev->coda_mutex); + if (ret == -EAGAIN) { + return 0; + } else if (ret < 0) { + return ret; + } else { + ctx->initialized = 1; + return 0; + } + } + if (!coda_is_initialized(dev)) { v4l2_err(v4l2_dev, "coda is not initialized.\n"); return -EFAULT; @@ -1628,6 +2105,9 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) 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), + 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); @@ -1719,32 +2199,26 @@ static int coda_stop_streaming(struct vb2_queue *q) struct coda_dev *dev = ctx->dev; if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s: output\n", __func__); ctx->streamon_out = 0; + + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + + ctx->isequence = 0; } else { - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s: capture\n", __func__); ctx->streamon_cap = 0; - } - - /* Don't stop the coda unless both queues are off */ - if (ctx->streamon_out || ctx->streamon_cap) - return 0; - cancel_delayed_work(&dev->timeout); - - 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"); - return -ETIMEDOUT; + ctx->osequence = 0; } - mutex_unlock(&dev->coda_mutex); - coda_free_framebuffers(ctx); + if (!ctx->streamon_out && !ctx->streamon_cap) { + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + ctx->runcounter = 0; + } return 0; } @@ -1904,7 +2378,7 @@ static int coda_open(struct file *file) { struct coda_dev *dev = video_drvdata(file); struct coda_ctx *ctx = NULL; - int ret = 0; + int ret; int idx; idx = coda_next_free_instance(dev); @@ -1916,6 +2390,7 @@ static int coda_open(struct file *file) if (!ctx) return -ENOMEM; + INIT_WORK(&ctx->skip_run, coda_skip_run); v4l2_fh_init(&ctx->fh, video_devdata(file)); file->private_data = &ctx->fh; v4l2_fh_add(&ctx->fh); @@ -1963,6 +2438,7 @@ static int coda_open(struct file *file) kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, ctx->bitstream.size); mutex_init(&ctx->bitstream_mutex); + mutex_init(&ctx->buffer_mutex); coda_lock(ctx); list_add(&ctx->list, &dev->instances); @@ -1991,6 +2467,23 @@ static int coda_release(struct file *file) v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n", ctx); + /* If this instance is running, call .job_abort and wait for it to end */ + 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; + } + mutex_unlock(&dev->coda_mutex); + + coda_free_framebuffers(ctx); + coda_lock(ctx); list_del(&ctx->list); coda_unlock(ctx); @@ -2041,7 +2534,159 @@ static const struct v4l2_file_operations coda_fops = { .mmap = coda_mmap, }; -static void coda_encode_finish(struct coda_ctx *ctx) +static void coda_finish_decode(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_src; + struct coda_q_data *q_data_dst; + struct vb2_buffer *dst_buf; + int width, height; + int decoded_idx; + int display_idx; + u32 src_fourcc; + int success; + u32 val; + + dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + + /* Update kfifo out pointer from coda bitstream read pointer */ + coda_kfifo_sync_from_device(ctx); + + /* + * in stream-end mode, the read pointer can overshoot the write pointer + * by up to 512 bytes + */ + if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) { + if (coda_get_bitstream_payload(ctx) >= 0x100000 - 512) + kfifo_init(&ctx->bitstream_fifo, + ctx->bitstream.vaddr, ctx->bitstream.size); + } + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_fourcc = q_data_src->fourcc; + + val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS); + if (val != 1) + pr_err("DEC_PIC_SUCCESS = %d\n", val); + + success = val & 0x1; + if (!success) + v4l2_err(&dev->v4l2_dev, "decode failed\n"); + + if (src_fourcc == V4L2_PIX_FMT_H264) { + if (val & (1 << 3)) + v4l2_err(&dev->v4l2_dev, + "insufficient PS buffer space (%d bytes)\n", + ctx->psbuf.size); + if (val & (1 << 2)) + v4l2_err(&dev->v4l2_dev, + "insufficient slice buffer space (%d bytes)\n", + ctx->slicebuf.size); + } + + val = coda_read(dev, CODA_RET_DEC_PIC_SIZE); + width = (val >> 16) & 0xffff; + height = val & 0xffff; + + 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, + "errors in %d macroblocks\n", val); + + if (dev->devtype->product == CODA_7541) { + val = coda_read(dev, CODA_RET_DEC_PIC_OPTION); + if (val == 0) { + /* not enough bitstream data */ + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "prescan failed: %d\n", val); + ctx->prescan_failed = true; + return; + } + } + + ctx->frm_dis_flg = coda_read(dev, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + + /* + * The previous display frame was copied out by the rotator, + * now it can be overwritten again + */ + if (ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) { + ctx->frm_dis_flg &= ~(1 << ctx->display_idx); + coda_write(dev, ctx->frm_dis_flg, + CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx)); + } + + /* + * The index of the last decoded frame, not necessarily in + * display order, and the index of the next display frame. + * The latter could have been decoded in a previous run. + */ + decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX); + display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX); + + if (decoded_idx == -1) { + /* no frame was decoded, but we might have a display frame */ + if (display_idx < 0 && ctx->display_idx < 0) + ctx->prescan_failed = 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) { + v4l2_err(&dev->v4l2_dev, + "decoded frame index out of range: %d\n", decoded_idx); + } + + if (display_idx == -1) { + /* + * no more frames to be decoded, but there could still + * be rotator output to dequeue + */ + ctx->prescan_failed = true; + } else if (display_idx == -3) { + /* possibly prescan failure */ + } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) { + v4l2_err(&dev->v4l2_dev, + "presentation frame index out of range: %d\n", + display_idx); + } + + /* 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_buf.sequence = ctx->osequence++; + + 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_dbg(1, coda_debug, &dev->v4l2_dev, + "job finished: decoding frame (%d) (%s)\n", + dst_buf->v4l2_buf.sequence, + (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ? + "KEYFRAME" : "PFRAME"); + } else { + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, + "job finished: no frame decoded\n"); + } + + /* The rotator will copy the current display frame next time */ + ctx->display_idx = display_idx; +} + +static void coda_finish_encode(struct coda_ctx *ctx) { struct vb2_buffer *src_buf, *dst_buf; struct coda_dev *dev = ctx->dev; @@ -2118,8 +2763,7 @@ 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"); - mutex_unlock(&dev->coda_mutex); - return IRQ_HANDLED; + goto out; } if (coda_isbusy(ctx->dev)) { @@ -2128,9 +2772,29 @@ static irqreturn_t coda_irq_handler(int irq, void *data) return IRQ_NONE; } - coda_encode_finish(ctx); + 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); @@ -2147,6 +2811,8 @@ static void coda_timeout(struct work_struct *work) 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); } @@ -2227,6 +2893,7 @@ static int coda_hw_init(struct coda_dev *dev) if (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); } else { coda_write(dev, dev->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); @@ -2471,8 +3138,8 @@ static int coda_probe(struct platform_device *pdev) return -ENOENT; } - if (devm_request_irq(&pdev->dev, irq, coda_irq_handler, - 0, CODA_NAME, dev) < 0) { + if (devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler, + IRQF_ONESHOT, CODA_NAME, dev) < 0) { dev_err(&pdev->dev, "failed to request irq\n"); return -ENOENT; } @@ -2530,10 +3197,14 @@ static int coda_probe(struct platform_device *pdev) } } - if (dev->devtype->product == CODA_DX6) + switch (dev->devtype->product) { + case CODA_DX6: dev->iram_size = CODADX6_IRAM_SIZE; - else + break; + case CODA_7541: dev->iram_size = CODA7_IRAM_SIZE; + break; + } dev->iram_vaddr = gen_pool_alloc(dev->iram_pool, dev->iram_size); if (!dev->iram_vaddr) { dev_err(&pdev->dev, "unable to alloc iram\n"); diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda.h index 140eea5..4e32e2e 100644 --- a/drivers/media/platform/coda.h +++ b/drivers/media/platform/coda.h @@ -49,6 +49,7 @@ #define CODA_REG_BIT_TEMP_BUF_ADDR 0x118 #define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x)) #define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x)) +#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 CODA7_USE_HOST_ME_ENABLE (1 << 11) @@ -95,6 +96,7 @@ #define CODA_MODE_INVALID 0xffff #define CODA_REG_BIT_INT_ENABLE 0x170 #define CODA_INT_INTERRUPT_ENABLE (1 << 3) +#define CODA_REG_BIT_INT_REASON 0x174 #define CODA7_REG_BIT_RUN_AUX_STD 0x178 #define CODA_MP4_AUX_MPEG4 0 #define CODA_MP4_AUX_DIVX3 1 @@ -111,15 +113,89 @@ * issued. */ +/* Decoder Sequence Initialization */ +#define CODA_CMD_DEC_SEQ_BB_START 0x180 +#define CODA_CMD_DEC_SEQ_BB_SIZE 0x184 +#define CODA_CMD_DEC_SEQ_OPTION 0x188 +#define CODA_REORDER_ENABLE (1 << 1) +#define CODADX6_QP_REPORT (1 << 0) +#define CODA7_MP4_DEBLK_ENABLE (1 << 0) +#define CODA_CMD_DEC_SEQ_SRC_SIZE 0x18c +#define CODA_CMD_DEC_SEQ_START_BYTE 0x190 +#define CODA_CMD_DEC_SEQ_PS_BB_START 0x194 +#define CODA_CMD_DEC_SEQ_PS_BB_SIZE 0x198 +#define CODA_CMD_DEC_SEQ_MP4_ASP_CLASS 0x19c +#define CODA_CMD_DEC_SEQ_X264_MV_EN 0x19c +#define CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE 0x1a0 + +#define CODA7_RET_DEC_SEQ_ASPECT 0x1b0 +#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 +#define CODA_RET_DEC_SEQ_SRC_F_RATE 0x1c8 +#define CODA9_RET_DEC_SEQ_ASPECT 0x1c8 +#define CODA_RET_DEC_SEQ_FRAME_NEED 0x1cc +#define CODA_RET_DEC_SEQ_FRAME_DELAY 0x1d0 +#define CODA_RET_DEC_SEQ_INFO 0x1d4 +#define CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT 0x1d8 +#define CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM 0x1dc +#define CODA_RET_DEC_SEQ_NEXT_FRAME_NUM 0x1e0 +#define CODA_RET_DEC_SEQ_ERR_REASON 0x1e0 +#define CODA_RET_DEC_SEQ_FRATE_NR 0x1e4 +#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 + +/* Decoder Picture Run */ +#define CODA_CMD_DEC_PIC_ROT_MODE 0x180 +#define CODA_CMD_DEC_PIC_ROT_ADDR_Y 0x184 +#define CODA_CMD_DEC_PIC_ROT_ADDR_CB 0x188 +#define CODA_CMD_DEC_PIC_ROT_ADDR_CR 0x18c +#define CODA_CMD_DEC_PIC_ROT_STRIDE 0x190 + +#define CODA_CMD_DEC_PIC_OPTION 0x194 +#define CODA_PRE_SCAN_EN (1 << 0) +#define CODA_PRE_SCAN_MODE_DECODE (0 << 1) +#define CODA_PRE_SCAN_MODE_RETURN (1 << 1) +#define CODA_IFRAME_SEARCH_EN (1 << 2) +#define CODA_SKIP_FRAME_MODE (0x3 << 3) +#define CODA_CMD_DEC_PIC_SKIP_NUM 0x198 +#define CODA_CMD_DEC_PIC_CHUNK_SIZE 0x19c +#define CODA_CMD_DEC_PIC_BB_START 0x1a0 +#define CODA_CMD_DEC_PIC_START_BYTE 0x1a4 +#define CODA_RET_DEC_PIC_SIZE 0x1bc +#define CODA_RET_DEC_PIC_FRAME_NUM 0x1c0 +#define CODA_RET_DEC_PIC_FRAME_IDX 0x1c4 +#define CODA_RET_DEC_PIC_ERR_MB 0x1c8 +#define CODA_RET_DEC_PIC_TYPE 0x1cc +#define CODA_PIC_TYPE_MASK 0x7 +#define CODA_PIC_TYPE_MASK_VC1 0x3f +#define CODA9_PIC_TYPE_FIRST_MASK (0x7 << 3) +#define CODA9_PIC_TYPE_IDR_MASK (0x3 << 6) +#define CODA7_PIC_TYPE_H264_NPF_MASK (0x3 << 16) +#define CODA7_PIC_TYPE_INTERLACED (1 << 18) +#define CODA_RET_DEC_PIC_POST 0x1d0 +#define CODA_RET_DEC_PIC_MVC_REPORT 0x1d0 +#define CODA_RET_DEC_PIC_OPTION 0x1d4 +#define CODA_RET_DEC_PIC_SUCCESS 0x1d8 +#define CODA_RET_DEC_PIC_CUR_IDX 0x1dc +#define CODA_RET_DEC_PIC_CROP_LEFT_RIGHT 0x1e0 +#define CODA_RET_DEC_PIC_CROP_TOP_BOTTOM 0x1e4 +#define CODA_RET_DEC_PIC_FRAME_NEED 0x1ec + /* 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 CODA7_OPTION_GAMMA_OFFSET 8 +#define CODA7_OPTION_RCQPMAX_OFFSET 7 #define CODADX6_OPTION_GAMMA_OFFSET 7 +#define CODA7_OPTION_RCQPMIN_OFFSET 6 #define CODA_OPTION_LIMITQP_OFFSET 6 #define CODA_OPTION_RCINTRAQP_OFFSET 5 #define CODA_OPTION_FMO_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 @@ -188,8 +264,10 @@ #define CODA_FMOPARAM_TYPE_MASK 1 #define CODA_FMOPARAM_SLICENUM_OFFSET 0 #define CODA_FMOPARAM_SLICENUM_MASK 0x0f +#define CODADX6_CMD_ENC_SEQ_INTRA_QP 0x1bc #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_QPMAX_OFFSET 0 #define CODA_QPMAX_MASK 0x3f @@ -216,18 +294,24 @@ #define CODA_CMD_ENC_PIC_OPTION 0x194 #define CODA_CMD_ENC_PIC_BB_START 0x198 #define CODA_CMD_ENC_PIC_BB_SIZE 0x19c +#define CODA_RET_ENC_FRAME_NUM 0x1c0 #define CODA_RET_ENC_PIC_TYPE 0x1c4 +#define CODA_RET_ENC_PIC_FRAME_IDX 0x1c8 #define CODA_RET_ENC_PIC_SLICE_NUM 0x1cc #define CODA_RET_ENC_PIC_FLAG 0x1d0 +#define CODA_RET_ENC_PIC_SUCCESS 0x1d8 /* Set Frame Buffer */ #define CODA_CMD_SET_FRAME_BUF_NUM 0x180 #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 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 CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8 /* Encoder Header */ -- cgit v0.10.2 From 8b2ff3204909687be26f20d63dcddc8e3d7a6c14 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 22 Jul 2013 04:22:57 -0300 Subject: [media] media: rc: Add rc_open/close and use count to rc_dev This patch adds user count to rc_dev structure, the reason to add this new member is to allow other code like lirc to open rc device directly. In the existing code, rc device is only opened by input subsystem which works ok if we have any input drivers to match. But in case like lirc where there will be no input driver, rc device will be never opened. Having this user count variable will be usefull to allow rc device to be opened from code other than rc-main. This patch also adds rc_open and rc_close functions for other drivers like lirc to open and close rc devices. This functions safely increment and decrement the user count. Other driver wanting to open rc device should call rc_open and rc_close, rather than directly modifying the rc_dev structure. Signed-off-by: Srinivas Kandagatla Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 1cf382a..1dedebd 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -699,19 +699,50 @@ void rc_keydown_notimeout(struct rc_dev *dev, int scancode, u8 toggle) } EXPORT_SYMBOL_GPL(rc_keydown_notimeout); +int rc_open(struct rc_dev *rdev) +{ + int rval = 0; + + if (!rdev) + return -EINVAL; + + mutex_lock(&rdev->lock); + if (!rdev->users++) + rval = rdev->open(rdev); + + if (rval) + rdev->users--; + + mutex_unlock(&rdev->lock); + + return rval; +} +EXPORT_SYMBOL_GPL(rc_open); + static int ir_open(struct input_dev *idev) { struct rc_dev *rdev = input_get_drvdata(idev); - return rdev->open(rdev); + return rc_open(rdev); +} + +void rc_close(struct rc_dev *rdev) +{ + if (rdev) { + mutex_lock(&rdev->lock); + + if (!--rdev->users) + rdev->close(rdev); + + mutex_unlock(&rdev->lock); + } } +EXPORT_SYMBOL_GPL(rc_close); static void ir_close(struct input_dev *idev) { struct rc_dev *rdev = input_get_drvdata(idev); - - if (rdev) - rdev->close(rdev); + rc_close(rdev); } /* class for /sys/class/rc */ @@ -1076,7 +1107,14 @@ int rc_register_device(struct rc_dev *dev) memcpy(&dev->input_dev->id, &dev->input_id, sizeof(dev->input_id)); dev->input_dev->phys = dev->input_phys; dev->input_dev->name = dev->input_name; + + /* input_register_device can call ir_open, so unlock mutex here */ + mutex_unlock(&dev->lock); + rc = input_register_device(dev->input_dev); + + mutex_lock(&dev->lock); + if (rc) goto out_table; diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 06a75de..2f6f1f7 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -101,6 +101,7 @@ struct rc_dev { bool idle; u64 allowed_protos; u64 enabled_protocols; + u32 users; u32 scanmask; void *priv; spinlock_t keylock; @@ -142,6 +143,9 @@ void rc_free_device(struct rc_dev *dev); int rc_register_device(struct rc_dev *dev); void rc_unregister_device(struct rc_dev *dev); +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); -- cgit v0.10.2 From ca7a722db1c90dfe0dba165ecef01d6ac8cfee0d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 22 Jul 2013 04:23:07 -0300 Subject: [media] media: lirc: Allow lirc dev to talk to rc device The use case is simple, if any rc device has allowed protocols = RC_TYPE_LIRC and map_name = RC_MAP_LIRC set, the driver open will be never called. The reason for this is, all of the key maps except lirc have some KEYS in there map, so during rc_register_device process these keys are matched against the input drivers and open is performed, so for the case of RC_MAP_EMPTY, a vt/keyboard is matched and the driver open is performed. In case of lirc, there is no match and result is that there is no open performed, however the lirc-dev will go ahead and create a /dev/lirc0 node. Now when lircd/mode2 opens this device, no data is available because the driver was never opened. Other case pointed by Sean Young, As rc device gets opened via the input interface. If the input device is never opened (e.g. embedded with no console) then the rc open is never called and lirc will not work either. So that's another case. lirc_dev seems to have no link with actual rc device w.r.t open/close. This patch adds rc_dev pointer to lirc_driver structure for cases like this, so that it can do the open/close of the real driver in accordance to lircd/mode2 open/close. Without this patch its impossible to open a rc device which has RC_TYPE_LIRC ad RC_MAP_LIRC set. Signed-off-by: Srinivas Kandagatla Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index e5be920..ed2c8a1 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -384,6 +384,7 @@ static int ir_lirc_register(struct rc_dev *dev) drv->code_length = sizeof(struct ir_raw_event) * 8; drv->fops = &lirc_fops; drv->dev = &dev->dev; + drv->rdev = dev; drv->owner = THIS_MODULE; drv->minor = lirc_register_driver(drv); diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 8dc057b..dc5cbff 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -467,6 +468,12 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) goto error; } + if (ir->d.rdev) { + retval = rc_open(ir->d.rdev); + if (retval) + goto error; + } + cdev = ir->cdev; if (try_module_get(cdev->owner)) { ir->open++; @@ -511,6 +518,9 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) WARN_ON(mutex_lock_killable(&lirc_dev_lock)); + if (ir->d.rdev) + rc_close(ir->d.rdev); + ir->open--; if (ir->attached) { ir->d.set_use_dec(ir->d.data); diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 168dd0b..78f0637 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -139,6 +139,7 @@ struct lirc_driver { struct lirc_buffer *rbuf; int (*set_use_inc) (void *data); void (*set_use_dec) (void *data); + struct rc_dev *rdev; const struct file_operations *fops; struct device *dev; struct module *owner; -- cgit v0.10.2 From 00865fe61e2e0c7641b8a96d871003d6287ec7bc Mon Sep 17 00:00:00 2001 From: Dean Anderson Date: Tue, 23 Jul 2013 18:06:41 -0300 Subject: [media] S2255: Removal of unnecessary videobuf_queue_is_busy Removes unnecessary query of buffer state. The code already checks if stream is active or not. Signed-off-by: Dean Anderson Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index ab97e7d..6bc9b8e 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -1,7 +1,7 @@ /* * s2255drv.c - a driver for the Sensoray 2255 USB video capture device * - * Copyright (C) 2007-2010 by Sensoray Company Inc. + * Copyright (C) 2007-2013 by Sensoray Company Inc. * Dean Anderson * * Some video buffer code based on vivi driver: @@ -52,7 +52,7 @@ #include #include -#define S2255_VERSION "1.22.1" +#define S2255_VERSION "1.23.1" #define FIRMWARE_FILE_NAME "f2255usb.bin" /* default JPEG quality */ @@ -1303,11 +1303,6 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i) int ret = 0; mutex_lock(&q->vb_lock); - if (videobuf_queue_is_busy(q)) { - dprintk(1, "queue busy\n"); - ret = -EBUSY; - goto out_s_std; - } if (res_locked(fh)) { dprintk(1, "can't change standard after started\n"); ret = -EBUSY; -- cgit v0.10.2 From 5bc08e1921e46101457d3be09835697490177fdd Mon Sep 17 00:00:00 2001 From: Luis Alves Date: Wed, 24 Jul 2013 09:06:01 -0300 Subject: [media] cx23885[v4]: Fix interrupt storm when enabling IR receiver Apparently the Flatiron genereates an interrupt after the built-in self test for each of its left and right channels has completed. Apparently Conexant wire-OR'ed the Flatiron's interrupt output with the interrupt output of the CX23885 A/V core. Those interrupts need to be handled, otherwise, they generate an interrrupt request storm. So: - Add flatiron readreg and writereg functions prototypes on a new header file; - Modify the av interrupt handler to cleanup flatiron IRQs if no other interrupt handling happens. Signed-off-by: Luis Alves Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/cx23885-av.c b/drivers/media/pci/cx23885/cx23885-av.c index e958a01..c443b7a 100644 --- a/drivers/media/pci/cx23885/cx23885-av.c +++ b/drivers/media/pci/cx23885/cx23885-av.c @@ -23,6 +23,7 @@ #include "cx23885.h" #include "cx23885-av.h" +#include "cx23885-video.h" void cx23885_av_work_handler(struct work_struct *work) { @@ -32,5 +33,17 @@ void cx23885_av_work_handler(struct work_struct *work) v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine, PCI_MSK_AV_CORE, &handled); + + /* Getting here with the interrupt not handled + then probbaly flatiron does have pending interrupts. + */ + if (!handled) { + /* clear left and right adc channel interrupt request flag */ + cx23885_flatiron_write(dev, 0x1f, + cx23885_flatiron_read(dev, 0x1f) | 0x80); + cx23885_flatiron_write(dev, 0x23, + cx23885_flatiron_read(dev, 0x23) | 0x80); + } + cx23885_irq_enable(dev, PCI_MSK_AV_CORE); } diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index e33d1a7..f4e7cef 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -417,7 +417,7 @@ static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh, mutex_unlock(&dev->lock); } -static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data) +int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data) { /* 8 bit registers, 8 bit values */ u8 buf[] = { reg, data }; @@ -428,7 +428,7 @@ static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data) return i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg, 1); } -static u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg) +u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg) { /* 8 bit registers, 8 bit values */ int ret; diff --git a/drivers/media/pci/cx23885/cx23885-video.h b/drivers/media/pci/cx23885/cx23885-video.h new file mode 100644 index 0000000..c961a2b --- /dev/null +++ b/drivers/media/pci/cx23885/cx23885-video.h @@ -0,0 +1,26 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Copyright (C) 2010 Andy Walls + * + * 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. + */ + +#ifndef _CX23885_VIDEO_H_ +#define _CX23885_VIDEO_H_ +int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data); +u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg); +#endif -- cgit v0.10.2 From 9f1595245574a2dc1fb375df665e4d9fe336a9c4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 1 Aug 2013 14:30:30 -0300 Subject: [media] cx23885-video: fix two warnings drivers/media/pci/cx23885/cx23885-video.c:420:5: warning: no previous prototype for 'cx23885_flatiron_write' [-Wmissing-prototypes] int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data) ^ drivers/media/pci/cx23885/cx23885-video.c:431:4: warning: no previous prototype for 'cx23885_flatiron_read' [-Wmissing-prototypes] u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg) Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index f4e7cef..1616868 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -32,6 +32,7 @@ #include #include "cx23885.h" +#include "cx23885-video.h" #include #include #include "cx23885-ioctl.h" -- cgit v0.10.2 From dfb9f94e8e5e7f73c8e2bcb7d4fb1de57e7c333d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 27 Jul 2013 15:29:36 -0300 Subject: [media] stk1160: Build as a module if SND is m and audio support is selected As reported by Randy Dunlap: When CONFIG_SND=m and CONFIG_SND_AC97_CODEC=m and CONFIG_VIDEO_STK1160=y CONFIG_VIDEO_STK1160_AC97=y drivers/built-in.o: In function `stk1160_ac97_register': (.text+0x122706): undefined reference to `snd_card_create' drivers/built-in.o: In function `stk1160_ac97_register': (.text+0x1227b2): undefined reference to `snd_ac97_bus' drivers/built-in.o: In function `stk1160_ac97_register': (.text+0x1227cd): undefined reference to `snd_card_free' drivers/built-in.o: In function `stk1160_ac97_register': (.text+0x12281b): undefined reference to `snd_ac97_mixer' drivers/built-in.o: In function `stk1160_ac97_register': (.text+0x122832): undefined reference to `snd_card_register' drivers/built-in.o: In function `stk1160_ac97_unregister': (.text+0x12285e): undefined reference to `snd_card_free' Reported-by: Randy Dunlap Signed-off-by: Mauro Carvalho Chehab Acked-by: Randy Dunlap Acked-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/stk1160/Kconfig b/drivers/media/usb/stk1160/Kconfig index 1c3a1ec..95584c1 100644 --- a/drivers/media/usb/stk1160/Kconfig +++ b/drivers/media/usb/stk1160/Kconfig @@ -1,8 +1,6 @@ -config VIDEO_STK1160 +config VIDEO_STK1160_COMMON tristate "STK1160 USB video capture support" depends on VIDEO_DEV && I2C - select VIDEOBUF2_VMALLOC - select VIDEO_SAA711X ---help--- This is a video4linux driver for STK1160 based video capture devices. @@ -12,9 +10,15 @@ config VIDEO_STK1160 config VIDEO_STK1160_AC97 bool "STK1160 AC97 codec support" - depends on VIDEO_STK1160 && SND - select SND_AC97_CODEC + depends on VIDEO_STK1160_COMMON && SND ---help--- Enables AC97 codec support for stk1160 driver. -. + +config VIDEO_STK1160 + tristate + depends on (!VIDEO_STK1160_AC97 || (SND='n') || SND) && VIDEO_STK1160_COMMON + default y + select VIDEOBUF2_VMALLOC + select VIDEO_SAA711X + select SND_AC97_CODEC if SND -- cgit v0.10.2 From 3dc7cc24db5986ed5c94c79201f35e3d4610384c Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Tue, 9 Jul 2013 01:24:35 -0300 Subject: [media] s5p-mfc: Update v6 encoder buffer sizes The patch updates few encoder buffer sizes for MFC v6.5 as per the udpdated user manual. The same buffer sizes holds good for v7 firmware also. Signed-off-by: Kiran AVND Signed-off-by: Arun Kumar K Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h index 363a97c..2398cdf 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h +++ b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h @@ -374,9 +374,9 @@ #define S5P_FIMV_NUM_PIXELS_IN_MB_COL_V6 16 /* Buffer size requirements defined by hardware */ -#define S5P_FIMV_TMV_BUFFER_SIZE_V6(w, h) (((w) + 1) * ((h) + 1) * 8) +#define S5P_FIMV_TMV_BUFFER_SIZE_V6(w, h) (((w) + 1) * ((h) + 3) * 8) #define S5P_FIMV_ME_BUFFER_SIZE_V6(imw, imh, mbw, mbh) \ - ((DIV_ROUND_UP(imw, 64) * DIV_ROUND_UP(imh, 64) * 256) + \ + (((((imw + 127) / 64) * 16) * DIV_ROUND_UP(imh, 64) * 256) + \ (DIV_ROUND_UP((mbw) * (mbh), 32) * 16)) #define S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(w, h) (((w) * 192) + 64) #define S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(w, h) \ -- cgit v0.10.2 From 722b979e555d002ca97c9254a91ff3bc5e83763c Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Tue, 9 Jul 2013 01:24:36 -0300 Subject: [media] s5p-mfc: Rename IS_MFCV6 macro The MFC v6 specific code holds good for MFC v7 also as the v7 version is a superset of v6 and the HW interface remains more or less similar. This patch renames the macro IS_MFCV6() to IS_MFCV6_PLUS() so that it can be used for v7 also. Signed-off-by: Arun Kumar K Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c index f0a41c9..242c033 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c @@ -20,7 +20,7 @@ static struct s5p_mfc_hw_cmds *s5p_mfc_cmds; void s5p_mfc_init_hw_cmds(struct s5p_mfc_dev *dev) { - if (IS_MFCV6(dev)) + if (IS_MFCV6_PLUS(dev)) s5p_mfc_cmds = s5p_mfc_init_hw_cmds_v6(); else s5p_mfc_cmds = s5p_mfc_init_hw_cmds_v5(); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index ef4074c..d47016d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -683,6 +683,6 @@ void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx); #define HAS_PORTNUM(dev) (dev ? (dev->variant ? \ (dev->variant->port_num ? 1 : 0) : 0) : 0) #define IS_TWOPORT(dev) (dev->variant->port_num == 2 ? 1 : 0) -#define IS_MFCV6(dev) (dev->variant->version >= 0x60 ? 1 : 0) +#define IS_MFCV6_PLUS(dev) (dev->variant->version >= 0x60 ? 1 : 0) #endif /* S5P_MFC_COMMON_H_ */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index dc1fc94..7cab684 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -164,7 +164,7 @@ int s5p_mfc_reset(struct s5p_mfc_dev *dev) mfc_debug_enter(); - if (IS_MFCV6(dev)) { + if (IS_MFCV6_PLUS(dev)) { /* Reset IP */ /* except RISC, reset */ mfc_write(dev, 0xFEE, S5P_FIMV_MFC_RESET_V6); @@ -213,7 +213,7 @@ int s5p_mfc_reset(struct s5p_mfc_dev *dev) static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev) { - if (IS_MFCV6(dev)) { + if (IS_MFCV6_PLUS(dev)) { mfc_write(dev, dev->bank1, S5P_FIMV_RISC_BASE_ADDRESS_V6); mfc_debug(2, "Base Address : %08x\n", dev->bank1); } else { @@ -226,7 +226,7 @@ static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev) static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev) { - if (IS_MFCV6(dev)) { + if (IS_MFCV6_PLUS(dev)) { /* Zero initialization should be done before RESET. * Nothing to do here. */ } else { @@ -264,7 +264,7 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev) s5p_mfc_clear_cmds(dev); /* 3. Release reset signal to the RISC */ s5p_mfc_clean_dev_int_flags(dev); - if (IS_MFCV6(dev)) + if (IS_MFCV6_PLUS(dev)) mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6); else mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET); @@ -301,7 +301,7 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev) s5p_mfc_clock_off(); return -EIO; } - if (IS_MFCV6(dev)) + if (IS_MFCV6_PLUS(dev)) ver = mfc_read(dev, S5P_FIMV_FW_VERSION_V6); else ver = mfc_read(dev, S5P_FIMV_FW_VERSION); @@ -380,7 +380,7 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev) return ret; } /* 4. Release reset signal to the RISC */ - if (IS_MFCV6(dev)) + if (IS_MFCV6_PLUS(dev)) mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6); else mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 5296385..e2f2212 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -382,7 +382,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("Unsupported format for source.\n"); return -EINVAL; } - if (!IS_MFCV6(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) { + if (!IS_MFCV6_PLUS(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) { mfc_err("Not supported format.\n"); return -EINVAL; } @@ -392,10 +392,11 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("Unsupported format for destination.\n"); return -EINVAL; } - if (IS_MFCV6(dev) && (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) { + if (IS_MFCV6_PLUS(dev) && + (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) { mfc_err("Not supported format.\n"); return -EINVAL; - } else if (!IS_MFCV6(dev) && + } else if (!IS_MFCV6_PLUS(dev) && (fmt->fourcc != V4L2_PIX_FMT_NV12MT)) { mfc_err("Not supported format.\n"); return -EINVAL; @@ -430,10 +431,11 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("Unsupported format for source.\n"); return -EINVAL; } - if (!IS_MFCV6(dev) && (fmt->fourcc != V4L2_PIX_FMT_NV12MT)) { + if (!IS_MFCV6_PLUS(dev) && + (fmt->fourcc != V4L2_PIX_FMT_NV12MT)) { mfc_err("Not supported format.\n"); return -EINVAL; - } else if (IS_MFCV6(dev) && + } else if (IS_MFCV6_PLUS(dev) && (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) { mfc_err("Not supported format.\n"); return -EINVAL; @@ -457,7 +459,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) ret = -EINVAL; goto out; } - if (!IS_MFCV6(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) { + if (!IS_MFCV6_PLUS(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) { mfc_err("Not supported format.\n"); return -EINVAL; } @@ -942,7 +944,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, psize[0] = ctx->luma_size; psize[1] = ctx->chroma_size; - if (IS_MFCV6(dev)) + if (IS_MFCV6_PLUS(dev)) allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX]; else @@ -1067,7 +1069,7 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q) ctx->dpb_flush_flag = 1; ctx->dec_dst_flag = 0; spin_unlock_irqrestore(&dev->irqlock, flags); - if (IS_MFCV6(dev) && (ctx->state == MFCINST_RUNNING)) { + if (IS_MFCV6_PLUS(dev) && (ctx->state == MFCINST_RUNNING)) { ctx->state = MFCINST_FLUSH; set_work_bit_irqsave(ctx); s5p_mfc_clean_ctx_int_flags(ctx); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 2549967..f734ccc 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -663,7 +663,7 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx) spin_unlock_irqrestore(&dev->irqlock, flags); } - if (!IS_MFCV6(dev)) { + if (!IS_MFCV6_PLUS(dev)) { ctx->state = MFCINST_RUNNING; if (s5p_mfc_ctx_ready(ctx)) set_work_bit_irqsave(ctx); @@ -993,11 +993,11 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) return -EINVAL; } - if (!IS_MFCV6(dev) && + if (!IS_MFCV6_PLUS(dev) && (fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16)) { mfc_err("Not supported format.\n"); return -EINVAL; - } else if (IS_MFCV6(dev) && + } else if (IS_MFCV6_PLUS(dev) && (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) { mfc_err("Not supported format.\n"); return -EINVAL; @@ -1072,7 +1072,7 @@ static int vidioc_reqbufs(struct file *file, void *priv, return -EINVAL; } - if (IS_MFCV6(dev)) { + if (IS_MFCV6_PLUS(dev)) { /* Check for min encoder buffers */ if (ctx->pb_count && (reqbufs->count < ctx->pb_count)) { @@ -1353,7 +1353,7 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) S5P_FIMV_ENC_PROFILE_H264_BASELINE; break; case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: - if (IS_MFCV6(dev)) + if (IS_MFCV6_PLUS(dev)) p->codec.h264.profile = S5P_FIMV_ENC_PROFILE_H264_CONSTRAINED_BASELINE; else @@ -1662,9 +1662,10 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, *buf_count = 1; if (*buf_count > MFC_MAX_BUFFERS) *buf_count = MFC_MAX_BUFFERS; + psize[0] = ctx->luma_size; psize[1] = ctx->chroma_size; - if (IS_MFCV6(dev)) { + if (IS_MFCV6_PLUS(dev)) { allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX]; allocators[1] = @@ -1773,7 +1774,8 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); struct s5p_mfc_dev *dev = ctx->dev; - if (IS_MFCV6(dev) && (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) { + if (IS_MFCV6_PLUS(dev) && + (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) { if ((ctx->state == MFCINST_GOT_INST) && (dev->curr_ctx == ctx->num) && dev->hw_lock) { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c index 10f8ac3..3c01c33 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c @@ -21,7 +21,7 @@ static struct s5p_mfc_hw_ops *s5p_mfc_ops; void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev) { - if (IS_MFCV6(dev)) { + if (IS_MFCV6_PLUS(dev)) { s5p_mfc_ops = s5p_mfc_init_hw_ops_v6(); dev->warn_start = S5P_FIMV_ERR_WARNINGS_START_V6; } else { -- cgit v0.10.2 From a60ee1e8d002770bd93ee48219e2cdad40dd0028 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Tue, 9 Jul 2013 01:24:37 -0300 Subject: [media] s5p-mfc: Add register definition file for MFC v7 The patch adds the register definition file for new firmware version v7 for MFC. New firmware supports VP8 encoding along with many other features. Signed-off-by: Arun Kumar K Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h new file mode 100644 index 0000000..24dba69 --- /dev/null +++ b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h @@ -0,0 +1,58 @@ +/* + * Register definition file for Samsung MFC V7.x Interface (FIMV) driver + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * 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 _REGS_MFC_V7_H +#define _REGS_MFC_V7_H + +#include "regs-mfc-v6.h" + +/* Additional features of v7 */ +#define S5P_FIMV_CODEC_VP8_ENC_V7 25 + +/* Additional registers for v7 */ +#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7 0xf47c + +#define S5P_FIMV_E_SOURCE_FIRST_ADDR_V7 0xf9e0 +#define S5P_FIMV_E_SOURCE_SECOND_ADDR_V7 0xf9e4 +#define S5P_FIMV_E_SOURCE_THIRD_ADDR_V7 0xf9e8 +#define S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7 0xf9ec +#define S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7 0xf9f0 +#define S5P_FIMV_E_SOURCE_THIRD_STRIDE_V7 0xf9f4 + +#define S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7 0xfa70 +#define S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7 0xfa74 + +#define S5P_FIMV_E_VP8_OPTIONS_V7 0xfdb0 +#define S5P_FIMV_E_VP8_FILTER_OPTIONS_V7 0xfdb4 +#define S5P_FIMV_E_VP8_GOLDEN_FRAME_OPTION_V7 0xfdb8 +#define S5P_FIMV_E_VP8_NUM_T_LAYER_V7 0xfdc4 + +/* MFCv7 variant defines */ +#define MAX_FW_SIZE_V7 (SZ_1M) /* 1MB */ +#define MAX_CPB_SIZE_V7 (3 * SZ_1M) /* 3MB */ +#define MFC_VERSION_V7 0x72 +#define MFC_NUM_PORTS_V7 1 + +/* MFCv7 Context buffer sizes */ +#define MFC_CTX_BUF_SIZE_V7 (30 * SZ_1K) /* 30KB */ +#define MFC_H264_DEC_CTX_BUF_SIZE_V7 (2 * SZ_1M) /* 2MB */ +#define MFC_OTHER_DEC_CTX_BUF_SIZE_V7 (20 * SZ_1K) /* 20KB */ +#define MFC_H264_ENC_CTX_BUF_SIZE_V7 (100 * SZ_1K) /* 100KB */ +#define MFC_OTHER_ENC_CTX_BUF_SIZE_V7 (10 * SZ_1K) /* 10KB */ + +/* Buffer size defines */ +#define S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7(w, h) \ + (SZ_1M + ((w) * 144) + (8192 * (h)) + 49216) + +#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(w, h) \ + (((w) * 48) + (((w) + 1) / 2 * 128) + 144 + 8192) + +#endif /*_REGS_MFC_V7_H*/ -- cgit v0.10.2 From 5441e9dafdfc6dc40fa506fd02c4d06d31a5873b Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Tue, 9 Jul 2013 01:24:38 -0300 Subject: [media] s5p-mfc: Core support for MFC v7 Adds variant data and core support for the MFC v7 firmware Signed-off-by: Arun Kumar K Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/s5p-mfc.txt b/Documentation/devicetree/bindings/media/s5p-mfc.txt index df37b02..36bd2d6 100644 --- a/Documentation/devicetree/bindings/media/s5p-mfc.txt +++ b/Documentation/devicetree/bindings/media/s5p-mfc.txt @@ -10,6 +10,7 @@ Required properties: - compatible : value should be either one among the following (a) "samsung,mfc-v5" for MFC v5 present in Exynos4 SoCs (b) "samsung,mfc-v6" for MFC v6 present in Exynos5 SoCs + (b) "samsung,mfc-v7" for MFC v7 present in Exynos5420 SoC - reg : Physical base address of the IP registers and length of memory mapped region. diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index a130dcd..084263d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1391,6 +1391,32 @@ static struct s5p_mfc_variant mfc_drvdata_v6 = { .fw_name = "s5p-mfc-v6.fw", }; +struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = { + .dev_ctx = MFC_CTX_BUF_SIZE_V7, + .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V7, + .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V7, + .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V7, + .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V7, +}; + +struct s5p_mfc_buf_size buf_size_v7 = { + .fw = MAX_FW_SIZE_V7, + .cpb = MAX_CPB_SIZE_V7, + .priv = &mfc_buf_size_v7, +}; + +struct s5p_mfc_buf_align mfc_buf_align_v7 = { + .base = 0, +}; + +static struct s5p_mfc_variant mfc_drvdata_v7 = { + .version = MFC_VERSION_V7, + .port_num = MFC_NUM_PORTS_V7, + .buf_size = &buf_size_v7, + .buf_align = &mfc_buf_align_v7, + .fw_name = "s5p-mfc-v7.fw", +}; + static struct platform_device_id mfc_driver_ids[] = { { .name = "s5p-mfc", @@ -1401,6 +1427,9 @@ static struct platform_device_id mfc_driver_ids[] = { }, { .name = "s5p-mfc-v6", .driver_data = (unsigned long)&mfc_drvdata_v6, + }, { + .name = "s5p-mfc-v7", + .driver_data = (unsigned long)&mfc_drvdata_v7, }, {}, }; @@ -1413,6 +1442,9 @@ static const struct of_device_id exynos_mfc_match[] = { }, { .compatible = "samsung,mfc-v6", .data = &mfc_drvdata_v6, + }, { + .compatible = "samsung,mfc-v7", + .data = &mfc_drvdata_v7, }, {}, }; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index d47016d..17545d7 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -24,6 +24,7 @@ #include #include "regs-mfc.h" #include "regs-mfc-v6.h" +#include "regs-mfc-v7.h" /* Definitions related to MFC memory */ @@ -684,5 +685,6 @@ void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx); (dev->variant->port_num ? 1 : 0) : 0) : 0) #define IS_TWOPORT(dev) (dev->variant->port_num == 2 ? 1 : 0) #define IS_MFCV6_PLUS(dev) (dev->variant->version >= 0x60 ? 1 : 0) +#define IS_MFCV7(dev) (dev->variant->version >= 0x70 ? 1 : 0) #endif /* S5P_MFC_COMMON_H_ */ -- cgit v0.10.2 From debe6267b718526ee9715501b8f52a0295e3712a Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Tue, 9 Jul 2013 01:24:39 -0300 Subject: [media] s5p-mfc: Update driver for v7 firmware Firmware version v7 is mostly similar to v6 in terms of hardware specific controls and commands. So the hardware specific opr_v6 and cmd_v6 are re-used for v7 also. This patch updates the v6 files to handle v7 version also. Signed-off-by: Arun Kumar K Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h index 24dba69..ea5ec2a 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h +++ b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h @@ -41,6 +41,9 @@ #define MFC_VERSION_V7 0x72 #define MFC_NUM_PORTS_V7 1 +#define MFC_LUMA_PAD_BYTES_V7 256 +#define MFC_CHROMA_PAD_BYTES_V7 128 + /* MFCv7 Context buffer sizes */ #define MFC_CTX_BUF_SIZE_V7 (30 * SZ_1K) /* 30KB */ #define MFC_H264_DEC_CTX_BUF_SIZE_V7 (2 * SZ_1M) /* 2MB */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index f734ccc..6dafe96 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1665,6 +1665,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, psize[0] = ctx->luma_size; psize[1] = ctx->chroma_size; + if (IS_MFCV6_PLUS(dev)) { allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX]; 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 66f0d04..32826ba 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -80,6 +80,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V6(mb_width, mb_height), S5P_FIMV_TMV_BUFFER_ALIGN_V6); + ctx->luma_dpb_size = ALIGN((mb_width * mb_height) * S5P_FIMV_LUMA_MB_TO_PIXEL_V6, S5P_FIMV_LUMA_DPB_BUFFER_ALIGN_V6); @@ -112,10 +113,18 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) (ctx->mv_count * ctx->mv_size); break; case S5P_MFC_CODEC_MPEG4_DEC: - ctx->scratch_buf_size = - S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6( - mb_width, - mb_height); + if (IS_MFCV7(dev)) { + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7( + mb_width, + mb_height); + } else { + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6( + mb_width, + mb_height); + } + ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); ctx->bank1.size = ctx->scratch_buf_size; @@ -329,6 +338,12 @@ static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx) ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6); ctx->luma_size = ALIGN((mb_width * mb_height) * 256, 256); ctx->chroma_size = ALIGN((mb_width * mb_height) * 128, 256); + + /* MFCv7 needs pad bytes for Luma and Chroma */ + if (IS_MFCV7(ctx->dev)) { + ctx->luma_size += MFC_LUMA_PAD_BYTES_V7; + ctx->chroma_size += MFC_CHROMA_PAD_BYTES_V7; + } } /* Set registers for decoding stream buffer */ @@ -453,8 +468,13 @@ static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, { struct s5p_mfc_dev *dev = ctx->dev; - WRITEL(y_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6); /* 256B align */ - WRITEL(c_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6); + if (IS_MFCV7(dev)) { + WRITEL(y_addr, S5P_FIMV_E_SOURCE_FIRST_ADDR_V7); + WRITEL(c_addr, S5P_FIMV_E_SOURCE_SECOND_ADDR_V7); + } else { + WRITEL(y_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6); + WRITEL(c_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6); + } mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr); mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr); @@ -466,8 +486,13 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, struct s5p_mfc_dev *dev = ctx->dev; unsigned long enc_recon_y_addr, enc_recon_c_addr; - *y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6); - *c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6); + if (IS_MFCV7(dev)) { + *y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7); + *c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7); + } else { + *y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6); + *c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6); + } enc_recon_y_addr = READL(S5P_FIMV_E_RECON_LUMA_DPB_ADDR_V6); enc_recon_c_addr = READL(S5P_FIMV_E_RECON_CHROMA_DPB_ADDR_V6); @@ -1166,6 +1191,12 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6); WRITEL(ctx->display_delay, S5P_FIMV_D_DISPLAY_DELAY_V6); } + + if (IS_MFCV7(dev)) { + WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6); + reg = 0; + } + /* Setup loop filter, for decoding this is only valid for MPEG4 */ if (ctx->codec_mode == S5P_MFC_CODEC_MPEG4_DEC) { mfc_debug(2, "Set loop filter to: %d\n", @@ -1176,7 +1207,10 @@ 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); - WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6); + if (IS_MFCV7(dev)) + WRITEL(reg, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7); + else + WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6); /* 0: NV12(CbCr), 1: NV21(CrCb) */ if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M) @@ -1184,6 +1218,7 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) else WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6); + /* sei parse */ WRITEL(ctx->sei_fp_parse & 0x1, S5P_FIMV_D_SEI_ENABLE_V6); @@ -1254,6 +1289,12 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) return -EINVAL; } + /* Set stride lengths */ + if (IS_MFCV7(dev)) { + WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7); + WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7); + } + WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_SEQ_HEADER_V6, NULL); -- cgit v0.10.2 From d1e9b7c12b745af715101578a677e4a6b16c09e4 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 9 Jul 2013 01:24:40 -0300 Subject: [media] V4L: Add support for integer menu controls with standard menu items The patch modifies the helper function v4l2_ctrl_new_std_menu to accept integer menu controls with standard menu items. Signed-off-by: Sylwester Nawrocki Signed-off-by: Arun Kumar K Acked-by: Hans Verkuil Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index 676f873..06cf3ac 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -124,26 +124,27 @@ You add non-menu controls by calling v4l2_ctrl_new_std: const struct v4l2_ctrl_ops *ops, u32 id, s32 min, s32 max, u32 step, s32 def); -Menu controls are added by calling v4l2_ctrl_new_std_menu: +Menu and integer menu controls are added by calling v4l2_ctrl_new_std_menu: struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, s32 max, s32 skip_mask, s32 def); -Or alternatively for integer menu controls, by calling v4l2_ctrl_new_int_menu: +Menu controls with a driver specific menu are added by calling +v4l2_ctrl_new_std_menu_items: + + 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 skip_mask, s32 def, const char * const *qmenu); + +Integer menu controls with a driver specific menu can be added by calling +v4l2_ctrl_new_int_menu: 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); -Standard menu controls with a driver specific menu are added by calling -v4l2_ctrl_new_std_menu_items: - - 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 skip_mask, s32 def, const char * const *qmenu); - These functions are typically called right after the v4l2_ctrl_handler_init: static const s64 exp_bias_qmenu[] = { diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index fccd08b..e03a2e8 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -552,6 +552,20 @@ const char * const *v4l2_ctrl_get_menu(u32 id) } EXPORT_SYMBOL(v4l2_ctrl_get_menu); +/* + * Returns NULL or an s64 type array containing the menu for given + * control ID. The total number of the menu items is returned in @len. + */ +const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len) +{ + switch (id) { + default: + *len = 0; + return NULL; + }; +} +EXPORT_SYMBOL(v4l2_ctrl_get_int_menu); + /* Return the control name. */ const char *v4l2_ctrl_get_name(u32 id) { @@ -1712,20 +1726,28 @@ 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) { - const char * const *qmenu = v4l2_ctrl_get_menu(id); + const char * const *qmenu = NULL; + const s64 *qmenu_int = NULL; const char *name; enum v4l2_ctrl_type type; + unsigned int qmenu_int_len; s32 min; s32 step; u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type != V4L2_CTRL_TYPE_MENU) { + + if (type == V4L2_CTRL_TYPE_MENU) + qmenu = v4l2_ctrl_get_menu(id); + else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) + qmenu_int = v4l2_ctrl_get_int_menu(id, &qmenu_int_len); + + if ((!qmenu && !qmenu_int) || (qmenu_int && max > qmenu_int_len)) { handler_set_err(hdl, -EINVAL); return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, mask, def, flags, qmenu, NULL, NULL); + 0, max, mask, def, flags, qmenu, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); -- cgit v0.10.2 From bc9028e1d38419f9249cb0d1285e290be7e67223 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Tue, 9 Jul 2013 01:24:41 -0300 Subject: [media] V4L: Add VP8 encoder controls This patch adds new V4L controls for VP8 encoding. Signed-off-by: Kiran AVND Signed-off-by: Arun Kumar K Acked-by: Hans Verkuil Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index c2fc9ec..7a3b49b 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -722,17 +722,22 @@ for more details.
- MPEG Control Reference + Codec Control Reference - Below all controls within the MPEG control class are + Below all controls within the Codec control class are described. First the generic controls, then controls specific for certain hardware. + Note: These controls are applicable to all codecs and +not just MPEG. The defines are prefixed with V4L2_CID_MPEG/V4L2_MPEG +as the controls were originally made for MPEG codecs and later +extended to cover all encoding formats. +
- Generic MPEG Controls + Generic Codec Controls - MPEG Control IDs + Codec Control IDs @@ -752,7 +757,7 @@ certain hardware. V4L2_CID_MPEG_CLASS  class - The MPEG class + The Codec class descriptor. Calling &VIDIOC-QUERYCTRL; for this control will return a description of this control class. This description can be used as the caption of a Tab page in a GUI, for example. @@ -3009,6 +3014,159 @@ in by the application. 0 = do not insert, 1 = insert packets.
+ +
+ VPX Control Reference + + The VPX controls include controls for encoding parameters + of VPx video codec. + + + VPX Control IDs + + + + + + + + + + + ID + Type + Description + + + + + + + + V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS + enum v4l2_vp8_num_partitions + + The number of token partitions to use in VP8 encoder. +Possible values are: + + + + + + V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION + 1 coefficient partition + + + V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS + 2 coefficient partitions + + + V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS + 4 coefficient partitions + + + V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS + 8 coefficient partitions + + + + + + + + V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 + boolean + + Setting this prevents intra 4x4 mode in the intra mode decision. + + + + + V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES + enum v4l2_vp8_num_ref_frames + + The number of reference pictures for encoding P frames. +Possible values are: + + + + + + V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME + Last encoded frame will be searched + + + V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME + Two frames will be searched among the last encoded frame, the golden frame +and the alternate reference (altref) frame. The encoder implementation will decide which two are chosen. + + + V4L2_CID_MPEG_VIDEO_VPX_3_REF_FRAME + The last encoded frame, the golden frame and the altref frame will be searched. + + + + + + + + V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL + integer + + Indicates the loop filter level. The adjustment of the loop +filter level is done via a delta value against a baseline loop filter value. + + + + + V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS + integer + + This parameter affects the loop filter. Anything above +zero weakens the deblocking effect on the loop filter. + + + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD + integer + + Sets the refresh period for the golden frame. The period is defined +in number of frames. For a value of 'n', every nth frame starting from the first key frame will be taken as a golden frame. +For eg. for encoding sequence of 0, 1, 2, 3, 4, 5, 6, 7 where the golden frame refresh period is set as 4, the frames +0, 4, 8 etc will be taken as the golden frames as frame 0 is always a key frame. + + + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL + enum v4l2_vp8_golden_frame_sel + + Selects the golden frame for encoding. +Possible values are: + + + + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV + Use the (n-2)th frame as a golden frame, current frame index being 'n'. + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD + Use the previous specific frame indicated by +V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD as a golden frame. + + + + + + + + +
+ +
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e03a2e8..c6dc1fd 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -424,6 +424,12 @@ const char * const *v4l2_ctrl_get_menu(u32 id) NULL, }; + static const char * const vpx_golden_frame_sel[] = { + "Use Previous Frame", + "Use Previous Specific Frame", + NULL, + }; + static const char * const flash_led_mode[] = { "Off", "Flash", @@ -538,6 +544,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return mpeg_mpeg4_level; case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return mpeg4_profile; + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: + return vpx_golden_frame_sel; case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return jpeg_chroma_subsampling; case V4L2_CID_DV_TX_MODE: @@ -552,13 +560,26 @@ const char * const *v4l2_ctrl_get_menu(u32 id) } EXPORT_SYMBOL(v4l2_ctrl_get_menu); +#define __v4l2_qmenu_int_len(arr, len) ({ *(len) = ARRAY_SIZE(arr); arr; }) /* * Returns NULL or an s64 type array containing the menu for given * control ID. The total number of the menu items is returned in @len. */ const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len) { + static const s64 const qmenu_int_vpx_num_partitions[] = { + 1, 2, 4, 8, + }; + + static const s64 const qmenu_int_vpx_num_ref_frames[] = { + 1, 2, 3, + }; + switch (id) { + case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: + return __v4l2_qmenu_int_len(qmenu_int_vpx_num_partitions, len); + case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES: + return __v4l2_qmenu_int_len(qmenu_int_vpx_num_ref_frames, len); default: *len = 0; return NULL; @@ -614,9 +635,11 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component"; case V4L2_CID_COLORFX_CBCR: return "Color Effects, CbCr"; - /* MPEG controls */ + /* Codec controls */ + /* The MPEG controls are applicable to all codec controls + * and the 'MPEG' part of the define is historical */ /* Keep the order of the 'case's the same as in videodev2.h! */ - case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; + case V4L2_CID_MPEG_CLASS: return "Codec Controls"; case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type"; case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID"; case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID"; @@ -714,6 +737,15 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VBV_DELAY: return "Initial Delay for VBV Control"; case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: return "Repeat Sequence Header"; + /* VPX controls */ + case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: return "VPX Number of Partitions"; + case V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4: return "VPX Intra Mode Decision Disable"; + case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES: return "VPX No. of Refs for P Frame"; + case V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL: return "VPX Loop Filter Level Range"; + case V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS: return "VPX Deblocking Effect Control"; + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD: return "VPX Golden Frame Refresh Period"; + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: return "VPX Golden Frame Indicator"; + /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; @@ -928,6 +960,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DV_RX_RGB_RANGE: case V4L2_CID_TEST_PATTERN: case V4L2_CID_TUNE_DEEMPHASIS: + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: @@ -939,6 +972,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_ISO_SENSITIVITY: case V4L2_CID_AUTO_EXPOSURE_BIAS: + case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: + case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES: *type = V4L2_CTRL_TYPE_INTEGER_MENU; break; case V4L2_CID_USER_CLASS: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index e90a88a..083bb5a 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -161,6 +161,8 @@ enum v4l2_colorfx { #define V4L2_CID_USER_SI476X_BASE (V4L2_CID_USER_BASE + 0x1040) /* MPEG-class control IDs */ +/* The MPEG controls are applicable to all codec controls + * and the 'MPEG' part of the define is historical */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) #define V4L2_CID_MPEG_CLASS (V4L2_CTRL_CLASS_MPEG | 1) @@ -522,6 +524,33 @@ enum v4l2_mpeg_video_mpeg4_profile { }; #define V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (V4L2_CID_MPEG_BASE+407) +/* Control IDs for VP8 streams + * Although VP8 is not part of MPEG we add these controls to the MPEG class + * as that class is already handling other video compression standards + */ +#define V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS (V4L2_CID_MPEG_BASE+500) +enum v4l2_vp8_num_partitions { + V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION = 0, + V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS = 1, + V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS = 2, + V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS = 3, +}; +#define V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 (V4L2_CID_MPEG_BASE+501) +#define V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES (V4L2_CID_MPEG_BASE+502) +enum v4l2_vp8_num_ref_frames { + V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME = 0, + V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME = 1, + V4L2_CID_MPEG_VIDEO_VPX_3_REF_FRAME = 2, +}; +#define V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL (V4L2_CID_MPEG_BASE+503) +#define V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS (V4L2_CID_MPEG_BASE+504) +#define V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD (V4L2_CID_MPEG_BASE+505) +#define V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL (V4L2_CID_MPEG_BASE+506) +enum v4l2_vp8_golden_frame_sel { + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV = 0, + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD = 1, +}; + /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+0) -- cgit v0.10.2 From 3a9677063f00a61b6067a07df3d7ee12eace79b7 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Tue, 9 Jul 2013 01:24:42 -0300 Subject: [media] s5p-mfc: Add support for VP8 encoder MFC v7 supports VP8 encoding and this patch adds support for it in the driver. Signed-off-by: Arun Kumar K Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c index 5708fc3..db796c8 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c @@ -108,6 +108,9 @@ static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx) case S5P_MFC_CODEC_H263_ENC: codec_type = S5P_FIMV_CODEC_H263_ENC_V6; break; + case S5P_MFC_CODEC_VP8_ENC: + codec_type = S5P_FIMV_CODEC_VP8_ENC_V7; + break; default: codec_type = S5P_FIMV_CODEC_NONE_V6; }; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 17545d7..6920b54 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -65,7 +65,7 @@ static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b) #define MFC_ENC_CAP_PLANE_COUNT 1 #define MFC_ENC_OUT_PLANE_COUNT 2 #define STUFF_BYTE 4 -#define MFC_MAX_CTRLS 70 +#define MFC_MAX_CTRLS 77 #define S5P_MFC_CODEC_NONE -1 #define S5P_MFC_CODEC_H264_DEC 0 @@ -81,6 +81,7 @@ static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b) #define S5P_MFC_CODEC_H264_MVC_ENC 21 #define S5P_MFC_CODEC_MPEG4_ENC 22 #define S5P_MFC_CODEC_H263_ENC 23 +#define S5P_MFC_CODEC_VP8_ENC 24 #define S5P_MFC_R2H_CMD_EMPTY 0 #define S5P_MFC_R2H_CMD_SYS_INIT_RET 1 @@ -409,6 +410,21 @@ struct s5p_mfc_mpeg4_enc_params { }; /** + * struct s5p_mfc_vp8_enc_params - encoding parameters for vp8 + */ +struct s5p_mfc_vp8_enc_params { + u8 imd_4x4; + enum v4l2_vp8_num_partitions num_partitions; + enum v4l2_vp8_num_ref_frames num_ref; + u8 filter_level; + u8 filter_sharpness; + u32 golden_frame_ref_period; + enum v4l2_vp8_golden_frame_sel golden_frame_sel; + u8 hier_layer; + u8 hier_layer_qp[3]; +}; + +/** * struct s5p_mfc_enc_params - general encoding parameters */ struct s5p_mfc_enc_params { @@ -442,6 +458,7 @@ struct s5p_mfc_enc_params { struct { struct s5p_mfc_h264_enc_params h264; struct s5p_mfc_mpeg4_enc_params mpeg4; + struct s5p_mfc_vp8_enc_params vp8; } codec; }; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 6dafe96..fb077b3 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -84,6 +84,13 @@ static struct s5p_mfc_fmt formats[] = { .type = MFC_FMT_ENC, .num_planes = 1, }, + { + .name = "VP8 Encoded Stream", + .fourcc = V4L2_PIX_FMT_VP8, + .codec_mode = S5P_MFC_CODEC_VP8_ENC, + .type = MFC_FMT_ENC, + .num_planes = 1, + }, }; #define NUM_FORMATS ARRAY_SIZE(formats) @@ -557,6 +564,60 @@ static struct mfc_control controls[] = { .step = 1, .default_value = 0, }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS, + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .maximum = V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS, + .default_value = V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION, + .menu_skip_mask = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES, + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .maximum = V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME, + .default_value = V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME, + .menu_skip_mask = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 63, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 7, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = (1 << 16) - 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV, + .maximum = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD, + .default_value = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV, + .menu_skip_mask = 0, + }, }; #define NUM_CTRLS ARRAY_SIZE(controls) @@ -965,6 +1026,10 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("failed to set capture format\n"); return -EINVAL; } + if (!IS_MFCV7(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) { + mfc_err("VP8 is supported only in MFC v7\n"); + return -EINVAL; + } ctx->state = MFCINST_INIT; ctx->dst_fmt = fmt; ctx->codec_mode = ctx->dst_fmt->codec_mode; @@ -1482,6 +1547,27 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: p->codec.mpeg4.quarter_pixel = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: + p->codec.vp8.num_partitions = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4: + p->codec.vp8.imd_4x4 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES: + p->codec.vp8.num_ref = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL: + p->codec.vp8.filter_level = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS: + p->codec.vp8.filter_sharpness = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD: + p->codec.vp8.golden_frame_ref_period = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: + p->codec.vp8.golden_frame_sel = ctrl->val; + break; default: v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", ctrl->id, ctrl->val); @@ -1930,7 +2016,9 @@ int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx) ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler, &cfg, NULL); } else { - if (controls[i].type == V4L2_CTRL_TYPE_MENU) { + if ((controls[i].type == V4L2_CTRL_TYPE_MENU) || + (controls[i].type == + V4L2_CTRL_TYPE_INTEGER_MENU)) { ctx->ctrls[i] = v4l2_ctrl_new_std_menu( &ctx->ctrl_handler, &s5p_mfc_enc_ctrl_ops, controls[i].id, 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 32826ba..461358c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -188,6 +188,19 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->chroma_dpb_size + ctx->me_buffer_size)); ctx->bank2.size = 0; break; + case S5P_MFC_CODEC_VP8_ENC: + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7( + mb_width, + mb_height); + ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, + S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); + ctx->bank1.size = + ctx->scratch_buf_size + ctx->tmv_buffer_size + + (ctx->pb_count * (ctx->luma_dpb_size + + ctx->chroma_dpb_size + ctx->me_buffer_size)); + ctx->bank2.size = 0; + break; default: break; } @@ -237,6 +250,7 @@ static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) break; case S5P_MFC_CODEC_MPEG4_ENC: case S5P_MFC_CODEC_H263_ENC: + case S5P_MFC_CODEC_VP8_ENC: ctx->ctx.size = buf_size->other_enc_ctx; break; default: @@ -1165,6 +1179,80 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) return 0; } +static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) +{ + struct s5p_mfc_dev *dev = ctx->dev; + struct s5p_mfc_enc_params *p = &ctx->enc_params; + struct s5p_mfc_vp8_enc_params *p_vp8 = &p->codec.vp8; + unsigned int reg = 0; + unsigned int val = 0; + + mfc_debug_enter(); + + s5p_mfc_set_enc_params(ctx); + + /* pictype : number of B */ + reg = READL(S5P_FIMV_E_GOP_CONFIG_V6); + reg &= ~(0x3 << 16); + reg |= ((p->num_b_frame & 0x3) << 16); + WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6); + + /* profile & level */ + reg = 0; + /** profile */ + reg |= (0x1 << 4); + WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6); + + /* rate control config. */ + reg = READL(S5P_FIMV_E_RC_CONFIG_V6); + /** macroblock level rate control */ + reg &= ~(0x1 << 8); + reg |= ((p->rc_mb & 0x1) << 8); + WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + + /* frame rate */ + if (p->rc_frame && p->rc_framerate_num && p->rc_framerate_denom) { + reg = 0; + reg |= ((p->rc_framerate_num & 0xFFFF) << 16); + reg |= p->rc_framerate_denom & 0xFFFF; + WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6); + } + + /* vbv buffer size */ + if (p->frame_skip_mode == + V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { + WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6); + + if (p->rc_frame) + WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6); + } + + /* VP8 specific params */ + reg = 0; + reg |= (p_vp8->imd_4x4 & 0x1) << 10; + switch (p_vp8->num_partitions) { + case V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION: + val = 0; + break; + case V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS: + val = 2; + break; + case V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS: + val = 4; + break; + case V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS: + val = 8; + break; + } + reg |= (val & 0xF) << 3; + reg |= (p_vp8->num_ref & 0x2); + WRITEL(reg, S5P_FIMV_E_VP8_OPTIONS_V7); + + mfc_debug_leave(); + + return 0; +} + /* Initialize decoding */ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) { @@ -1283,6 +1371,8 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) s5p_mfc_set_enc_params_mpeg4(ctx); else if (ctx->codec_mode == S5P_MFC_CODEC_H263_ENC) s5p_mfc_set_enc_params_h263(ctx); + else if (ctx->codec_mode == S5P_MFC_CODEC_VP8_ENC) + s5p_mfc_set_enc_params_vp8(ctx); else { mfc_err("Unknown codec for encoding (%x).\n", ctx->codec_mode); -- cgit v0.10.2 From 5c7b25b90d36942c524d06522ebaf0510a75592a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 7 Jun 2013 12:45:11 -0300 Subject: [media] media: Add support for circular graph traversal The graph traversal API (media_entity_graph_walk_*) doesn't support cyclic graphs and will fail to correctly walk a graph when circular links exist. Support circular graph traversal by checking whether an entity has already been visited before pushing it to the stack. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index cb30ffb..2c286c3 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -20,6 +20,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include @@ -121,7 +122,6 @@ static struct media_entity *stack_pop(struct media_entity_graph *graph) return entity; } -#define stack_peek(en) ((en)->stack[(en)->top - 1].entity) #define link_top(en) ((en)->stack[(en)->top].link) #define stack_top(en) ((en)->stack[(en)->top].entity) @@ -140,6 +140,12 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph, { graph->top = 0; graph->stack[graph->top].entity = NULL; + bitmap_zero(graph->entities, MEDIA_ENTITY_ENUM_MAX_ID); + + if (WARN_ON(entity->id >= MEDIA_ENTITY_ENUM_MAX_ID)) + return; + + __set_bit(entity->id, graph->entities); stack_push(graph, entity); } EXPORT_SYMBOL_GPL(media_entity_graph_walk_start); @@ -180,9 +186,11 @@ media_entity_graph_walk_next(struct media_entity_graph *graph) /* Get the entity in the other end of the link . */ next = media_entity_other(entity, link); + if (WARN_ON(next->id >= MEDIA_ENTITY_ENUM_MAX_ID)) + return NULL; - /* Was it the entity we came here from? */ - if (next == stack_peek(graph)) { + /* Has the entity already been visited? */ + if (__test_and_set_bit(next->id, graph->entities)) { link_top(graph)++; continue; } diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 06bacf9..10df551 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -23,6 +23,7 @@ #ifndef _MEDIA_ENTITY_H #define _MEDIA_ENTITY_H +#include #include #include @@ -113,12 +114,15 @@ static inline u32 media_entity_subtype(struct media_entity *entity) } #define MEDIA_ENTITY_ENUM_MAX_DEPTH 16 +#define MEDIA_ENTITY_ENUM_MAX_ID 64 struct media_entity_graph { struct { struct media_entity *entity; int link; } stack[MEDIA_ENTITY_ENUM_MAX_DEPTH]; + + DECLARE_BITMAP(entities, MEDIA_ENTITY_ENUM_MAX_ID); int top; }; -- cgit v0.10.2 From c09aada7ac8a8c03970bd2fb41176647da7cef99 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 1 Aug 2013 21:34:52 -0300 Subject: [media] Documentation: media: Clarify the VIDIOC_CREATE_BUFS format requirements The VIDIOC_CREATE_BUFS ioctl takes a format argument that must contain a valid format supported by the driver. Clarify the documentation. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml index cd99436..9b700a5 100644 --- a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml @@ -62,18 +62,29 @@ addition to the VIDIOC_REQBUFS ioctl, when a tighter control over buffers is required. This ioctl can be called multiple times to create buffers of different sizes. - To allocate device buffers applications initialize relevant fields of -the v4l2_create_buffers structure. They set the -type field in the -&v4l2-format; structure, embedded in this -structure, to the respective stream or buffer type. -count must be set to the number of required buffers. -memory specifies the required I/O method. The -format field shall typically be filled in using -either the VIDIOC_TRY_FMT or -VIDIOC_G_FMT ioctl(). Additionally, applications can adjust -sizeimage fields to fit their specific needs. The -reserved array must be zeroed. + To allocate the device buffers applications must initialize the +relevant fields of the v4l2_create_buffers structure. +The count field must be set to the number of +requested buffers, the memory field specifies the +requested I/O method and the reserved array must be +zeroed. + + The format field specifies the image format +that the buffers must be able to handle. The application has to fill in this +&v4l2-format;. Usually this will be done using the +VIDIOC_TRY_FMT or VIDIOC_G_FMT ioctl() +to ensure that the requested format is supported by the driver. Unsupported +formats will result in an error. + + The buffers created by this ioctl will have as minimum size the size +defined by the format.pix.sizeimage field. If the +format.pix.sizeimage field is less than the minimum +required for the given format, then sizeimage will be +increased by the driver to that minimum to allocate the buffers. If it is +larger, then the value will be used as-is. The same applies to the +sizeimage field of the +v4l2_plane_pix_format structure in the case of +multiplanar formats. When the ioctl is called with a pointer to this structure the driver will attempt to allocate up to the requested number of buffers and store the @@ -144,9 +155,9 @@ mapped I/O. EINVAL - The buffer type (type field) or the -requested I/O method (memory) is not -supported. + The buffer type (format.type field), +requested I/O method (memory) or format +(format field) is not valid. -- cgit v0.10.2 From 5548a382508f1f76320e1ed55524dda73234299d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 1 Aug 2013 21:44:38 -0300 Subject: [media] media: vb2: Clarify queue_setup() and buf_prepare() usage documentation Explain how the two operations must handle formats and validate buffer sizes when used with VIDIOC_CREATE_BUFS. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index d88a098..6781258 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -219,8 +219,9 @@ struct vb2_buffer { * configured format and *num_buffers is the total number * of buffers, that are being allocated. When called from * VIDIOC_CREATE_BUFS, fmt != NULL and it describes the - * target frame format. In this case *num_buffers are being - * allocated additionally to q->num_buffers. + * target frame format (if the format isn't valid the + * callback must return -EINVAL). In this case *num_buffers + * are being allocated additionally to q->num_buffers. * @wait_prepare: release any locks taken while calling vb2 functions; * it is called before an ioctl needs to wait for a new * buffer to arrive; required to avoid a deadlock in @@ -236,8 +237,10 @@ struct vb2_buffer { * @buf_prepare: called every time the buffer is queued from userspace * and from the VIDIOC_PREPARE_BUF ioctl; drivers may * perform any initialization required before each hardware - * operation in this callback; if an error is returned, the - * buffer will not be queued in driver; optional + * operation in this callback; drivers that support + * VIDIOC_CREATE_BUFS must also validate the buffer size; + * if an error is returned, the buffer will not be queued + * in driver; optional * @buf_finish: called before every dequeue of the buffer back to * userspace; drivers may perform any operations required * before userspace accesses the buffer; optional -- cgit v0.10.2 From 8a90f1a61c9b31bd0c89b8456fa4dce91ad69c57 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 2 Aug 2013 13:55:21 -0300 Subject: [media] media: vb2: Take queue or device lock in mmap-related vb2 ioctl handlers The vb2_fop_mmap() and vb2_fop_get_unmapped_area() functions are plug-in implementation of the mmap() and get_unmapped_area() file operations that calls vb2_mmap() and vb2_get_unmapped_area() on the queue associated with the video device. Neither the vb2_fop_mmap/vb2_fop_get_unmapped_area nor the v4l2_mmap/vb2_get_unmapped_area functions in the V4L2 core take any lock, leading to race conditions between mmap/get_unmapped_area and other buffer-related ioctls such as VIDIOC_REQBUFS. Fix it by taking the queue or device lock around the vb2_mmap() and vb2_get_unmapped_area() calls. 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 9fc4bab..c9b50c7 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -2578,8 +2578,15 @@ EXPORT_SYMBOL_GPL(vb2_ioctl_expbuf); int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma) { struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + int err; - return vb2_mmap(vdev->queue, vma); + if (lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + err = vb2_mmap(vdev->queue, vma); + if (lock) + mutex_unlock(lock); + return err; } EXPORT_SYMBOL_GPL(vb2_fop_mmap); @@ -2685,8 +2692,15 @@ unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + int ret; - return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); + if (lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + ret = vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); + if (lock) + mutex_unlock(lock); + return ret; } EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area); #endif -- cgit v0.10.2 From 0236f0905a1e5231998784de580b451d0aa17c76 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 5 Jul 2013 10:44:15 -0300 Subject: [media] v4l: Fix V4L2_MBUS_FMT_YUV10_1X30 media bus pixel code value The V4L2_MBUS_FMT_YUV10_1X30 code is documented as being equal to 0x2014, while the v4l2-mediabus.h header defines it as 0x2016. Fix the documentation. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index adc6198..0c2b1f2 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -2574,7 +2574,7 @@ V4L2_MBUS_FMT_YUV10_1X30 - 0x2014 + 0x2016 y9 y8 -- cgit v0.10.2 From f57fa2102cd5c0b50359def79a3d39cda8431204 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 5 Jun 2013 04:19:53 -0300 Subject: [media] v4l: Add media format codes for ARGB8888 and AYUV8888 on 32-bit busses Signed-off-by: Laurent Pinchart Reviewed-by: Sylwester Nawrocki Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 0c2b1f2..f72c1cc 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -97,31 +97,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Identifier @@ -133,6 +141,14 @@ Bit + 31 + 30 + 29 + 28 + 27 + 26 + 25 + 24 23 22 21 @@ -164,7 +180,7 @@ V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE 0x1001 - &dash-ent-16; + &dash-ent-24; 0 0 0 @@ -178,7 +194,7 @@ - &dash-ent-16; + &dash-ent-24; g3 g2 g1 @@ -192,7 +208,7 @@ V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE 0x1002 - &dash-ent-16; + &dash-ent-24; g3 g2 g1 @@ -206,7 +222,7 @@ - &dash-ent-16; + &dash-ent-24; 0 0 0 @@ -220,7 +236,7 @@ V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE 0x1003 - &dash-ent-16; + &dash-ent-24; 0 r4 r3 @@ -234,7 +250,7 @@ - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -248,7 +264,7 @@ V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE 0x1004 - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -262,7 +278,7 @@ - &dash-ent-16; + &dash-ent-24; 0 r4 r3 @@ -276,7 +292,7 @@ V4L2_MBUS_FMT_BGR565_2X8_BE 0x1005 - &dash-ent-16; + &dash-ent-24; b4 b3 b2 @@ -290,7 +306,7 @@ - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -304,7 +320,7 @@ V4L2_MBUS_FMT_BGR565_2X8_LE 0x1006 - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -318,7 +334,7 @@ - &dash-ent-16; + &dash-ent-24; b4 b3 b2 @@ -332,7 +348,7 @@ V4L2_MBUS_FMT_RGB565_2X8_BE 0x1007 - &dash-ent-16; + &dash-ent-24; r4 r3 r2 @@ -346,7 +362,7 @@ - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -360,7 +376,7 @@ V4L2_MBUS_FMT_RGB565_2X8_LE 0x1008 - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -374,7 +390,7 @@ - &dash-ent-16; + &dash-ent-24; r4 r3 r2 @@ -388,12 +404,7 @@ V4L2_MBUS_FMT_RGB666_1X18 0x1009 - - - - - - - - - - - - + &dash-ent-14; r5 r4 r3 @@ -417,6 +428,7 @@ V4L2_MBUS_FMT_RGB888_1X24 0x100a + &dash-ent-8; r7 r6 r5 @@ -446,9 +458,7 @@ V4L2_MBUS_FMT_RGB888_2X12_BE 0x100b - &dash-ent-10; - - - - + &dash-ent-20; r7 r6 r5 @@ -466,9 +476,7 @@ - &dash-ent-10; - - - - + &dash-ent-20; g3 g2 g1 @@ -486,9 +494,7 @@ V4L2_MBUS_FMT_RGB888_2X12_LE 0x100c - &dash-ent-10; - - - - + &dash-ent-20; g3 g2 g1 @@ -506,9 +512,7 @@ - &dash-ent-10; - - - - + &dash-ent-20; r7 r6 r5 @@ -522,6 +526,43 @@ g5 g4 + + V4L2_MBUS_FMT_ARGB888_1X32 + 0x100d + + 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 + @@ -1149,6 +1190,7 @@ yx for luma component bit number x ux for blue chroma component bit number x vx for red chroma component bit number x + ax for alpha component bit number x - for non-available bits (for positions higher than the bus width) d for dummy bits @@ -1159,37 +1201,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Identifier @@ -1201,6 +1245,8 @@ Bit + 31 + 30 29 28 27 @@ -1238,10 +1284,7 @@ V4L2_MBUS_FMT_Y8_1X8 0x2001 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1255,18 +1298,7 @@ V4L2_MBUS_FMT_UV8_1X8 0x2015 - - - - - - - - - - - - - - - - - - - - - - - - + &dash-ent-24; u7 u6 u5 @@ -1280,18 +1312,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - + &dash-ent-24; v7 v6 v5 @@ -1305,10 +1326,7 @@ V4L2_MBUS_FMT_UYVY8_1_5X8 0x2002 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1322,10 +1340,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1339,10 +1354,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1356,10 +1368,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1373,10 +1382,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1390,10 +1396,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1407,10 +1410,7 @@ V4L2_MBUS_FMT_VYUY8_1_5X8 0x2003 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1424,10 +1424,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1441,10 +1438,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1458,10 +1452,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1475,10 +1466,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1492,10 +1480,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1509,10 +1494,7 @@ V4L2_MBUS_FMT_YUYV8_1_5X8 0x2004 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1526,10 +1508,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1543,10 +1522,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1560,10 +1536,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1577,10 +1550,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1594,10 +1564,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1611,10 +1578,7 @@ V4L2_MBUS_FMT_YVYU8_1_5X8 0x2005 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1628,10 +1592,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1645,10 +1606,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1662,10 +1620,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1679,10 +1634,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1696,10 +1648,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1713,10 +1662,7 @@ V4L2_MBUS_FMT_UYVY8_2X8 0x2006 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1730,10 +1676,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1747,10 +1690,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1764,10 +1704,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1781,10 +1718,7 @@ V4L2_MBUS_FMT_VYUY8_2X8 0x2007 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1798,10 +1732,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1815,10 +1746,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1832,10 +1760,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1849,10 +1774,7 @@ V4L2_MBUS_FMT_YUYV8_2X8 0x2008 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1866,10 +1788,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1883,10 +1802,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1900,10 +1816,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1917,10 +1830,7 @@ V4L2_MBUS_FMT_YVYU8_2X8 0x2009 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1934,10 +1844,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1951,10 +1858,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1968,10 +1872,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1985,8 +1886,7 @@ V4L2_MBUS_FMT_Y10_1X10 0x200a - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; y9 y8 y7 @@ -2002,8 +1902,7 @@ V4L2_MBUS_FMT_YUYV10_2X10 0x200b - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; y9 y8 y7 @@ -2019,8 +1918,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; u9 u8 u7 @@ -2036,8 +1934,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; y9 y8 y7 @@ -2053,8 +1950,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; v9 v8 v7 @@ -2070,8 +1966,7 @@ V4L2_MBUS_FMT_YVYU10_2X10 0x200c - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; y9 y8 y7 @@ -2087,8 +1982,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; v9 v8 v7 @@ -2104,8 +1998,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; y9 y8 y7 @@ -2121,8 +2014,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; u9 u8 u7 @@ -2138,15 +2030,7 @@ V4L2_MBUS_FMT_Y12_1X12 0x2013 - &dash-ent-10; - - - - - - - - - - - - - - - - + &dash-ent-20; y11 y10 y9 @@ -2164,11 +2048,7 @@ V4L2_MBUS_FMT_UYVY8_1X16 0x200f - &dash-ent-10; - - - - - - - - + &dash-ent-16; u7 u6 u5 @@ -2190,11 +2070,7 @@ - &dash-ent-10; - - - - - - - - + &dash-ent-16; v7 v6 v5 @@ -2216,11 +2092,7 @@ V4L2_MBUS_FMT_VYUY8_1X16 0x2010 - &dash-ent-10; - - - - - - - - + &dash-ent-16; v7 v6 v5 @@ -2242,11 +2114,7 @@ - &dash-ent-10; - - - - - - - - + &dash-ent-16; u7 u6 u5 @@ -2268,11 +2136,7 @@ V4L2_MBUS_FMT_YUYV8_1X16 0x2011 - &dash-ent-10; - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2294,11 +2158,7 @@ - &dash-ent-10; - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2320,11 +2180,7 @@ V4L2_MBUS_FMT_YVYU8_1X16 0x2012 - &dash-ent-10; - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2346,11 +2202,7 @@ - &dash-ent-10; - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2372,10 +2224,7 @@ V4L2_MBUS_FMT_YDYUYDYV8_1X16 0x2014 - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2397,10 +2246,7 @@ - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2422,10 +2268,7 @@ - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2447,10 +2290,7 @@ - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2472,7 +2312,7 @@ V4L2_MBUS_FMT_YUYV10_1X20 0x200d - &dash-ent-10; + &dash-ent-12; y9 y8 y7 @@ -2498,7 +2338,7 @@ - &dash-ent-10; + &dash-ent-12; y9 y8 y7 @@ -2524,7 +2364,7 @@ V4L2_MBUS_FMT_YVYU10_1X20 0x200e - &dash-ent-10; + &dash-ent-12; y9 y8 y7 @@ -2550,7 +2390,7 @@ - &dash-ent-10; + &dash-ent-12; y9 y8 y7 @@ -2576,6 +2416,8 @@ V4L2_MBUS_FMT_YUV10_1X30 0x2016 + - + - y9 y8 y7 @@ -2607,6 +2449,43 @@ v1 v0 + + V4L2_MBUS_FMT_AYUV8_1X32 + 0x2017 + + a7 + a6 + a5 + a4 + a3 + a2 + a1 + a0 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl index 6a8b715..07e7eea 100644 --- a/Documentation/DocBook/media_api.tmpl +++ b/Documentation/DocBook/media_api.tmpl @@ -22,8 +22,14 @@ http://linuxtv.org/repo/"> +--------"> ----------"> +------------"> +--------------"> ----------------"> +--------------------"> +----------------------"> +------------------------"> ]> diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 6ee63d0..a960125 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -37,7 +37,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_FIXED = 0x0001, - /* RGB - next is 0x100d */ + /* RGB - next is 0x100e */ V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE = 0x1001, V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE = 0x1002, V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE = 0x1003, @@ -50,8 +50,9 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB888_1X24 = 0x100a, V4L2_MBUS_FMT_RGB888_2X12_BE = 0x100b, V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c, + V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d, - /* YUV (including grey) - next is 0x2017 */ + /* YUV (including grey) - next is 0x2018 */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, @@ -74,6 +75,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, V4L2_MBUS_FMT_YUV10_1X30 = 0x2016, + V4L2_MBUS_FMT_AYUV8_1X32 = 0x2017, /* Bayer - next is 0x3019 */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, -- cgit v0.10.2 From 8493054844145e47a8f0668f57e2b0073aca850f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Jun 2013 09:46:42 -0300 Subject: [media] v4l: Add V4L2_PIX_FMT_NV16M and V4L2_PIX_FMT_NV61M formats NV16M and NV61M are planar YCbCr 4:2:2 and YCrCb 4:2:2 formats with a luma plane followed by an interleaved chroma plane. The planes are not required to be contiguous in memory, and the formats can only be used with the multi-planar formats API. Signed-off-by: Laurent Pinchart Reviewed-by: Sylwester Nawrocki Reviewed-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/pixfmt-nv16m.xml b/Documentation/DocBook/media/v4l/pixfmt-nv16m.xml new file mode 100644 index 0000000..c51d5a4 --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-nv16m.xml @@ -0,0 +1,171 @@ + + + V4L2_PIX_FMT_NV16M ('NM16'), V4L2_PIX_FMT_NV61M ('NM61') + &manvol; + + + V4L2_PIX_FMT_NV16M + V4L2_PIX_FMT_NV61M + Variation of V4L2_PIX_FMT_NV16 and V4L2_PIX_FMT_NV61 with planes + non contiguous in memory. + + + Description + + This is a multi-planar, two-plane version of the YUV 4:2:0 format. +The three components are separated into two sub-images or planes. +V4L2_PIX_FMT_NV16M differs from V4L2_PIX_FMT_NV16 + in that the two planes are non-contiguous in memory, i.e. the chroma +plane does not necessarily immediately follows the luma plane. +The luminance data occupies the first plane. The Y plane has one byte per pixel. +In the second plane there is chrominance data with alternating chroma samples. +The CbCr plane is the same width and height, in bytes, as the Y plane. +Each CbCr pair belongs to four pixels. For example, +Cb0/Cr0 belongs to +Y'00, Y'01, +Y'10, Y'11. +V4L2_PIX_FMT_NV61M is the same as V4L2_PIX_FMT_NV16M +except the Cb and Cr bytes are swapped, the CrCb plane starts with a Cr byte. + + V4L2_PIX_FMT_NV16M and +V4L2_PIX_FMT_NV61M are intended to be used only in drivers +and applications that support the multi-planar API, described in +. + + + <constant>V4L2_PIX_FMT_NV16M</constant> 4 × 4 pixel image + + + Byte Order. + Each cell is one byte. + + + + + + start0 + 0: + Y'00 + Y'01 + Y'02 + Y'03 + + + start0 + 4: + Y'10 + Y'11 + Y'12 + Y'13 + + + start0 + 8: + Y'20 + Y'21 + Y'22 + Y'23 + + + start0 + 12: + Y'30 + Y'31 + Y'32 + Y'33 + + + + + + start1 + 0: + Cb00 + Cr00 + Cb02 + Cr02 + + + start1 + 4: + Cb10 + Cr10 + Cb12 + Cr12 + + + start1 + 8: + Cb20 + Cr20 + Cb22 + Cr22 + + + start1 + 12: + Cb30 + Cr30 + Cb32 + Cr32 + + + + + + + + + Color Sample Location. + + + + + + + 01 + 23 + + + 0 + YY + YY + + + + C + C + + + 1 + YY + YY + + + + C + C + + + + + + 2 + YY + YY + + + + C + C + + + 3 + YY + YY + + + + C + C + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index 99b8d2a..16db350 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -718,6 +718,7 @@ information. &sub-nv12m; &sub-nv12mt; &sub-nv16; + &sub-nv16m; &sub-nv24; &sub-m420;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 95ef455..fec0c20 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -348,6 +348,8 @@ struct v4l2_pix_format { /* two non contiguous planes - one Y, one Cr + Cb interleaved */ #define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */ #define V4L2_PIX_FMT_NV21M v4l2_fourcc('N', 'M', '2', '1') /* 21 Y/CrCb 4:2:0 */ +#define V4L2_PIX_FMT_NV16M v4l2_fourcc('N', 'M', '1', '6') /* 16 Y/CbCr 4:2:2 */ +#define V4L2_PIX_FMT_NV61M v4l2_fourcc('N', 'M', '6', '1') /* 16 Y/CrCb 4:2:2 */ #define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 macroblocks */ #define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 16x16 macroblocks */ -- cgit v0.10.2 From 26e0ca22c3b85b04f693dd0422f13a61846ccfa9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Jun 2013 11:22:30 -0300 Subject: [media] v4l: Renesas R-Car VSP1 driver The VSP1 is a video processing engine that includes a blender, scalers, filters and statistics computation. Configurable data path routing logic allows ordering the internal blocks in a flexible way. Due to the configurable nature of the pipeline the driver implements the media controller API and doesn't use the V4L2 mem-to-mem framework, even though the device usually operates in memory to memory mode. Only the read pixel formatters, up/down scalers, write pixel formatters and LCDC interface are supported at this stage. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 08de865..9a44e06 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -210,6 +210,16 @@ config VIDEO_SH_VEU Support for the Video Engine Unit (VEU) on SuperH and SH-Mobile SoCs. +config VIDEO_RENESAS_VSP1 + tristate "Renesas VSP1 Video Processing Engine" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + ---help--- + This is a V4L2 driver for the Renesas VSP1 video processing engine. + + To compile this driver as a module, choose M here: the module + will be called vsp1. + endif # V4L_MEM2MEM_DRIVERS menuconfig V4L_TEST_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index eee28dd..4e4da48 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -46,6 +46,8 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o obj-$(CONFIG_SOC_CAMERA) += soc_camera/ +obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ + obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile new file mode 100644 index 0000000..4da2261 --- /dev/null +++ b/drivers/media/platform/vsp1/Makefile @@ -0,0 +1,5 @@ +vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_video.o +vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o +vsp1-y += vsp1_lif.o vsp1_uds.o + +obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h new file mode 100644 index 0000000..11ac94b --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1.h @@ -0,0 +1,73 @@ +/* + * vsp1.h -- R-Car VSP1 Driver + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_H__ +#define __VSP1_H__ + +#include +#include +#include +#include + +#include +#include +#include + +#include "vsp1_regs.h" + +struct clk; +struct device; + +struct vsp1_platform_data; +struct vsp1_lif; +struct vsp1_rwpf; +struct vsp1_uds; + +#define VPS1_MAX_RPF 5 +#define VPS1_MAX_UDS 3 +#define VPS1_MAX_WPF 4 + +struct vsp1_device { + struct device *dev; + struct vsp1_platform_data *pdata; + + void __iomem *mmio; + struct clk *clock; + + struct mutex lock; + int ref_count; + + struct vsp1_lif *lif; + struct vsp1_rwpf *rpf[VPS1_MAX_RPF]; + struct vsp1_uds *uds[VPS1_MAX_UDS]; + struct vsp1_rwpf *wpf[VPS1_MAX_WPF]; + + struct list_head entities; + + struct v4l2_device v4l2_dev; + struct media_device media_dev; +}; + +struct vsp1_device *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) +{ + return ioread32(vsp1->mmio + reg); +} + +static inline void vsp1_write(struct vsp1_device *vsp1, u32 reg, u32 data) +{ + iowrite32(data, vsp1->mmio + reg); +} + +#endif /* __VSP1_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c new file mode 100644 index 0000000..e58e49c --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -0,0 +1,492 @@ +/* + * vsp1_drv.c -- R-Car VSP1 Driver + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "vsp1.h" +#include "vsp1_lif.h" +#include "vsp1_rwpf.h" +#include "vsp1_uds.h" + +/* ----------------------------------------------------------------------------- + * Interrupt Handling + */ + +static irqreturn_t vsp1_irq_handler(int irq, void *data) +{ + u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE; + struct vsp1_device *vsp1 = data; + irqreturn_t ret = IRQ_NONE; + unsigned int i; + + for (i = 0; i < VPS1_MAX_WPF; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + u32 status; + + if (wpf == NULL) + continue; + + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i)); + vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask); + + if (status & VI6_WFP_IRQ_STA_FRE) { + vsp1_pipeline_frame_end(pipe); + ret = IRQ_HANDLED; + } + } + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Entities + */ + +/* + * vsp1_create_links - Create links from all sources to the given sink + * + * This function creates media links from all valid sources to the given sink + * pad. Links that would be invalid according to the VSP1 hardware capabilities + * are skipped. Those include all links + * + * - from a UDS to a UDS (UDS entities can't be chained) + * - from an entity to itself (no loops are allowed) + */ +static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink) +{ + struct media_entity *entity = &sink->subdev.entity; + struct vsp1_entity *source; + unsigned int pad; + int ret; + + list_for_each_entry(source, &vsp1->entities, list_dev) { + u32 flags; + + if (source->type == sink->type) + continue; + + if (source->type == VSP1_ENTITY_LIF || + source->type == VSP1_ENTITY_WPF) + continue; + + flags = source->type == VSP1_ENTITY_RPF && + sink->type == VSP1_ENTITY_WPF && + source->index == sink->index + ? MEDIA_LNK_FL_ENABLED : 0; + + for (pad = 0; pad < entity->num_pads; ++pad) { + if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK)) + continue; + + ret = media_entity_create_link(&source->subdev.entity, + source->source_pad, + entity, pad, flags); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static void vsp1_destroy_entities(struct vsp1_device *vsp1) +{ + struct vsp1_entity *entity; + struct vsp1_entity *next; + + list_for_each_entry_safe(entity, next, &vsp1->entities, list_dev) { + list_del(&entity->list_dev); + vsp1_entity_destroy(entity); + } + + v4l2_device_unregister(&vsp1->v4l2_dev); + media_device_unregister(&vsp1->media_dev); +} + +static int vsp1_create_entities(struct vsp1_device *vsp1) +{ + struct media_device *mdev = &vsp1->media_dev; + struct v4l2_device *vdev = &vsp1->v4l2_dev; + struct vsp1_entity *entity; + unsigned int i; + int ret; + + mdev->dev = vsp1->dev; + strlcpy(mdev->model, "VSP1", sizeof(mdev->model)); + ret = media_device_register(mdev); + if (ret < 0) { + dev_err(vsp1->dev, "media device registration failed (%d)\n", + ret); + return ret; + } + + vdev->mdev = mdev; + ret = v4l2_device_register(vsp1->dev, vdev); + if (ret < 0) { + dev_err(vsp1->dev, "V4L2 device registration failed (%d)\n", + ret); + goto done; + } + + /* Instantiate all the entities. */ + if (vsp1->pdata->features & VSP1_HAS_LIF) { + vsp1->lif = vsp1_lif_create(vsp1); + if (IS_ERR(vsp1->lif)) { + ret = PTR_ERR(vsp1->lif); + goto done; + } + + list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities); + } + + for (i = 0; i < vsp1->pdata->rpf_count; ++i) { + struct vsp1_rwpf *rpf; + + rpf = vsp1_rpf_create(vsp1, i); + if (IS_ERR(rpf)) { + ret = PTR_ERR(rpf); + goto done; + } + + vsp1->rpf[i] = rpf; + list_add_tail(&rpf->entity.list_dev, &vsp1->entities); + } + + for (i = 0; i < vsp1->pdata->uds_count; ++i) { + struct vsp1_uds *uds; + + uds = vsp1_uds_create(vsp1, i); + if (IS_ERR(uds)) { + ret = PTR_ERR(uds); + goto done; + } + + vsp1->uds[i] = uds; + list_add_tail(&uds->entity.list_dev, &vsp1->entities); + } + + for (i = 0; i < vsp1->pdata->wpf_count; ++i) { + struct vsp1_rwpf *wpf; + + wpf = vsp1_wpf_create(vsp1, i); + if (IS_ERR(wpf)) { + ret = PTR_ERR(wpf); + goto done; + } + + vsp1->wpf[i] = wpf; + list_add_tail(&wpf->entity.list_dev, &vsp1->entities); + } + + /* Create links. */ + list_for_each_entry(entity, &vsp1->entities, list_dev) { + if (entity->type == VSP1_ENTITY_LIF || + entity->type == VSP1_ENTITY_RPF) + continue; + + ret = vsp1_create_links(vsp1, entity); + if (ret < 0) + goto done; + } + + if (vsp1->pdata->features & VSP1_HAS_LIF) { + ret = media_entity_create_link( + &vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE, + &vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0); + if (ret < 0) + return ret; + } + + /* Register all subdevs. */ + list_for_each_entry(entity, &vsp1->entities, list_dev) { + ret = v4l2_device_register_subdev(&vsp1->v4l2_dev, + &entity->subdev); + if (ret < 0) + goto done; + } + + ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev); + +done: + if (ret < 0) + vsp1_destroy_entities(vsp1); + + return ret; +} + +static int vsp1_device_init(struct vsp1_device *vsp1) +{ + unsigned int i; + u32 status; + + /* Reset any channel that might be running. */ + status = vsp1_read(vsp1, VI6_STATUS); + + for (i = 0; i < VPS1_MAX_WPF; ++i) { + unsigned int timeout; + + if (!(status & VI6_STATUS_SYS_ACT(i))) + continue; + + vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(i)); + for (timeout = 10; timeout > 0; --timeout) { + status = vsp1_read(vsp1, VI6_STATUS); + if (!(status & VI6_STATUS_SYS_ACT(i))) + break; + + usleep_range(1000, 2000); + } + + if (!timeout) { + dev_err(vsp1->dev, "failed to reset wpf.%u\n", i); + return -ETIMEDOUT; + } + } + + vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) | + (8 << VI6_CLK_DCSWT_CSTRW_SHIFT)); + + for (i = 0; i < VPS1_MAX_RPF; ++i) + vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED); + + for (i = 0; i < VPS1_MAX_UDS; ++i) + vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED); + + vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_HST_ROUTE, VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED); + + vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | + (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT)); + vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | + (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT)); + + return 0; +} + +/* + * vsp1_device_get - Acquire the VSP1 device + * + * 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 occured. + */ +struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1) +{ + struct vsp1_device *__vsp1 = vsp1; + int ret; + + mutex_lock(&vsp1->lock); + if (vsp1->ref_count > 0) + goto done; + + ret = clk_prepare_enable(vsp1->clock); + if (ret < 0) { + __vsp1 = NULL; + goto done; + } + + ret = vsp1_device_init(vsp1); + if (ret < 0) { + clk_disable_unprepare(vsp1->clock); + __vsp1 = NULL; + goto done; + } + +done: + if (__vsp1) + vsp1->ref_count++; + + mutex_unlock(&vsp1->lock); + return __vsp1; +} + +/* + * vsp1_device_put - Release the VSP1 device + * + * Decrement the VSP1 reference count and cleanup the device if the last + * reference is released. + */ +void vsp1_device_put(struct vsp1_device *vsp1) +{ + mutex_lock(&vsp1->lock); + + if (--vsp1->ref_count == 0) + clk_disable_unprepare(vsp1->clock); + + mutex_unlock(&vsp1->lock); +} + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +#ifdef CONFIG_PM_SLEEP +static int vsp1_pm_suspend(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + + WARN_ON(mutex_is_locked(&vsp1->lock)); + + if (vsp1->ref_count == 0) + return 0; + + clk_disable_unprepare(vsp1->clock); + return 0; +} + +static int vsp1_pm_resume(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + + WARN_ON(mutex_is_locked(&vsp1->lock)); + + if (vsp1->ref_count) + return 0; + + return clk_prepare_enable(vsp1->clock); +} +#endif + +static const struct dev_pm_ops vsp1_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume) +}; + +/* ----------------------------------------------------------------------------- + * Platform Driver + */ + +static struct vsp1_platform_data * +vsp1_get_platform_data(struct platform_device *pdev) +{ + struct vsp1_platform_data *pdata = pdev->dev.platform_data; + + if (pdata == NULL) { + dev_err(&pdev->dev, "missing platform data\n"); + return NULL; + } + + if (pdata->rpf_count <= 0 || pdata->rpf_count > VPS1_MAX_RPF) { + dev_err(&pdev->dev, "invalid number of RPF (%u)\n", + pdata->rpf_count); + return NULL; + } + + if (pdata->uds_count <= 0 || pdata->uds_count > VPS1_MAX_UDS) { + dev_err(&pdev->dev, "invalid number of UDS (%u)\n", + pdata->uds_count); + return NULL; + } + + if (pdata->wpf_count <= 0 || pdata->wpf_count > VPS1_MAX_WPF) { + dev_err(&pdev->dev, "invalid number of WPF (%u)\n", + pdata->wpf_count); + return NULL; + } + + return pdata; +} + +static int vsp1_probe(struct platform_device *pdev) +{ + struct vsp1_device *vsp1; + struct resource *irq; + struct resource *io; + int ret; + + vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL); + if (vsp1 == NULL) + return -ENOMEM; + + vsp1->dev = &pdev->dev; + mutex_init(&vsp1->lock); + INIT_LIST_HEAD(&vsp1->entities); + + vsp1->pdata = vsp1_get_platform_data(pdev); + if (vsp1->pdata == NULL) + return -ENODEV; + + /* I/O, IRQ and clock resources */ + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + vsp1->mmio = devm_ioremap_resource(&pdev->dev, io); + if (IS_ERR((void *)vsp1->mmio)) + return PTR_ERR((void *)vsp1->mmio); + + vsp1->clock = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(vsp1->clock)) { + dev_err(&pdev->dev, "failed to get clock\n"); + return PTR_ERR(vsp1->clock); + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "missing IRQ\n"); + return -EINVAL; + } + + ret = devm_request_irq(&pdev->dev, irq->start, vsp1_irq_handler, + IRQF_SHARED, dev_name(&pdev->dev), vsp1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + return ret; + } + + /* Instanciate entities */ + ret = vsp1_create_entities(vsp1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to create entities\n"); + return ret; + } + + platform_set_drvdata(pdev, vsp1); + + return 0; +} + +static int vsp1_remove(struct platform_device *pdev) +{ + struct vsp1_device *vsp1 = platform_get_drvdata(pdev); + + vsp1_destroy_entities(vsp1); + + return 0; +} + +static struct platform_driver vsp1_platform_driver = { + .probe = vsp1_probe, + .remove = vsp1_remove, + .driver = { + .owner = THIS_MODULE, + .name = "vsp1", + .pm = &vsp1_pm_ops, + }, +}; + +module_platform_driver(vsp1_platform_driver); + +MODULE_ALIAS("vsp1"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("Renesas VSP1 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c new file mode 100644 index 0000000..9028f9d --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -0,0 +1,181 @@ +/* + * vsp1_entity.c -- R-Car VSP1 Base Entity + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include + +#include "vsp1.h" +#include "vsp1_entity.h" + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +struct v4l2_mbus_framefmt * +vsp1_entity_get_pad_format(struct vsp1_entity *entity, + struct v4l2_subdev_fh *fh, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &entity->formats[pad]; + default: + return NULL; + } +} + +/* + * vsp1_entity_init_formats - Initialize formats on all pads + * @subdev: V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +void vsp1_entity_init_formats(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + unsigned int pad; + + for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { + memset(&format, 0, sizeof(format)); + + format.pad = pad; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE; + + v4l2_subdev_call(subdev, pad, set_fmt, fh, &format); + } +} + +static int vsp1_entity_open(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + vsp1_entity_init_formats(subdev, fh); + + return 0; +} + +const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = { + .open = vsp1_entity_open, +}; + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static int vsp1_entity_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct vsp1_entity *source; + + if (!(local->flags & MEDIA_PAD_FL_SOURCE)) + return 0; + + source = container_of(local->entity, struct vsp1_entity, subdev.entity); + + if (!source->route) + return 0; + + if (flags & MEDIA_LNK_FL_ENABLED) { + if (source->sink) + return -EBUSY; + source->sink = remote->entity; + } else { + source->sink = NULL; + } + + return 0; +} + +const struct media_entity_operations vsp1_media_ops = { + .link_setup = vsp1_entity_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, + unsigned int num_pads) +{ + static const struct { + unsigned int id; + unsigned int reg; + } routes[] = { + { VI6_DPR_NODE_LIF, 0 }, + { VI6_DPR_NODE_RPF(0), VI6_DPR_RPF_ROUTE(0) }, + { VI6_DPR_NODE_RPF(1), VI6_DPR_RPF_ROUTE(1) }, + { VI6_DPR_NODE_RPF(2), VI6_DPR_RPF_ROUTE(2) }, + { VI6_DPR_NODE_RPF(3), VI6_DPR_RPF_ROUTE(3) }, + { VI6_DPR_NODE_RPF(4), VI6_DPR_RPF_ROUTE(4) }, + { VI6_DPR_NODE_UDS(0), VI6_DPR_UDS_ROUTE(0) }, + { VI6_DPR_NODE_UDS(1), VI6_DPR_UDS_ROUTE(1) }, + { VI6_DPR_NODE_UDS(2), VI6_DPR_UDS_ROUTE(2) }, + { VI6_DPR_NODE_WPF(0), 0 }, + { VI6_DPR_NODE_WPF(1), 0 }, + { VI6_DPR_NODE_WPF(2), 0 }, + { VI6_DPR_NODE_WPF(3), 0 }, + }; + + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(routes); ++i) { + if (routes[i].id == entity->id) { + entity->route = routes[i].reg; + break; + } + } + + if (i == ARRAY_SIZE(routes)) + return -EINVAL; + + entity->vsp1 = vsp1; + entity->source_pad = num_pads - 1; + + /* Allocate formats and pads. */ + entity->formats = devm_kzalloc(vsp1->dev, + num_pads * sizeof(*entity->formats), + GFP_KERNEL); + if (entity->formats == NULL) + return -ENOMEM; + + entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads), + GFP_KERNEL); + if (entity->pads == NULL) + return -ENOMEM; + + /* Initialize pads. */ + for (i = 0; i < num_pads - 1; ++i) + entity->pads[i].flags = MEDIA_PAD_FL_SINK; + + entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE; + + /* Initialize the media entity. */ + return media_entity_init(&entity->subdev.entity, num_pads, + entity->pads, 0); +} + +void vsp1_entity_destroy(struct vsp1_entity *entity) +{ + media_entity_cleanup(&entity->subdev.entity); +} diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h new file mode 100644 index 0000000..c4feab2c --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -0,0 +1,68 @@ +/* + * vsp1_entity.h -- R-Car VSP1 Base Entity + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_ENTITY_H__ +#define __VSP1_ENTITY_H__ + +#include + +#include + +struct vsp1_device; + +enum vsp1_entity_type { + VSP1_ENTITY_LIF, + VSP1_ENTITY_RPF, + VSP1_ENTITY_UDS, + VSP1_ENTITY_WPF, +}; + +struct vsp1_entity { + struct vsp1_device *vsp1; + + enum vsp1_entity_type type; + unsigned int index; + unsigned int id; + unsigned int route; + + struct list_head list_dev; + struct list_head list_pipe; + + struct media_pad *pads; + unsigned int source_pad; + + struct media_entity *sink; + + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt *formats; +}; + +static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_entity, subdev); +} + +int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, + unsigned int num_pads); +void vsp1_entity_destroy(struct vsp1_entity *entity); + +extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops; +extern const struct media_entity_operations vsp1_media_ops; + +struct v4l2_mbus_framefmt * +vsp1_entity_get_pad_format(struct vsp1_entity *entity, + struct v4l2_subdev_fh *fh, + unsigned int pad, u32 which); +void vsp1_entity_init_formats(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh); + +#endif /* __VSP1_ENTITY_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c new file mode 100644 index 0000000..74a32e6 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -0,0 +1,238 @@ +/* + * vsp1_lif.c -- R-Car VSP1 LCD Controller Interface + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include + +#include "vsp1.h" +#include "vsp1_lif.h" + +#define LIF_MIN_SIZE 2U +#define LIF_MAX_SIZE 2048U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_lif_read(struct vsp1_lif *lif, u32 reg) +{ + return vsp1_read(lif->entity.vsp1, reg); +} + +static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data) +{ + vsp1_write(lif->entity.vsp1, reg, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static int lif_s_stream(struct v4l2_subdev *subdev, int enable) +{ + const struct v4l2_mbus_framefmt *format; + struct vsp1_lif *lif = to_lif(subdev); + unsigned int hbth = 1300; + unsigned int obth = 400; + unsigned int lbth = 200; + + if (!enable) { + vsp1_lif_write(lif, VI6_LIF_CTRL, 0); + return 0; + } + + format = &lif->entity.formats[LIF_PAD_SOURCE]; + + obth = min(obth, (format->width + 1) / 2 * format->height - 4); + + vsp1_lif_write(lif, VI6_LIF_CSBTH, + (hbth << VI6_LIF_CSBTH_HBTH_SHIFT) | + (lbth << VI6_LIF_CSBTH_LBTH_SHIFT)); + + vsp1_lif_write(lif, VI6_LIF_CTRL, + (obth << VI6_LIF_CTRL_OBTH_SHIFT) | + (format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) | + VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int lif_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const unsigned int codes[] = { + V4L2_MBUS_FMT_ARGB8888_1X32, + V4L2_MBUS_FMT_AYUV8_1X32, + }; + + if (code->pad == LIF_PAD_SINK) { + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = codes[code->index]; + } else { + struct v4l2_mbus_framefmt *format; + + /* The LIF can't perform format conversion, the sink format is + * always identical to the source format. + */ + if (code->index) + return -EINVAL; + + format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK); + code->code = format->code; + } + + return 0; +} + +static int lif_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == LIF_PAD_SINK) { + fse->min_width = LIF_MIN_SIZE; + fse->max_width = LIF_MAX_SIZE; + fse->min_height = LIF_MIN_SIZE; + fse->max_height = LIF_MAX_SIZE; + } else { + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} + +static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_lif *lif = to_lif(subdev); + + fmt->format = *vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad, + fmt->which); + + return 0; +} + +static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_lif *lif = to_lif(subdev); + struct v4l2_mbus_framefmt *format; + + /* Default to YUV if the requested format is not supported. */ + if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 && + fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32) + fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32; + + format = vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad, + fmt->which); + + if (fmt->pad == LIF_PAD_SOURCE) { + /* The LIF source format is always identical to its sink + * format. + */ + fmt->format = *format; + return 0; + } + + format->code = fmt->format.code; + format->width = clamp_t(unsigned int, fmt->format.width, + LIF_MIN_SIZE, LIF_MAX_SIZE); + format->height = clamp_t(unsigned int, fmt->format.height, + LIF_MIN_SIZE, LIF_MAX_SIZE); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&lif->entity, fh, LIF_PAD_SOURCE, + fmt->which); + *format = fmt->format; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops lif_video_ops = { + .s_stream = lif_s_stream, +}; + +static struct v4l2_subdev_pad_ops lif_pad_ops = { + .enum_mbus_code = lif_enum_mbus_code, + .enum_frame_size = lif_enum_frame_size, + .get_fmt = lif_get_format, + .set_fmt = lif_set_format, +}; + +static struct v4l2_subdev_ops lif_ops = { + .video = &lif_video_ops, + .pad = &lif_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1) +{ + struct v4l2_subdev *subdev; + struct vsp1_lif *lif; + int ret; + + lif = devm_kzalloc(vsp1->dev, sizeof(*lif), GFP_KERNEL); + if (lif == NULL) + return ERR_PTR(-ENOMEM); + + lif->entity.type = VSP1_ENTITY_LIF; + lif->entity.id = VI6_DPR_NODE_LIF; + + ret = vsp1_entity_init(vsp1, &lif->entity, 2); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &lif->entity.subdev; + v4l2_subdev_init(subdev, &lif_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s lif", + dev_name(vsp1->dev)); + v4l2_set_subdevdata(subdev, lif); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + return lif; +} diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/vsp1/vsp1_lif.h new file mode 100644 index 0000000..89b93af --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_lif.h @@ -0,0 +1,37 @@ +/* + * vsp1_lif.h -- R-Car VSP1 LCD Controller Interface + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_LIF_H__ +#define __VSP1_LIF_H__ + +#include +#include + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define LIF_PAD_SINK 0 +#define LIF_PAD_SOURCE 1 + +struct vsp1_lif { + struct vsp1_entity entity; +}; + +static inline struct vsp1_lif *to_lif(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_lif, entity.subdev); +} + +struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_LIF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h new file mode 100644 index 0000000..1d3304f --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -0,0 +1,581 @@ +/* + * vsp1_regs.h -- R-Car VSP1 Registers Definitions + * + * Copyright (C) 2013 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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 __VSP1_REGS_H__ +#define __VSP1_REGS_H__ + +/* ----------------------------------------------------------------------------- + * General Control Registers + */ + +#define VI6_CMD(n) (0x0000 + (n) * 4) +#define VI6_CMD_STRCMD (1 << 0) + +#define VI6_CLK_DCSWT 0x0018 +#define VI6_CLK_DCSWT_CSTPW_MASK (0xff << 8) +#define VI6_CLK_DCSWT_CSTPW_SHIFT 8 +#define VI6_CLK_DCSWT_CSTRW_MASK (0xff << 0) +#define VI6_CLK_DCSWT_CSTRW_SHIFT 0 + +#define VI6_SRESET 0x0028 +#define VI6_SRESET_SRTS(n) (1 << (n)) + +#define VI6_STATUS 0x0038 +#define VI6_STATUS_SYS_ACT(n) (1 << ((n) + 8)) + +#define VI6_WPF_IRQ_ENB(n) (0x0048 + (n) * 12) +#define VI6_WFP_IRQ_ENB_DFEE (1 << 1) +#define VI6_WFP_IRQ_ENB_FREE (1 << 0) + +#define VI6_WPF_IRQ_STA(n) (0x004c + (n) * 12) +#define VI6_WFP_IRQ_STA_DFE (1 << 1) +#define VI6_WFP_IRQ_STA_FRE (1 << 0) + +#define VI6_DISP_IRQ_ENB 0x0078 +#define VI6_DISP_IRQ_ENB_DSTE (1 << 8) +#define VI6_DISP_IRQ_ENB_MAEE (1 << 5) +#define VI6_DISP_IRQ_ENB_LNEE(n) (1 << ((n) + 4)) + +#define VI6_DISP_IRQ_STA 0x007c +#define VI6_DISP_IRQ_STA_DSE (1 << 8) +#define VI6_DISP_IRQ_STA_MAE (1 << 5) +#define VI6_DISP_IRQ_STA_LNE(n) (1 << ((n) + 4)) + +#define VI6_WPF_LINE_COUNT(n) (0x0084 + (n) * 4) +#define VI6_WPF_LINE_COUNT_MASK (0x1fffff << 0) + +/* ----------------------------------------------------------------------------- + * Display List Control Registers + */ + +#define VI6_DL_CTRL 0x0100 +#define VI6_DL_CTRL_AR_WAIT_MASK (0xffff << 16) +#define VI6_DL_CTRL_AR_WAIT_SHIFT 16 +#define VI6_DL_CTRL_DC2 (1 << 12) +#define VI6_DL_CTRL_DC1 (1 << 8) +#define VI6_DL_CTRL_DC0 (1 << 4) +#define VI6_DL_CTRL_CFM0 (1 << 2) +#define VI6_DL_CTRL_NH0 (1 << 1) +#define VI6_DL_CTRL_DLE (1 << 0) + +#define VI6_DL_HDR_ADDR(n) (0x0104 + (n) * 4) + +#define VI6_DL_SWAP 0x0114 +#define VI6_DL_SWAP_LWS (1 << 2) +#define VI6_DL_SWAP_WDS (1 << 1) +#define VI6_DL_SWAP_BTS (1 << 0) + +#define VI6_DL_EXT_CTRL 0x011c +#define VI6_DL_EXT_CTRL_NWE (1 << 16) +#define VI6_DL_EXT_CTRL_POLINT_MASK (0x3f << 8) +#define VI6_DL_EXT_CTRL_POLINT_SHIFT 8 +#define VI6_DL_EXT_CTRL_DLPRI (1 << 5) +#define VI6_DL_EXT_CTRL_EXPRI (1 << 4) +#define VI6_DL_EXT_CTRL_EXT (1 << 0) + +#define VI6_DL_BODY_SIZE 0x0120 +#define VI6_DL_BODY_SIZE_UPD (1 << 24) +#define VI6_DL_BODY_SIZE_BS_MASK (0x1ffff << 0) +#define VI6_DL_BODY_SIZE_BS_SHIFT 0 + +/* ----------------------------------------------------------------------------- + * RPF Control Registers + */ + +#define VI6_RPF_OFFSET 0x100 + +#define VI6_RPF_SRC_BSIZE 0x0300 +#define VI6_RPF_SRC_BSIZE_BHSIZE_MASK (0x1fff << 16) +#define VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT 16 +#define VI6_RPF_SRC_BSIZE_BVSIZE_MASK (0x1fff << 0) +#define VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT 0 + +#define VI6_RPF_SRC_ESIZE 0x0304 +#define VI6_RPF_SRC_ESIZE_EHSIZE_MASK (0x1fff << 16) +#define VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT 16 +#define VI6_RPF_SRC_ESIZE_EVSIZE_MASK (0x1fff << 0) +#define VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT 0 + +#define VI6_RPF_INFMT 0x0308 +#define VI6_RPF_INFMT_VIR (1 << 28) +#define VI6_RPF_INFMT_CIPM (1 << 16) +#define VI6_RPF_INFMT_SPYCS (1 << 15) +#define VI6_RPF_INFMT_SPUVS (1 << 14) +#define VI6_RPF_INFMT_CEXT_ZERO (0 << 12) +#define VI6_RPF_INFMT_CEXT_EXT (1 << 12) +#define VI6_RPF_INFMT_CEXT_ONE (2 << 12) +#define VI6_RPF_INFMT_CEXT_MASK (3 << 12) +#define VI6_RPF_INFMT_RDTM_BT601 (0 << 9) +#define VI6_RPF_INFMT_RDTM_BT601_EXT (1 << 9) +#define VI6_RPF_INFMT_RDTM_BT709 (2 << 9) +#define VI6_RPF_INFMT_RDTM_BT709_EXT (3 << 9) +#define VI6_RPF_INFMT_RDTM_MASK (7 << 9) +#define VI6_RPF_INFMT_CSC (1 << 8) +#define VI6_RPF_INFMT_RDFMT_MASK (0x7f << 0) +#define VI6_RPF_INFMT_RDFMT_SHIFT 0 + +#define VI6_RPF_DSWAP 0x030c +#define VI6_RPF_DSWAP_A_LLS (1 << 11) +#define VI6_RPF_DSWAP_A_LWS (1 << 10) +#define VI6_RPF_DSWAP_A_WDS (1 << 9) +#define VI6_RPF_DSWAP_A_BTS (1 << 8) +#define VI6_RPF_DSWAP_P_LLS (1 << 3) +#define VI6_RPF_DSWAP_P_LWS (1 << 2) +#define VI6_RPF_DSWAP_P_WDS (1 << 1) +#define VI6_RPF_DSWAP_P_BTS (1 << 0) + +#define VI6_RPF_LOC 0x0310 +#define VI6_RPF_LOC_HCOORD_MASK (0x1fff << 16) +#define VI6_RPF_LOC_HCOORD_SHIFT 16 +#define VI6_RPF_LOC_VCOORD_MASK (0x1fff << 0) +#define VI6_RPF_LOC_VCOORD_SHIFT 0 + +#define VI6_RPF_ALPH_SEL 0x0314 +#define VI6_RPF_ALPH_SEL_ASEL_PACKED (0 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_8B_PLANE (1 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_SELECT (2 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_1B_PLANE (3 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_FIXED (4 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_MASK (7 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_SHIFT 28 +#define VI6_RPF_ALPH_SEL_IROP_MASK (0xf << 24) +#define VI6_RPF_ALPH_SEL_IROP_SHIFT 24 +#define VI6_RPF_ALPH_SEL_BSEL (1 << 23) +#define VI6_RPF_ALPH_SEL_AEXT_ZERO (0 << 18) +#define VI6_RPF_ALPH_SEL_AEXT_EXT (1 << 18) +#define VI6_RPF_ALPH_SEL_AEXT_ONE (2 << 18) +#define VI6_RPF_ALPH_SEL_AEXT_MASK (3 << 18) +#define VI6_RPF_ALPH_SEL_ALPHA0_MASK (0xff << 8) +#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT 8 +#define VI6_RPF_ALPH_SEL_ALPHA1_MASK (0xff << 0) +#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT 0 + +#define VI6_RPF_VRTCOL_SET 0x0318 +#define VI6_RPF_VRTCOL_SET_LAYA_MASK (0xff << 24) +#define VI6_RPF_VRTCOL_SET_LAYA_SHIFT 24 +#define VI6_RPF_VRTCOL_SET_LAYR_MASK (0xff << 16) +#define VI6_RPF_VRTCOL_SET_LAYR_SHIFT 16 +#define VI6_RPF_VRTCOL_SET_LAYG_MASK (0xff << 8) +#define VI6_RPF_VRTCOL_SET_LAYG_SHIFT 8 +#define VI6_RPF_VRTCOL_SET_LAYB_MASK (0xff << 0) +#define VI6_RPF_VRTCOL_SET_LAYB_SHIFT 0 + +#define VI6_RPF_MSK_CTRL 0x031c +#define VI6_RPF_MSK_CTRL_MSK_EN (1 << 24) +#define VI6_RPF_MSK_CTRL_MGR_MASK (0xff << 16) +#define VI6_RPF_MSK_CTRL_MGR_SHIFT 16 +#define VI6_RPF_MSK_CTRL_MGG_MASK (0xff << 8) +#define VI6_RPF_MSK_CTRL_MGG_SHIFT 8 +#define VI6_RPF_MSK_CTRL_MGB_MASK (0xff << 0) +#define VI6_RPF_MSK_CTRL_MGB_SHIFT 0 + +#define VI6_RPF_MSK_SET0 0x0320 +#define VI6_RPF_MSK_SET1 0x0324 +#define VI6_RPF_MSK_SET_MSA_MASK (0xff << 24) +#define VI6_RPF_MSK_SET_MSA_SHIFT 24 +#define VI6_RPF_MSK_SET_MSR_MASK (0xff << 16) +#define VI6_RPF_MSK_SET_MSR_SHIFT 16 +#define VI6_RPF_MSK_SET_MSG_MASK (0xff << 8) +#define VI6_RPF_MSK_SET_MSG_SHIFT 8 +#define VI6_RPF_MSK_SET_MSB_MASK (0xff << 0) +#define VI6_RPF_MSK_SET_MSB_SHIFT 0 + +#define VI6_RPF_CKEY_CTRL 0x0328 +#define VI6_RPF_CKEY_CTRL_CV (1 << 4) +#define VI6_RPF_CKEY_CTRL_SAPE1 (1 << 1) +#define VI6_RPF_CKEY_CTRL_SAPE0 (1 << 0) + +#define VI6_RPF_CKEY_SET0 0x032c +#define VI6_RPF_CKEY_SET1 0x0330 +#define VI6_RPF_CKEY_SET_AP_MASK (0xff << 24) +#define VI6_RPF_CKEY_SET_AP_SHIFT 24 +#define VI6_RPF_CKEY_SET_R_MASK (0xff << 16) +#define VI6_RPF_CKEY_SET_R_SHIFT 16 +#define VI6_RPF_CKEY_SET_GY_MASK (0xff << 8) +#define VI6_RPF_CKEY_SET_GY_SHIFT 8 +#define VI6_RPF_CKEY_SET_B_MASK (0xff << 0) +#define VI6_RPF_CKEY_SET_B_SHIFT 0 + +#define VI6_RPF_SRCM_PSTRIDE 0x0334 +#define VI6_RPF_SRCM_PSTRIDE_Y_SHIFT 16 +#define VI6_RPF_SRCM_PSTRIDE_C_SHIFT 0 + +#define VI6_RPF_SRCM_ASTRIDE 0x0338 +#define VI6_RPF_SRCM_PSTRIDE_A_SHIFT 0 + +#define VI6_RPF_SRCM_ADDR_Y 0x033c +#define VI6_RPF_SRCM_ADDR_C0 0x0340 +#define VI6_RPF_SRCM_ADDR_C1 0x0344 +#define VI6_RPF_SRCM_ADDR_AI 0x0348 + +/* ----------------------------------------------------------------------------- + * WPF Control Registers + */ + +#define VI6_WPF_OFFSET 0x100 + +#define VI6_WPF_SRCRPF 0x1000 +#define VI6_WPF_SRCRPF_VIRACT_DIS (0 << 28) +#define VI6_WPF_SRCRPF_VIRACT_SUB (1 << 28) +#define VI6_WPF_SRCRPF_VIRACT_MST (2 << 28) +#define VI6_WPF_SRCRPF_VIRACT_MASK (3 << 28) +#define VI6_WPF_SRCRPF_RPF_ACT_DIS(n) (0 << ((n) * 2)) +#define VI6_WPF_SRCRPF_RPF_ACT_SUB(n) (1 << ((n) * 2)) +#define VI6_WPF_SRCRPF_RPF_ACT_MST(n) (2 << ((n) * 2)) +#define VI6_WPF_SRCRPF_RPF_ACT_MASK(n) (3 << ((n) * 2)) + +#define VI6_WPF_HSZCLIP 0x1004 +#define VI6_WPF_VSZCLIP 0x1008 +#define VI6_WPF_SZCLIP_EN (1 << 28) +#define VI6_WPF_SZCLIP_OFST_MASK (0xff << 16) +#define VI6_WPF_SZCLIP_OFST_SHIFT 16 +#define VI6_WPF_SZCLIP_SIZE_MASK (0x1fff << 0) +#define VI6_WPF_SZCLIP_SIZE_SHIFT 0 + +#define VI6_WPF_OUTFMT 0x100c +#define VI6_WPF_OUTFMT_PDV_MASK (0xff << 24) +#define VI6_WPF_OUTFMT_PDV_SHIFT 24 +#define VI6_WPF_OUTFMT_PXA (1 << 23) +#define VI6_WPF_OUTFMT_FLP (1 << 16) +#define VI6_WPF_OUTFMT_SPYCS (1 << 15) +#define VI6_WPF_OUTFMT_SPUVS (1 << 14) +#define VI6_WPF_OUTFMT_DITH_DIS (0 << 12) +#define VI6_WPF_OUTFMT_DITH_EN (3 << 12) +#define VI6_WPF_OUTFMT_DITH_MASK (3 << 12) +#define VI6_WPF_OUTFMT_WRTM_BT601 (0 << 9) +#define VI6_WPF_OUTFMT_WRTM_BT601_EXT (1 << 9) +#define VI6_WPF_OUTFMT_WRTM_BT709 (2 << 9) +#define VI6_WPF_OUTFMT_WRTM_BT709_EXT (3 << 9) +#define VI6_WPF_OUTFMT_WRTM_MASK (7 << 9) +#define VI6_WPF_OUTFMT_CSC (1 << 8) +#define VI6_WPF_OUTFMT_WRFMT_MASK (0x7f << 0) +#define VI6_WPF_OUTFMT_WRFMT_SHIFT 0 + +#define VI6_WPF_DSWAP 0x1010 +#define VI6_WPF_DSWAP_P_LLS (1 << 3) +#define VI6_WPF_DSWAP_P_LWS (1 << 2) +#define VI6_WPF_DSWAP_P_WDS (1 << 1) +#define VI6_WPF_DSWAP_P_BTS (1 << 0) + +#define VI6_WPF_RNDCTRL 0x1014 +#define VI6_WPF_RNDCTRL_CBRM (1 << 28) +#define VI6_WPF_RNDCTRL_ABRM_TRUNC (0 << 24) +#define VI6_WPF_RNDCTRL_ABRM_ROUND (1 << 24) +#define VI6_WPF_RNDCTRL_ABRM_THRESH (2 << 24) +#define VI6_WPF_RNDCTRL_ABRM_MASK (3 << 24) +#define VI6_WPF_RNDCTRL_ATHRESH_MASK (0xff << 16) +#define VI6_WPF_RNDCTRL_ATHRESH_SHIFT 16 +#define VI6_WPF_RNDCTRL_CLMD_FULL (0 << 12) +#define VI6_WPF_RNDCTRL_CLMD_CLIP (1 << 12) +#define VI6_WPF_RNDCTRL_CLMD_EXT (2 << 12) +#define VI6_WPF_RNDCTRL_CLMD_MASK (3 << 12) + +#define VI6_WPF_DSTM_STRIDE_Y 0x101c +#define VI6_WPF_DSTM_STRIDE_C 0x1020 +#define VI6_WPF_DSTM_ADDR_Y 0x1024 +#define VI6_WPF_DSTM_ADDR_C0 0x1028 +#define VI6_WPF_DSTM_ADDR_C1 0x102c + +#define VI6_WPF_WRBCK_CTRL 0x1034 +#define VI6_WPF_WRBCK_CTRL_WBMD (1 << 0) + +/* ----------------------------------------------------------------------------- + * DPR Control Registers + */ + +#define VI6_DPR_RPF_ROUTE(n) (0x2000 + (n) * 4) + +#define VI6_DPR_WPF_FPORCH(n) (0x2014 + (n) * 4) +#define VI6_DPR_WPF_FPORCH_FP_WPFN (5 << 8) + +#define VI6_DPR_SRU_ROUTE 0x2024 +#define VI6_DPR_UDS_ROUTE(n) (0x2028 + (n) * 4) +#define VI6_DPR_LUT_ROUTE 0x203c +#define VI6_DPR_CLU_ROUTE 0x2040 +#define VI6_DPR_HST_ROUTE 0x2044 +#define VI6_DPR_HSI_ROUTE 0x2048 +#define VI6_DPR_BRU_ROUTE 0x204c +#define VI6_DPR_ROUTE_FXA_MASK (0xff << 8) +#define VI6_DPR_ROUTE_FXA_SHIFT 16 +#define VI6_DPR_ROUTE_FP_MASK (0xff << 8) +#define VI6_DPR_ROUTE_FP_SHIFT 8 +#define VI6_DPR_ROUTE_RT_MASK (0x3f << 0) +#define VI6_DPR_ROUTE_RT_SHIFT 0 + +#define VI6_DPR_HGO_SMPPT 0x2050 +#define VI6_DPR_HGT_SMPPT 0x2054 +#define VI6_DPR_SMPPT_TGW_MASK (7 << 8) +#define VI6_DPR_SMPPT_TGW_SHIFT 8 +#define VI6_DPR_SMPPT_PT_MASK (0x3f << 0) +#define VI6_DPR_SMPPT_PT_SHIFT 0 + +#define VI6_DPR_NODE_RPF(n) (n) +#define VI6_DPR_NODE_SRU 16 +#define VI6_DPR_NODE_UDS(n) (17 + (n)) +#define VI6_DPR_NODE_LUT 22 +#define VI6_DPR_NODE_BRU_IN(n) (23 + (n)) +#define VI6_DPR_NODE_BRU_OUT 27 +#define VI6_DPR_NODE_CLU 29 +#define VI6_DPR_NODE_HST 30 +#define VI6_DPR_NODE_HSI 31 +#define VI6_DPR_NODE_LIF 55 +#define VI6_DPR_NODE_WPF(n) (56 + (n)) +#define VI6_DPR_NODE_UNUSED 63 + +/* ----------------------------------------------------------------------------- + * SRU Control Registers + */ + +#define VI6_SRU_CTRL0 0x2200 +#define VI6_SRU_CTRL1 0x2204 +#define VI6_SRU_CTRL2 0x2208 + +/* ----------------------------------------------------------------------------- + * UDS Control Registers + */ + +#define VI6_UDS_OFFSET 0x100 + +#define VI6_UDS_CTRL 0x2300 +#define VI6_UDS_CTRL_AMD (1 << 30) +#define VI6_UDS_CTRL_FMD (1 << 29) +#define VI6_UDS_CTRL_BLADV (1 << 28) +#define VI6_UDS_CTRL_AON (1 << 25) +#define VI6_UDS_CTRL_ATHON (1 << 24) +#define VI6_UDS_CTRL_BC (1 << 20) +#define VI6_UDS_CTRL_NE_A (1 << 19) +#define VI6_UDS_CTRL_NE_RCR (1 << 18) +#define VI6_UDS_CTRL_NE_GY (1 << 17) +#define VI6_UDS_CTRL_NE_BCB (1 << 16) +#define VI6_UDS_CTRL_TDIPC (1 << 1) + +#define VI6_UDS_SCALE 0x2304 +#define VI6_UDS_SCALE_HMANT_MASK (0xf << 28) +#define VI6_UDS_SCALE_HMANT_SHIFT 28 +#define VI6_UDS_SCALE_HFRAC_MASK (0xfff << 16) +#define VI6_UDS_SCALE_HFRAC_SHIFT 16 +#define VI6_UDS_SCALE_VMANT_MASK (0xf << 12) +#define VI6_UDS_SCALE_VMANT_SHIFT 12 +#define VI6_UDS_SCALE_VFRAC_MASK (0xfff << 0) +#define VI6_UDS_SCALE_VFRAC_SHIFT 0 + +#define VI6_UDS_ALPTH 0x2308 +#define VI6_UDS_ALPTH_TH1_MASK (0xff << 8) +#define VI6_UDS_ALPTH_TH1_SHIFT 8 +#define VI6_UDS_ALPTH_TH0_MASK (0xff << 0) +#define VI6_UDS_ALPTH_TH0_SHIFT 0 + +#define VI6_UDS_ALPVAL 0x230c +#define VI6_UDS_ALPVAL_VAL2_MASK (0xff << 16) +#define VI6_UDS_ALPVAL_VAL2_SHIFT 16 +#define VI6_UDS_ALPVAL_VAL1_MASK (0xff << 8) +#define VI6_UDS_ALPVAL_VAL1_SHIFT 8 +#define VI6_UDS_ALPVAL_VAL0_MASK (0xff << 0) +#define VI6_UDS_ALPVAL_VAL0_SHIFT 0 + +#define VI6_UDS_PASS_BWIDTH 0x2310 +#define VI6_UDS_PASS_BWIDTH_H_MASK (0x7f << 16) +#define VI6_UDS_PASS_BWIDTH_H_SHIFT 16 +#define VI6_UDS_PASS_BWIDTH_V_MASK (0x7f << 0) +#define VI6_UDS_PASS_BWIDTH_V_SHIFT 0 + +#define VI6_UDS_IPC 0x2318 +#define VI6_UDS_IPC_FIELD (1 << 27) +#define VI6_UDS_IPC_VEDP_MASK (0xfff << 0) +#define VI6_UDS_IPC_VEDP_SHIFT 0 + +#define VI6_UDS_CLIP_SIZE 0x2324 +#define VI6_UDS_CLIP_SIZE_HSIZE_MASK (0x1fff << 16) +#define VI6_UDS_CLIP_SIZE_HSIZE_SHIFT 16 +#define VI6_UDS_CLIP_SIZE_VSIZE_MASK (0x1fff << 0) +#define VI6_UDS_CLIP_SIZE_VSIZE_SHIFT 0 + +#define VI6_UDS_FILL_COLOR 0x2328 +#define VI6_UDS_FILL_COLOR_RFILC_MASK (0xff << 16) +#define VI6_UDS_FILL_COLOR_RFILC_SHIFT 16 +#define VI6_UDS_FILL_COLOR_GFILC_MASK (0xff << 8) +#define VI6_UDS_FILL_COLOR_GFILC_SHIFT 8 +#define VI6_UDS_FILL_COLOR_BFILC_MASK (0xff << 0) +#define VI6_UDS_FILL_COLOR_BFILC_SHIFT 0 + +/* ----------------------------------------------------------------------------- + * LUT Control Registers + */ + +#define VI6_LUT_CTRL 0x2800 + +/* ----------------------------------------------------------------------------- + * CLU Control Registers + */ + +#define VI6_CLU_CTRL 0x2900 + +/* ----------------------------------------------------------------------------- + * HST Control Registers + */ + +#define VI6_HST_CTRL 0x2a00 + +/* ----------------------------------------------------------------------------- + * HSI Control Registers + */ + +#define VI6_HSI_CTRL 0x2b00 + +/* ----------------------------------------------------------------------------- + * BRU Control Registers + */ + +#define VI6_BRU_INCTRL 0x2c00 +#define VI6_BRU_VIRRPF_SIZE 0x2c04 +#define VI6_BRU_VIRRPF_LOC 0x2c08 +#define VI6_BRU_VIRRPF_COL 0x2c0c +#define VI6_BRU_CTRL(n) (0x2c10 + (n) * 8) +#define VI6_BRU_BLD(n) (0x2c14 + (n) * 8) +#define VI6_BRU_ROP 0x2c30 + +/* ----------------------------------------------------------------------------- + * HGO Control Registers + */ + +#define VI6_HGO_OFFSET 0x3000 +#define VI6_HGO_SIZE 0x3004 +#define VI6_HGO_MODE 0x3008 +#define VI6_HGO_LB_TH 0x300c +#define VI6_HGO_LBn_H(n) (0x3010 + (n) * 8) +#define VI6_HGO_LBn_V(n) (0x3014 + (n) * 8) +#define VI6_HGO_R_HISTO 0x3030 +#define VI6_HGO_R_MAXMIN 0x3130 +#define VI6_HGO_R_SUM 0x3134 +#define VI6_HGO_R_LB_DET 0x3138 +#define VI6_HGO_G_HISTO 0x3140 +#define VI6_HGO_G_MAXMIN 0x3240 +#define VI6_HGO_G_SUM 0x3244 +#define VI6_HGO_G_LB_DET 0x3248 +#define VI6_HGO_B_HISTO 0x3250 +#define VI6_HGO_B_MAXMIN 0x3350 +#define VI6_HGO_B_SUM 0x3354 +#define VI6_HGO_B_LB_DET 0x3358 +#define VI6_HGO_REGRST 0x33fc + +/* ----------------------------------------------------------------------------- + * HGT Control Registers + */ + +#define VI6_HGT_OFFSET 0x3400 +#define VI6_HGT_SIZE 0x3404 +#define VI6_HGT_MODE 0x3408 +#define VI6_HGT_HUE_AREA(n) (0x340c + (n) * 4) +#define VI6_HGT_LB_TH 0x3424 +#define VI6_HGT_LBn_H(n) (0x3438 + (n) * 8) +#define VI6_HGT_LBn_V(n) (0x342c + (n) * 8) +#define VI6_HGT_HISTO(m, n) (0x3450 + (m) * 128 + (n) * 4) +#define VI6_HGT_MAXMIN 0x3750 +#define VI6_HGT_SUM 0x3754 +#define VI6_HGT_LB_DET 0x3758 +#define VI6_HGT_REGRST 0x37fc + +/* ----------------------------------------------------------------------------- + * LIF Control Registers + */ + +#define VI6_LIF_CTRL 0x3b00 +#define VI6_LIF_CTRL_OBTH_MASK (0x7ff << 16) +#define VI6_LIF_CTRL_OBTH_SHIFT 16 +#define VI6_LIF_CTRL_CFMT (1 << 4) +#define VI6_LIF_CTRL_REQSEL (1 << 1) +#define VI6_LIF_CTRL_LIF_EN (1 << 0) + +#define VI6_LIF_CSBTH 0x3b04 +#define VI6_LIF_CSBTH_HBTH_MASK (0x7ff << 16) +#define VI6_LIF_CSBTH_HBTH_SHIFT 16 +#define VI6_LIF_CSBTH_LBTH_MASK (0x7ff << 0) +#define VI6_LIF_CSBTH_LBTH_SHIFT 0 + +/* ----------------------------------------------------------------------------- + * Security Control Registers + */ + +#define VI6_SECURITY_CTRL0 0x3d00 +#define VI6_SECURITY_CTRL1 0x3d04 + +/* ----------------------------------------------------------------------------- + * RPF CLUT Registers + */ + +#define VI6_CLUT_TABLE 0x4000 + +/* ----------------------------------------------------------------------------- + * 1D LUT Registers + */ + +#define VI6_LUT_TABLE 0x7000 + +/* ----------------------------------------------------------------------------- + * 3D LUT Registers + */ + +#define VI6_CLU_ADDR 0x7400 +#define VI6_CLU_DATA 0x7404 + +/* ----------------------------------------------------------------------------- + * Formats + */ + +#define VI6_FMT_RGB_332 0x00 +#define VI6_FMT_XRGB_4444 0x01 +#define VI6_FMT_RGBX_4444 0x02 +#define VI6_FMT_XRGB_1555 0x04 +#define VI6_FMT_RGBX_5551 0x05 +#define VI6_FMT_RGB_565 0x06 +#define VI6_FMT_AXRGB_86666 0x07 +#define VI6_FMT_RGBXA_66668 0x08 +#define VI6_FMT_XRGBA_66668 0x09 +#define VI6_FMT_ARGBX_86666 0x0a +#define VI6_FMT_AXRXGXB_8262626 0x0b +#define VI6_FMT_XRXGXBA_2626268 0x0c +#define VI6_FMT_ARXGXBX_8626262 0x0d +#define VI6_FMT_RXGXBXA_6262628 0x0e +#define VI6_FMT_XRGB_6666 0x0f +#define VI6_FMT_RGBX_6666 0x10 +#define VI6_FMT_XRXGXB_262626 0x11 +#define VI6_FMT_RXGXBX_626262 0x12 +#define VI6_FMT_ARGB_8888 0x13 +#define VI6_FMT_RGBA_8888 0x14 +#define VI6_FMT_RGB_888 0x15 +#define VI6_FMT_XRGXGB_763763 0x16 +#define VI6_FMT_XXRGB_86666 0x17 +#define VI6_FMT_BGR_888 0x18 +#define VI6_FMT_ARGB_4444 0x19 +#define VI6_FMT_RGBA_4444 0x1a +#define VI6_FMT_ARGB_1555 0x1b +#define VI6_FMT_RGBA_5551 0x1c +#define VI6_FMT_ABGR_4444 0x1d +#define VI6_FMT_BGRA_4444 0x1e +#define VI6_FMT_ABGR_1555 0x1f +#define VI6_FMT_BGRA_5551 0x20 +#define VI6_FMT_XBXGXR_262626 0x21 +#define VI6_FMT_ABGR_8888 0x22 +#define VI6_FMT_XXRGB_88565 0x23 + +#define VI6_FMT_Y_UV_444 0x40 +#define VI6_FMT_Y_UV_422 0x41 +#define VI6_FMT_Y_UV_420 0x42 +#define VI6_FMT_YUV_444 0x46 +#define VI6_FMT_YUYV_422 0x47 +#define VI6_FMT_YYUV_422 0x48 +#define VI6_FMT_YUV_420 0x49 +#define VI6_FMT_Y_U_V_444 0x4a +#define VI6_FMT_Y_U_V_422 0x4b +#define VI6_FMT_Y_U_V_420 0x4c + +#endif /* __VSP1_REGS_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c new file mode 100644 index 0000000..254871d --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -0,0 +1,209 @@ +/* + * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + +#include + +#include "vsp1.h" +#include "vsp1_rwpf.h" +#include "vsp1_video.h" + +#define RPF_MAX_WIDTH 8190 +#define RPF_MAX_HEIGHT 8190 + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_rpf_read(struct vsp1_rwpf *rpf, u32 reg) +{ + return vsp1_read(rpf->entity.vsp1, + reg + rpf->entity.index * VI6_RPF_OFFSET); +} + +static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) +{ + vsp1_write(rpf->entity.vsp1, + reg + rpf->entity.index * VI6_RPF_OFFSET, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct vsp1_rwpf *rpf = to_rwpf(subdev); + const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo; + const struct v4l2_pix_format_mplane *format = &rpf->video.format; + u32 pstride; + u32 infmt; + + if (!enable) + return 0; + + /* Source size and stride. Cropping isn't supported yet. */ + vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE, + (format->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | + (format->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); + vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE, + (format->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | + (format->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); + + pstride = format->plane_fmt[0].bytesperline + << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; + if (format->num_planes > 1) + pstride |= format->plane_fmt[1].bytesperline + << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; + + vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); + + /* Format */ + infmt = VI6_RPF_INFMT_CIPM + | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); + + if (fmtinfo->swap_yc) + infmt |= VI6_RPF_INFMT_SPYCS; + if (fmtinfo->swap_uv) + infmt |= VI6_RPF_INFMT_SPUVS; + + if (rpf->entity.formats[RWPF_PAD_SINK].code != + rpf->entity.formats[RWPF_PAD_SOURCE].code) + infmt |= VI6_RPF_INFMT_CSC; + + vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt); + vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap); + + /* Output location. Composing isn't supported yet. */ + vsp1_rpf_write(rpf, VI6_RPF_LOC, 0); + + /* Disable alpha, mask and color key. Set the alpha channel to a fixed + * value of 255. + */ + vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, 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); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops rpf_video_ops = { + .s_stream = rpf_s_stream, +}; + +static struct v4l2_subdev_pad_ops rpf_pad_ops = { + .enum_mbus_code = vsp1_rwpf_enum_mbus_code, + .enum_frame_size = vsp1_rwpf_enum_frame_size, + .get_fmt = vsp1_rwpf_get_format, + .set_fmt = vsp1_rwpf_set_format, +}; + +static struct v4l2_subdev_ops rpf_ops = { + .video = &rpf_video_ops, + .pad = &rpf_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Video Device Operations + */ + +static void rpf_vdev_queue(struct vsp1_video *video, + struct vsp1_video_buffer *buf) +{ + struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video); + + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, buf->addr[0]); + if (buf->buf.num_planes > 1) + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, buf->addr[1]); + if (buf->buf.num_planes > 2) + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, buf->addr[2]); +} + +static const struct vsp1_video_operations rpf_vdev_ops = { + .queue = rpf_vdev_queue, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) +{ + struct v4l2_subdev *subdev; + struct vsp1_video *video; + struct vsp1_rwpf *rpf; + int ret; + + rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL); + if (rpf == NULL) + return ERR_PTR(-ENOMEM); + + rpf->max_width = RPF_MAX_WIDTH; + rpf->max_height = RPF_MAX_HEIGHT; + + rpf->entity.type = VSP1_ENTITY_RPF; + rpf->entity.index = index; + rpf->entity.id = VI6_DPR_NODE_RPF(index); + + ret = vsp1_entity_init(vsp1, &rpf->entity, 2); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &rpf->entity.subdev; + v4l2_subdev_init(subdev, &rpf_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u", + dev_name(vsp1->dev), index); + v4l2_set_subdevdata(subdev, rpf); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + /* Initialize the video device. */ + video = &rpf->video; + + video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + video->vsp1 = vsp1; + video->ops = &rpf_vdev_ops; + + ret = vsp1_video_init(video, &rpf->entity); + if (ret < 0) + goto error_video; + + /* Connect the video device to the RPF. */ + ret = media_entity_create_link(&rpf->video.video.entity, 0, + &rpf->entity.subdev.entity, + RWPF_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + goto error_link; + + return rpf; + +error_link: + vsp1_video_cleanup(video); +error_video: + media_entity_cleanup(&rpf->entity.subdev.entity); + return ERR_PTR(ret); +} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c new file mode 100644 index 0000000..9752d55 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -0,0 +1,124 @@ +/* + * vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + +#include "vsp1.h" +#include "vsp1_rwpf.h" +#include "vsp1_video.h" + +#define RWPF_MIN_WIDTH 1 +#define RWPF_MIN_HEIGHT 1 + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const unsigned int codes[] = { + V4L2_MBUS_FMT_ARGB8888_1X32, + V4L2_MBUS_FMT_AYUV8_1X32, + }; + + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = codes[code->index]; + + return 0; +} + +int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(fh, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == RWPF_PAD_SINK) { + fse->min_width = RWPF_MIN_WIDTH; + fse->max_width = rwpf->max_width; + fse->min_height = RWPF_MIN_HEIGHT; + fse->max_height = rwpf->max_height; + } else { + /* The size on the source pad are fixed and always identical to + * the size on the sink pad. + */ + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} + +int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_rwpf *rwpf = to_rwpf(subdev); + + fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad, + fmt->which); + + return 0; +} + +int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct v4l2_mbus_framefmt *format; + + /* Default to YUV if the requested format is not supported. */ + if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 && + fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32) + fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32; + + format = vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad, + fmt->which); + + if (fmt->pad == RWPF_PAD_SOURCE) { + /* The RWPF performs format conversion but can't scale, only the + * format code can be changed on the source pad. + */ + format->code = fmt->format.code; + fmt->format = *format; + return 0; + } + + format->code = fmt->format.code; + format->width = clamp_t(unsigned int, fmt->format.width, + RWPF_MIN_WIDTH, rwpf->max_width); + format->height = clamp_t(unsigned int, fmt->format.height, + RWPF_MIN_HEIGHT, rwpf->max_height); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE, + fmt->which); + *format = fmt->format; + + return 0; +} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h new file mode 100644 index 0000000..c182d85 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -0,0 +1,53 @@ +/* + * vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_RWPF_H__ +#define __VSP1_RWPF_H__ + +#include +#include + +#include "vsp1.h" +#include "vsp1_entity.h" +#include "vsp1_video.h" + +#define RWPF_PAD_SINK 0 +#define RWPF_PAD_SOURCE 1 + +struct vsp1_rwpf { + struct vsp1_entity entity; + struct vsp1_video video; + + unsigned int max_width; + unsigned int max_height; +}; + +static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_rwpf, entity.subdev); +} + +struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index); +struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index); + +int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code); +int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse); +int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt); +int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt); + +#endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c new file mode 100644 index 0000000..0e50b37 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -0,0 +1,346 @@ +/* + * vsp1_uds.c -- R-Car VSP1 Up and Down Scaler + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include + +#include "vsp1.h" +#include "vsp1_uds.h" + +#define UDS_MIN_SIZE 4U +#define UDS_MAX_SIZE 8190U + +#define UDS_MIN_FACTOR 0x0100 +#define UDS_MAX_FACTOR 0xffff + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_uds_read(struct vsp1_uds *uds, u32 reg) +{ + return vsp1_read(uds->entity.vsp1, + reg + uds->entity.index * VI6_UDS_OFFSET); +} + +static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data) +{ + vsp1_write(uds->entity.vsp1, + reg + uds->entity.index * VI6_UDS_OFFSET, data); +} + +/* ----------------------------------------------------------------------------- + * Scaling Computation + */ + +/* + * uds_output_size - Return the output size for an input size and scaling ratio + * @input: input size in pixels + * @ratio: scaling ratio in U4.12 fixed-point format + */ +static unsigned int uds_output_size(unsigned int input, unsigned int ratio) +{ + if (ratio > 4096) { + /* Down-scaling */ + unsigned int mp; + + mp = ratio / 4096; + mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4); + + return (input - 1) / mp * mp * 4096 / ratio + 1; + } else { + /* Up-scaling */ + return (input - 1) * 4096 / ratio + 1; + } +} + +/* + * uds_output_limits - Return the min and max output sizes for an input size + * @input: input size in pixels + * @minimum: minimum output size (returned) + * @maximum: maximum output size (returned) + */ +static void uds_output_limits(unsigned int input, + unsigned int *minimum, unsigned int *maximum) +{ + *minimum = max(uds_output_size(input, UDS_MAX_FACTOR), UDS_MIN_SIZE); + *maximum = min(uds_output_size(input, UDS_MIN_FACTOR), UDS_MAX_SIZE); +} + +/* + * uds_passband_width - Return the passband filter width for a scaling ratio + * @ratio: scaling ratio in U4.12 fixed-point format + */ +static unsigned int uds_passband_width(unsigned int ratio) +{ + if (ratio >= 4096) { + /* Down-scaling */ + unsigned int mp; + + mp = ratio / 4096; + mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4); + + return 64 * 4096 * mp / ratio; + } else { + /* Up-scaling */ + return 64; + } +} + +static unsigned int uds_compute_ratio(unsigned int input, unsigned int output) +{ + /* TODO: This is an approximation that will need to be refined. */ + 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); + + if (!enable) + return 0; + + /* Enable multi-tap scaling. */ + vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_BC); + + vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH, + (uds_passband_width(uds->hscale) + << VI6_UDS_PASS_BWIDTH_H_SHIFT) | + (uds_passband_width(uds->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)); + vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE, + (format->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | + (format->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int uds_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const unsigned int codes[] = { + V4L2_MBUS_FMT_ARGB8888_1X32, + V4L2_MBUS_FMT_AYUV8_1X32, + }; + + if (code->pad == UDS_PAD_SINK) { + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = codes[code->index]; + } else { + struct v4l2_mbus_framefmt *format; + + /* The UDS can't perform format conversion, the sink format is + * always identical to the source format. + */ + if (code->index) + return -EINVAL; + + format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK); + code->code = format->code; + } + + return 0; +} + +static int uds_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == UDS_PAD_SINK) { + fse->min_width = UDS_MIN_SIZE; + fse->max_width = UDS_MAX_SIZE; + fse->min_height = UDS_MIN_SIZE; + fse->max_height = UDS_MAX_SIZE; + } else { + uds_output_limits(format->width, &fse->min_width, + &fse->max_width); + uds_output_limits(format->height, &fse->min_height, + &fse->max_height); + } + + return 0; +} + +static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_uds *uds = to_uds(subdev); + + fmt->format = *vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad, + fmt->which); + + return 0; +} + +static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt *format; + unsigned int minimum; + unsigned int maximum; + + switch (pad) { + case UDS_PAD_SINK: + /* Default to YUV if the requested format is not supported. */ + if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 && + fmt->code != V4L2_MBUS_FMT_AYUV8_1X32) + fmt->code = V4L2_MBUS_FMT_AYUV8_1X32; + + fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE); + fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE); + break; + + case UDS_PAD_SOURCE: + /* The UDS scales but can't perform format conversion. */ + format = vsp1_entity_get_pad_format(&uds->entity, fh, + UDS_PAD_SINK, which); + fmt->code = format->code; + + uds_output_limits(format->width, &minimum, &maximum); + fmt->width = clamp(fmt->width, minimum, maximum); + uds_output_limits(format->height, &minimum, &maximum); + fmt->height = clamp(fmt->height, minimum, maximum); + break; + } + + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_uds *uds = to_uds(subdev); + struct v4l2_mbus_framefmt *format; + + uds_try_format(uds, fh, fmt->pad, &fmt->format, fmt->which); + + format = vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad, + fmt->which); + *format = fmt->format; + + if (fmt->pad == UDS_PAD_SINK) { + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&uds->entity, fh, + UDS_PAD_SOURCE, fmt->which); + *format = fmt->format; + + uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which); + } + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + uds_compute_ratios(uds); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops uds_video_ops = { + .s_stream = uds_s_stream, +}; + +static struct v4l2_subdev_pad_ops uds_pad_ops = { + .enum_mbus_code = uds_enum_mbus_code, + .enum_frame_size = uds_enum_frame_size, + .get_fmt = uds_get_format, + .set_fmt = uds_set_format, +}; + +static struct v4l2_subdev_ops uds_ops = { + .video = &uds_video_ops, + .pad = &uds_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index) +{ + struct v4l2_subdev *subdev; + struct vsp1_uds *uds; + int ret; + + uds = devm_kzalloc(vsp1->dev, sizeof(*uds), GFP_KERNEL); + if (uds == NULL) + return ERR_PTR(-ENOMEM); + + uds->entity.type = VSP1_ENTITY_UDS; + uds->entity.index = index; + uds->entity.id = VI6_DPR_NODE_UDS(index); + + ret = vsp1_entity_init(vsp1, &uds->entity, 2); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &uds->entity.subdev; + v4l2_subdev_init(subdev, &uds_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s uds.%u", + dev_name(vsp1->dev), index); + v4l2_set_subdevdata(subdev, uds); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + return uds; +} diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h new file mode 100644 index 0000000..972a285 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_uds.h @@ -0,0 +1,40 @@ +/* + * vsp1_uds.h -- R-Car VSP1 Up and Down Scaler + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_UDS_H__ +#define __VSP1_UDS_H__ + +#include +#include + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define UDS_PAD_SINK 0 +#define UDS_PAD_SOURCE 1 + +struct vsp1_uds { + struct vsp1_entity entity; + + unsigned int hscale; + unsigned int vscale; +}; + +static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_uds, entity.subdev); +} + +struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index); + +#endif /* __VSP1_UDS_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c new file mode 100644 index 0000000..f51f842 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -0,0 +1,1071 @@ +/* + * vsp1_video.c -- R-Car VSP1 Video Node + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "vsp1.h" +#include "vsp1_entity.h" +#include "vsp1_rwpf.h" +#include "vsp1_video.h" + +#define VSP1_VIDEO_DEF_FORMAT V4L2_PIX_FMT_YUYV +#define VSP1_VIDEO_DEF_WIDTH 1024 +#define VSP1_VIDEO_DEF_HEIGHT 768 + +#define VSP1_VIDEO_MIN_WIDTH 2U +#define VSP1_VIDEO_MAX_WIDTH 8190U +#define VSP1_VIDEO_MIN_HEIGHT 2U +#define VSP1_VIDEO_MAX_HEIGHT 8190U + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +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 }, + { V4L2_PIX_FMT_RGB444, 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, + 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 }, + { 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 }, + { 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 }, + { 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 }, + { V4L2_PIX_FMT_BGR32, 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, + 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, +}; + +/* + * vsp1_get_format_info - Retrieve format information for a 4CC + * @fourcc: the format 4CC + * + * Return a pointer to the format information structure corresponding to the + * given V4L2 format 4CC, or NULL if no corresponding format can be found. + */ +static const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { + const struct vsp1_format_info *info = &vsp1_video_formats[i]; + + if (info->fourcc == fourcc) + return info; + } + + return NULL; +} + + +static struct v4l2_subdev * +vsp1_video_remote_subdev(struct media_pad *local, u32 *pad) +{ + struct media_pad *remote; + + remote = media_entity_remote_pad(local); + if (remote == NULL || + media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int vsp1_video_verify_format(struct vsp1_video *video) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + int ret; + + subdev = vsp1_video_remote_subdev(&video->pad, &fmt.pad); + if (subdev == NULL) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + if (video->fmtinfo->mbus != fmt.format.code || + video->format.height != fmt.format.height || + video->format.width != fmt.format.width) + return -EINVAL; + + return 0; +} + +static int __vsp1_video_try_format(struct vsp1_video *video, + struct v4l2_pix_format_mplane *pix, + const struct vsp1_format_info **fmtinfo) +{ + const struct vsp1_format_info *info; + unsigned int width = pix->width; + unsigned int height = pix->height; + unsigned int i; + + /* Retrieve format information and select the default format if the + * requested format isn't supported. + */ + info = vsp1_get_format_info(pix->pixelformat); + if (info == NULL) + info = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT); + + pix->pixelformat = info->fourcc; + pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->field = V4L2_FIELD_NONE; + memset(pix->reserved, 0, sizeof(pix->reserved)); + + /* Align the width and height for YUV 4:2:2 and 4:2:0 formats. */ + width = round_down(width, info->hsub); + height = round_down(height, info->vsub); + + /* Clamp the width and height. */ + pix->width = clamp(width, VSP1_VIDEO_MIN_WIDTH, VSP1_VIDEO_MAX_WIDTH); + pix->height = clamp(height, VSP1_VIDEO_MIN_HEIGHT, + VSP1_VIDEO_MAX_HEIGHT); + + /* Compute and clamp the stride and image size. While not documented in + * the datasheet, strides not aligned to a multiple of 128 bytes result + * in image corruption. + */ + for (i = 0; i < max(info->planes, 2U); ++i) { + unsigned int hsub = i > 0 ? info->hsub : 1; + unsigned int vsub = i > 0 ? info->vsub : 1; + unsigned int align = 128; + unsigned int bpl; + + bpl = clamp_t(unsigned int, pix->plane_fmt[i].bytesperline, + pix->width / hsub * info->bpp[i] / 8, + round_down(65535U, align)); + + pix->plane_fmt[i].bytesperline = round_up(bpl, align); + pix->plane_fmt[i].sizeimage = pix->plane_fmt[i].bytesperline + * pix->height / vsub; + } + + if (info->planes == 3) { + /* The second and third planes must have the same stride. */ + pix->plane_fmt[2].bytesperline = pix->plane_fmt[1].bytesperline; + pix->plane_fmt[2].sizeimage = pix->plane_fmt[1].sizeimage; + } + + pix->num_planes = info->planes; + + if (fmtinfo) + *fmtinfo = info; + + return 0; +} + +static bool +vsp1_video_format_adjust(struct vsp1_video *video, + const struct v4l2_pix_format_mplane *format, + struct v4l2_pix_format_mplane *adjust) +{ + unsigned int i; + + *adjust = *format; + __vsp1_video_try_format(video, adjust, NULL); + + if (format->width != adjust->width || + format->height != adjust->height || + format->pixelformat != adjust->pixelformat || + format->num_planes != adjust->num_planes) + return false; + + for (i = 0; i < format->num_planes; ++i) { + if (format->plane_fmt[i].bytesperline != + adjust->plane_fmt[i].bytesperline) + return false; + + adjust->plane_fmt[i].sizeimage = + max(adjust->plane_fmt[i].sizeimage, + format->plane_fmt[i].sizeimage); + } + + return true; +} + +/* ----------------------------------------------------------------------------- + * Pipeline Management + */ + +static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, + struct vsp1_rwpf *output) +{ + struct vsp1_entity *entity; + unsigned int entities = 0; + struct media_pad *pad; + bool uds_found = false; + + pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]); + + while (1) { + if (pad == NULL) + return -EPIPE; + + /* We've reached a video node, that shouldn't have happened. */ + if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return -EPIPE; + + entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); + + /* We've reached the WPF, we're done. */ + if (entity->type == VSP1_ENTITY_WPF) + break; + + /* Ensure the branch has no loop. */ + if (entities & (1 << entity->subdev.entity.id)) + return -EPIPE; + + entities |= 1 << entity->subdev.entity.id; + + /* UDS can't be chained. */ + if (entity->type == VSP1_ENTITY_UDS) { + if (uds_found) + return -EPIPE; + uds_found = true; + } + + /* Follow the source link. The link setup operations ensure + * that the output fan-out can't be more than one, there is thus + * no need to verify here that only a single source link is + * activated. + */ + pad = &entity->pads[entity->source_pad]; + pad = media_entity_remote_pad(pad); + } + + /* The last entity must be the output WPF. */ + if (entity != &output->entity) + return -EPIPE; + + return 0; +} + +static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, + struct vsp1_video *video) +{ + struct media_entity_graph graph; + struct media_entity *entity = &video->video.entity; + struct media_device *mdev = entity->parent; + unsigned int i; + int ret; + + mutex_lock(&mdev->graph_mutex); + + /* Walk the graph to locate the entities and video nodes. */ + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + struct v4l2_subdev *subdev; + struct vsp1_rwpf *rwpf; + struct vsp1_entity *e; + + if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) { + pipe->num_video++; + continue; + } + + subdev = media_entity_to_v4l2_subdev(entity); + e = to_vsp1_entity(subdev); + list_add_tail(&e->list_pipe, &pipe->entities); + + if (e->type == VSP1_ENTITY_RPF) { + rwpf = to_rwpf(subdev); + pipe->inputs[pipe->num_inputs++] = rwpf; + rwpf->video.pipe_index = pipe->num_inputs; + } else if (e->type == VSP1_ENTITY_WPF) { + rwpf = to_rwpf(subdev); + pipe->output = to_rwpf(subdev); + rwpf->video.pipe_index = 0; + } else if (e->type == VSP1_ENTITY_LIF) { + pipe->lif = e; + } + } + + mutex_unlock(&mdev->graph_mutex); + + /* We need one output and at least one input. */ + if (pipe->num_inputs == 0 || !pipe->output) { + ret = -EPIPE; + goto error; + } + + /* Follow links downstream for each input and make sure the graph + * 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], + pipe->output); + if (ret < 0) + goto error; + } + + return 0; + +error: + INIT_LIST_HEAD(&pipe->entities); + pipe->buffers_ready = 0; + pipe->num_video = 0; + pipe->num_inputs = 0; + pipe->output = NULL; + pipe->lif = NULL; + return ret; +} + +static int vsp1_pipeline_init(struct vsp1_pipeline *pipe, + struct vsp1_video *video) +{ + int ret; + + mutex_lock(&pipe->lock); + + /* If we're the first user validate and initialize the pipeline. */ + if (pipe->use_count == 0) { + ret = vsp1_pipeline_validate(pipe, video); + if (ret < 0) + goto done; + } + + pipe->use_count++; + ret = 0; + +done: + mutex_unlock(&pipe->lock); + return ret; +} + +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->lif = NULL; + } + + mutex_unlock(&pipe->lock); +} + +static void vsp1_pipeline_run(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + + vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index), VI6_CMD_STRCMD); + pipe->state = VSP1_PIPELINE_RUNNING; + pipe->buffers_ready = 0; +} + +static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) +{ + struct vsp1_entity *entity; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pipe->irqlock, flags); + pipe->state = VSP1_PIPELINE_STOPPING; + spin_unlock_irqrestore(&pipe->irqlock, flags); + + ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED, + msecs_to_jiffies(500)); + ret = ret == 0 ? -ETIMEDOUT : 0; + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + if (entity->route) + vsp1_write(entity->vsp1, entity->route, + VI6_DPR_NODE_UNUSED); + + v4l2_subdev_call(&entity->subdev, video, s_stream, 0); + } + + return ret; +} + +static bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) +{ + unsigned int mask; + + mask = ((1 << pipe->num_inputs) - 1) << 1; + if (!pipe->lif) + mask |= 1 << 0; + + return pipe->buffers_ready == mask; +} + +/* + * vsp1_video_complete_buffer - Complete the current buffer + * @video: the video node + * + * This function completes the current buffer by filling its sequence number, + * time stamp and payload size, and hands it back to the videobuf core. + * + * Return the next queued buffer or NULL if the queue is empty. + */ +static struct vsp1_video_buffer * +vsp1_video_complete_buffer(struct vsp1_video *video) +{ + struct vsp1_video_buffer *next = NULL; + struct vsp1_video_buffer *done; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&video->irqlock, flags); + + if (list_empty(&video->irqqueue)) { + spin_unlock_irqrestore(&video->irqlock, flags); + return NULL; + } + + done = list_first_entry(&video->irqqueue, + struct vsp1_video_buffer, queue); + list_del(&done->queue); + + if (!list_empty(&video->irqqueue)) + next = list_first_entry(&video->irqqueue, + struct vsp1_video_buffer, queue); + + spin_unlock_irqrestore(&video->irqlock, flags); + + done->buf.v4l2_buf.sequence = video->sequence++; + v4l2_get_timestamp(&done->buf.v4l2_buf.timestamp); + for (i = 0; i < done->buf.num_planes; ++i) + vb2_set_plane_payload(&done->buf, i, done->length[i]); + vb2_buffer_done(&done->buf, VB2_BUF_STATE_DONE); + + return next; +} + +static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, + struct vsp1_video *video) +{ + struct vsp1_video_buffer *buf; + unsigned long flags; + + buf = vsp1_video_complete_buffer(video); + if (buf == NULL) + return; + + spin_lock_irqsave(&pipe->irqlock, flags); + + video->ops->queue(video, buf); + pipe->buffers_ready |= 1 << video->pipe_index; + + spin_unlock_irqrestore(&pipe->irqlock, flags); +} + +void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) +{ + unsigned long flags; + unsigned int i; + + if (pipe == NULL) + return; + + /* Complete buffers on all video nodes. */ + for (i = 0; i < pipe->num_inputs; ++i) + vsp1_video_frame_end(pipe, &pipe->inputs[i]->video); + + if (!pipe->lif) + vsp1_video_frame_end(pipe, &pipe->output->video); + + spin_lock_irqsave(&pipe->irqlock, flags); + + /* If a stop has been requested, mark the pipeline as stopped and + * return. + */ + if (pipe->state == VSP1_PIPELINE_STOPPING) { + pipe->state = VSP1_PIPELINE_STOPPED; + wake_up(&pipe->wq); + goto done; + } + + /* Restart the pipeline if ready. */ + if (vsp1_pipeline_ready(pipe)) + vsp1_pipeline_run(pipe); + +done: + spin_unlock_irqrestore(&pipe->irqlock, flags); +} + +/* ----------------------------------------------------------------------------- + * videobuf2 Queue Operations + */ + +static int +vsp1_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct vsp1_video *video = vb2_get_drv_priv(vq); + const struct v4l2_pix_format_mplane *format; + struct v4l2_pix_format_mplane pix_mp; + unsigned int i; + + if (fmt) { + /* Make sure the format is valid and adjust the sizeimage field + * if needed. + */ + if (!vsp1_video_format_adjust(video, &fmt->fmt.pix_mp, &pix_mp)) + return -EINVAL; + + format = &pix_mp; + } else { + format = &video->format; + } + + *nplanes = format->num_planes; + + for (i = 0; i < format->num_planes; ++i) { + sizes[i] = format->plane_fmt[i].sizeimage; + alloc_ctxs[i] = video->alloc_ctx; + } + + return 0; +} + +static int vsp1_video_buffer_prepare(struct vb2_buffer *vb) +{ + struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb); + const struct v4l2_pix_format_mplane *format = &video->format; + unsigned int i; + + 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); + + if (buf->length[i] < format->plane_fmt[i].sizeimage) + return -EINVAL; + } + + return 0; +} + +static void vsp1_video_buffer_queue(struct vb2_buffer *vb) +{ + struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb); + unsigned long flags; + bool empty; + + spin_lock_irqsave(&video->irqlock, flags); + empty = list_empty(&video->irqqueue); + list_add_tail(&buf->queue, &video->irqqueue); + spin_unlock_irqrestore(&video->irqlock, flags); + + if (!empty) + return; + + spin_lock_irqsave(&pipe->irqlock, flags); + + video->ops->queue(video, buf); + pipe->buffers_ready |= 1 << video->pipe_index; + + if (vb2_is_streaming(&video->queue) && + vsp1_pipeline_ready(pipe)) + vsp1_pipeline_run(pipe); + + spin_unlock_irqrestore(&pipe->irqlock, flags); +} + +static void vsp1_entity_route_setup(struct vsp1_entity *source) +{ + struct vsp1_entity *sink; + + if (source->route == 0) + return; + + sink = container_of(source->sink, struct vsp1_entity, subdev.entity); + vsp1_write(source->vsp1, source->route, sink->id); +} + +static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vsp1_video *video = vb2_get_drv_priv(vq); + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + struct vsp1_entity *entity; + unsigned long flags; + int ret; + + mutex_lock(&pipe->lock); + if (pipe->stream_count == pipe->num_video - 1) { + list_for_each_entry(entity, &pipe->entities, list_pipe) { + vsp1_entity_route_setup(entity); + + ret = v4l2_subdev_call(&entity->subdev, video, + s_stream, 1); + if (ret < 0) { + mutex_unlock(&pipe->lock); + return ret; + } + } + } + + pipe->stream_count++; + mutex_unlock(&pipe->lock); + + spin_lock_irqsave(&pipe->irqlock, flags); + if (vsp1_pipeline_ready(pipe)) + vsp1_pipeline_run(pipe); + spin_unlock_irqrestore(&pipe->irqlock, flags); + + return 0; +} + +static int 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); + unsigned long flags; + int ret; + + mutex_lock(&pipe->lock); + if (--pipe->stream_count == 0) { + /* Stop the pipeline. */ + ret = vsp1_pipeline_stop(pipe); + if (ret == -ETIMEDOUT) + dev_err(video->vsp1->dev, "pipeline stop timeout\n"); + } + mutex_unlock(&pipe->lock); + + vsp1_pipeline_cleanup(pipe); + media_entity_pipeline_stop(&video->video.entity); + + /* Remove all buffers from the IRQ queue. */ + spin_lock_irqsave(&video->irqlock, flags); + INIT_LIST_HEAD(&video->irqqueue); + spin_unlock_irqrestore(&video->irqlock, flags); + + return 0; +} + +static struct vb2_ops vsp1_video_queue_qops = { + .queue_setup = vsp1_video_queue_setup, + .buf_prepare = vsp1_video_buffer_prepare, + .buf_queue = vsp1_video_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = vsp1_video_start_streaming, + .stop_streaming = vsp1_video_stop_streaming, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int +vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + + cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING + | V4L2_CAP_VIDEO_CAPTURE_MPLANE + | V4L2_CAP_VIDEO_OUTPUT_MPLANE; + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE + | V4L2_CAP_STREAMING; + else + cap->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE + | V4L2_CAP_STREAMING; + + strlcpy(cap->driver, "vsp1", sizeof(cap->driver)); + strlcpy(cap->card, video->video.name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(video->vsp1->dev)); + + return 0; +} + +static int +vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + + if (format->type != video->queue.type) + return -EINVAL; + + mutex_lock(&video->lock); + format->fmt.pix_mp = video->format; + mutex_unlock(&video->lock); + + return 0; +} + +static int +vsp1_video_try_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + + if (format->type != video->queue.type) + return -EINVAL; + + return __vsp1_video_try_format(video, &format->fmt.pix_mp, NULL); +} + +static int +vsp1_video_set_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + const struct vsp1_format_info *info; + int ret; + + if (format->type != video->queue.type) + return -EINVAL; + + ret = __vsp1_video_try_format(video, &format->fmt.pix_mp, &info); + if (ret < 0) + return ret; + + mutex_lock(&video->lock); + + if (vb2_is_busy(&video->queue)) { + ret = -EBUSY; + goto done; + } + + video->format = format->fmt.pix_mp; + video->fmtinfo = info; + +done: + mutex_unlock(&video->lock); + return ret; +} + +static int +vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + struct vsp1_pipeline *pipe; + int ret; + + mutex_lock(&video->lock); + + if (video->queue.owner && video->queue.owner != file->private_data) + return -EBUSY; + + video->sequence = 0; + + /* Start streaming on the pipeline. No link touching an entity in the + * pipeline can be activated or deactivated once streaming is started. + * + * Use the VSP1 pipeline object embedded in the first video object that + * starts streaming. + */ + pipe = video->video.entity.pipe + ? to_vsp1_pipeline(&video->video.entity) : &video->pipe; + + ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + if (ret < 0) + return ret; + + /* Verify that the configured format matches the output of the connected + * subdev. + */ + ret = vsp1_video_verify_format(video); + if (ret < 0) + goto err_stop; + + ret = vsp1_pipeline_init(pipe, video); + if (ret < 0) + goto err_stop; + + /* Start the queue. */ + ret = vb2_streamon(&video->queue, type); + if (ret < 0) + goto err_cleanup; + + return 0; + +err_cleanup: + vsp1_pipeline_cleanup(pipe); +err_stop: + media_entity_pipeline_stop(&video->video.entity); + return ret; +} + +static const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = { + .vidioc_querycap = vsp1_video_querycap, + .vidioc_g_fmt_vid_cap_mplane = vsp1_video_get_format, + .vidioc_s_fmt_vid_cap_mplane = vsp1_video_set_format, + .vidioc_try_fmt_vid_cap_mplane = vsp1_video_try_format, + .vidioc_g_fmt_vid_out_mplane = vsp1_video_get_format, + .vidioc_s_fmt_vid_out_mplane = vsp1_video_set_format, + .vidioc_try_fmt_vid_out_mplane = vsp1_video_try_format, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vsp1_video_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 File Operations + */ + +static int vsp1_video_open(struct file *file) +{ + struct vsp1_video *video = video_drvdata(file); + struct v4l2_fh *vfh; + int ret = 0; + + vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); + if (vfh == NULL) + return -ENOMEM; + + v4l2_fh_init(vfh, &video->video); + v4l2_fh_add(vfh); + + file->private_data = vfh; + + if (!vsp1_device_get(video->vsp1)) { + ret = -EBUSY; + v4l2_fh_del(vfh); + kfree(vfh); + } + + return ret; +} + +static int vsp1_video_release(struct file *file) +{ + struct vsp1_video *video = video_drvdata(file); + struct v4l2_fh *vfh = file->private_data; + + mutex_lock(&video->lock); + if (video->queue.owner == vfh) { + vb2_queue_release(&video->queue); + video->queue.owner = NULL; + } + mutex_unlock(&video->lock); + + vsp1_device_put(video->vsp1); + + v4l2_fh_release(file); + + file->private_data = NULL; + + return 0; +} + +static struct v4l2_file_operations vsp1_video_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = vsp1_video_open, + .release = vsp1_video_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf) +{ + const char *direction; + int ret; + + switch (video->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + direction = "output"; + video->pad.flags = MEDIA_PAD_FL_SINK; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + direction = "input"; + video->pad.flags = MEDIA_PAD_FL_SOURCE; + video->video.vfl_dir = VFL_DIR_TX; + break; + + default: + return -EINVAL; + } + + video->rwpf = rwpf; + + mutex_init(&video->lock); + spin_lock_init(&video->irqlock); + INIT_LIST_HEAD(&video->irqqueue); + + mutex_init(&video->pipe.lock); + spin_lock_init(&video->pipe.irqlock); + INIT_LIST_HEAD(&video->pipe.entities); + init_waitqueue_head(&video->pipe.wq); + video->pipe.state = VSP1_PIPELINE_STOPPED; + + /* Initialize the media entity... */ + ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); + if (ret < 0) + return ret; + + /* ... and the format ... */ + video->fmtinfo = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT); + video->format.pixelformat = video->fmtinfo->fourcc; + video->format.colorspace = V4L2_COLORSPACE_SRGB; + video->format.field = V4L2_FIELD_NONE; + video->format.width = VSP1_VIDEO_DEF_WIDTH; + video->format.height = VSP1_VIDEO_DEF_HEIGHT; + video->format.num_planes = 1; + video->format.plane_fmt[0].bytesperline = + video->format.width * video->fmtinfo->bpp[0] / 8; + video->format.plane_fmt[0].sizeimage = + video->format.plane_fmt[0].bytesperline * video->format.height; + + /* ... and the video node... */ + video->video.v4l2_dev = &video->vsp1->v4l2_dev; + video->video.fops = &vsp1_video_fops; + snprintf(video->video.name, sizeof(video->video.name), "%s %s", + rwpf->subdev.name, direction); + video->video.vfl_type = VFL_TYPE_GRABBER; + video->video.release = video_device_release_empty; + video->video.ioctl_ops = &vsp1_video_ioctl_ops; + + video_set_drvdata(&video->video, video); + + /* ... and the buffers queue... */ + video->alloc_ctx = vb2_dma_contig_init_ctx(video->vsp1->dev); + if (IS_ERR(video->alloc_ctx)) + goto error; + + video->queue.type = video->type; + video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + video->queue.lock = &video->lock; + video->queue.drv_priv = video; + video->queue.buf_struct_size = sizeof(struct vsp1_video_buffer); + video->queue.ops = &vsp1_video_queue_qops; + video->queue.mem_ops = &vb2_dma_contig_memops; + video->queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; + ret = vb2_queue_init(&video->queue); + if (ret < 0) { + dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n"); + goto error; + } + + /* ... and register the video device. */ + video->video.queue = &video->queue; + ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + dev_err(video->vsp1->dev, "failed to register video device\n"); + goto error; + } + + return 0; + +error: + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); + vsp1_video_cleanup(video); + return ret; +} + +void vsp1_video_cleanup(struct vsp1_video *video) +{ + if (video_is_registered(&video->video)) + video_unregister_device(&video->video); + + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); + media_entity_cleanup(&video->video.entity); +} diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h new file mode 100644 index 0000000..d8612a3 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -0,0 +1,144 @@ +/* + * vsp1_video.h -- R-Car VSP1 Video Node + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_VIDEO_H__ +#define __VSP1_VIDEO_H__ + +#include +#include +#include + +#include +#include + +struct vsp1_video; + +/* + * struct vsp1_format_info - VSP1 video format description + * @mbus: media bus format code + * @fourcc: V4L2 pixel format FCC identifier + * @planes: number of planes + * @bpp: bits per pixel + * @hwfmt: VSP1 hardware format + * @swap_yc: the Y and C components are swapped (Y comes before C) + * @swap_uv: the U and V components are swapped (V comes before U) + * @hsub: horizontal subsampling factor + * @vsub: vertical subsampling factor + */ +struct vsp1_format_info { + u32 fourcc; + unsigned int mbus; + unsigned int hwfmt; + unsigned int swap; + unsigned int planes; + unsigned int bpp[3]; + bool swap_yc; + bool swap_uv; + unsigned int hsub; + unsigned int vsub; +}; + +enum vsp1_pipeline_state { + VSP1_PIPELINE_STOPPED, + VSP1_PIPELINE_RUNNING, + VSP1_PIPELINE_STOPPING, +}; + +/* + * struct vsp1_pipeline - A VSP1 hardware pipeline + * @media: the media pipeline + * @irqlock: protects the pipeline state + * @lock: protects the pipeline use count and stream count + */ +struct vsp1_pipeline { + struct media_pipeline pipe; + + spinlock_t irqlock; + enum vsp1_pipeline_state state; + wait_queue_head_t wq; + + struct mutex lock; + unsigned int use_count; + unsigned int stream_count; + unsigned int buffers_ready; + + unsigned int num_video; + unsigned int num_inputs; + struct vsp1_rwpf *inputs[VPS1_MAX_RPF]; + struct vsp1_rwpf *output; + struct vsp1_entity *lif; + + struct list_head entities; +}; + +static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e) +{ + if (likely(e->pipe)) + return container_of(e->pipe, struct vsp1_pipeline, pipe); + else + return NULL; +} + +struct vsp1_video_buffer { + struct vsp1_video *video; + struct vb2_buffer buf; + struct list_head queue; + + dma_addr_t addr[3]; + unsigned int length[3]; +}; + +static inline struct vsp1_video_buffer * +to_vsp1_video_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct vsp1_video_buffer, buf); +} + +struct vsp1_video_operations { + void (*queue)(struct vsp1_video *video, struct vsp1_video_buffer *buf); +}; + +struct vsp1_video { + struct vsp1_device *vsp1; + struct vsp1_entity *rwpf; + + const struct vsp1_video_operations *ops; + + struct video_device video; + enum v4l2_buf_type type; + struct media_pad pad; + + struct mutex lock; + struct v4l2_pix_format_mplane format; + const struct vsp1_format_info *fmtinfo; + + struct vsp1_pipeline pipe; + unsigned int pipe_index; + + struct vb2_queue queue; + void *alloc_ctx; + spinlock_t irqlock; + struct list_head irqqueue; + unsigned int sequence; +}; + +static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev) +{ + return container_of(vdev, struct vsp1_video, video); +} + +int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf); +void vsp1_video_cleanup(struct vsp1_video *video); + +void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); + +#endif /* __VSP1_VIDEO_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c new file mode 100644 index 0000000..db4b85e --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -0,0 +1,233 @@ +/* + * vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + +#include + +#include "vsp1.h" +#include "vsp1_rwpf.h" +#include "vsp1_video.h" + +#define WPF_MAX_WIDTH 2048 +#define WPF_MAX_HEIGHT 2048 + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_wpf_read(struct vsp1_rwpf *wpf, u32 reg) +{ + return vsp1_read(wpf->entity.vsp1, + reg + wpf->entity.index * VI6_WPF_OFFSET); +} + +static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data) +{ + vsp1_write(wpf->entity.vsp1, + reg + wpf->entity.index * VI6_WPF_OFFSET, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) +{ + 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_mbus_framefmt *format = + &wpf->entity.formats[RWPF_PAD_SOURCE]; + unsigned int i; + u32 srcrpf = 0; + u32 outfmt = 0; + + if (!enable) { + vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); + return 0; + } + + /* Sources */ + for (i = 0; i < pipe->num_inputs; ++i) { + struct vsp1_rwpf *input = pipe->inputs[i]; + + srcrpf |= VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index); + } + + vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf); + + /* Destination stride. Cropping isn't supported yet. */ + if (!pipe->lif) { + struct v4l2_pix_format_mplane *format = &wpf->video.format; + + vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_Y, + format->plane_fmt[0].bytesperline); + if (format->num_planes > 1) + vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_C, + format->plane_fmt[1].bytesperline); + } + + vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, + format->width << VI6_WPF_SZCLIP_SIZE_SHIFT); + vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, + format->height << VI6_WPF_SZCLIP_SIZE_SHIFT); + + /* Format */ + if (!pipe->lif) { + const struct vsp1_format_info *fmtinfo = wpf->video.fmtinfo; + + outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; + + if (fmtinfo->swap_yc) + outfmt |= VI6_WPF_OUTFMT_SPYCS; + if (fmtinfo->swap_uv) + outfmt |= VI6_WPF_OUTFMT_SPUVS; + + vsp1_wpf_write(wpf, VI6_WPF_DSWAP, fmtinfo->swap); + } + + if (wpf->entity.formats[RWPF_PAD_SINK].code != + wpf->entity.formats[RWPF_PAD_SOURCE].code) + outfmt |= VI6_WPF_OUTFMT_CSC; + + vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt); + + vsp1_write(vsp1, VI6_DPR_WPF_FPORCH(wpf->entity.index), + VI6_DPR_WPF_FPORCH_FP_WPFN); + + vsp1_write(vsp1, VI6_WPF_WRBCK_CTRL, 0); + + /* Enable interrupts */ + vsp1_write(vsp1, VI6_WPF_IRQ_STA(wpf->entity.index), 0); + vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), + VI6_WFP_IRQ_ENB_FREE); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops wpf_video_ops = { + .s_stream = wpf_s_stream, +}; + +static struct v4l2_subdev_pad_ops wpf_pad_ops = { + .enum_mbus_code = vsp1_rwpf_enum_mbus_code, + .enum_frame_size = vsp1_rwpf_enum_frame_size, + .get_fmt = vsp1_rwpf_get_format, + .set_fmt = vsp1_rwpf_set_format, +}; + +static struct v4l2_subdev_ops wpf_ops = { + .video = &wpf_video_ops, + .pad = &wpf_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Video Device Operations + */ + +static void wpf_vdev_queue(struct vsp1_video *video, + struct vsp1_video_buffer *buf) +{ + struct vsp1_rwpf *wpf = container_of(video, struct vsp1_rwpf, video); + + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, buf->addr[0]); + if (buf->buf.num_planes > 1) + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, buf->addr[1]); + if (buf->buf.num_planes > 2) + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, buf->addr[2]); +} + +static const struct vsp1_video_operations wpf_vdev_ops = { + .queue = wpf_vdev_queue, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) +{ + struct v4l2_subdev *subdev; + struct vsp1_video *video; + struct vsp1_rwpf *wpf; + unsigned int flags; + int ret; + + wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL); + if (wpf == NULL) + return ERR_PTR(-ENOMEM); + + wpf->max_width = WPF_MAX_WIDTH; + wpf->max_height = WPF_MAX_HEIGHT; + + wpf->entity.type = VSP1_ENTITY_WPF; + wpf->entity.index = index; + wpf->entity.id = VI6_DPR_NODE_WPF(index); + + ret = vsp1_entity_init(vsp1, &wpf->entity, 2); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &wpf->entity.subdev; + v4l2_subdev_init(subdev, &wpf_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s wpf.%u", + dev_name(vsp1->dev), index); + v4l2_set_subdevdata(subdev, wpf); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + /* Initialize the video device. */ + video = &wpf->video; + + video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + video->vsp1 = vsp1; + video->ops = &wpf_vdev_ops; + + ret = vsp1_video_init(video, &wpf->entity); + if (ret < 0) + goto error_video; + + /* Connect the video device to the WPF. All connections are immutable + * except for the WPF0 source link if a LIF is present. + */ + flags = MEDIA_LNK_FL_ENABLED; + if (!(vsp1->pdata->features & VSP1_HAS_LIF) || index != 0) + flags |= MEDIA_LNK_FL_IMMUTABLE; + + ret = media_entity_create_link(&wpf->entity.subdev.entity, + RWPF_PAD_SOURCE, + &wpf->video.video.entity, 0, flags); + if (ret < 0) + goto error_link; + + wpf->entity.sink = &wpf->video.video.entity; + + return wpf; + +error_link: + vsp1_video_cleanup(video); +error_video: + media_entity_cleanup(&wpf->entity.subdev.entity); + return ERR_PTR(ret); +} diff --git a/include/linux/platform_data/vsp1.h b/include/linux/platform_data/vsp1.h new file mode 100644 index 0000000..a73a456 --- /dev/null +++ b/include/linux/platform_data/vsp1.h @@ -0,0 +1,25 @@ +/* + * vsp1.h -- R-Car VSP1 Platform Data + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __PLATFORM_VSP1_H__ +#define __PLATFORM_VSP1_H__ + +#define VSP1_HAS_LIF (1 << 0) + +struct vsp1_platform_data { + unsigned int features; + unsigned int rpf_count; + unsigned int uds_count; + unsigned int wpf_count; +}; + +#endif /* __PLATFORM_VSP1_H__ */ -- cgit v0.10.2 From db32eb6c18d7617e61ca65eb13b66de4dea56f1f Mon Sep 17 00:00:00 2001 From: Katsuya Matsubara Date: Fri, 26 Jul 2013 06:32:11 -0300 Subject: [media] vsp1: Fix lack of the sink entity registration for enabled links Each source entity maintains a pointer to the counterpart sink entity while an enabled link connects them. It should be managed by the setup_link callback in the media controller framework at runtime. However, enabled links which connect RPFs and WPFs that have an equivalent index number are created during initialization. This registers the pointer to a sink entity from the source entity when an enabled link is created. Signed-off-by: Katsuya Matsubara Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index e58e49c..41dd891 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -101,6 +101,9 @@ static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink) entity, pad, flags); if (ret < 0) return ret; + + if (flags & MEDIA_LNK_FL_ENABLED) + source->sink = entity; } } -- cgit v0.10.2 From 83453a7cc8b703ce818c24c2298822b0b179e652 Mon Sep 17 00:00:00 2001 From: Katsuya Matsubara Date: Fri, 26 Jul 2013 06:32:12 -0300 Subject: [media] vsp1: Use the maximum number of entities defined in platform data The VSP1 driver allows to define the maximum number of each module such as RPF, WPF, and UDS in a platform data definition. This suppresses operations for nonexistent or unused modules. Signed-off-by: Katsuya Matsubara Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 41dd891..8700842 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -35,7 +35,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) irqreturn_t ret = IRQ_NONE; unsigned int i; - for (i = 0; i < VPS1_MAX_WPF; ++i) { + for (i = 0; i < vsp1->pdata->wpf_count; ++i) { struct vsp1_rwpf *wpf = vsp1->wpf[i]; struct vsp1_pipeline *pipe; u32 status; @@ -243,7 +243,7 @@ static int vsp1_device_init(struct vsp1_device *vsp1) /* Reset any channel that might be running. */ status = vsp1_read(vsp1, VI6_STATUS); - for (i = 0; i < VPS1_MAX_WPF; ++i) { + for (i = 0; i < vsp1->pdata->wpf_count; ++i) { unsigned int timeout; if (!(status & VI6_STATUS_SYS_ACT(i))) @@ -267,10 +267,10 @@ static int vsp1_device_init(struct vsp1_device *vsp1) vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) | (8 << VI6_CLK_DCSWT_CSTRW_SHIFT)); - for (i = 0; i < VPS1_MAX_RPF; ++i) + for (i = 0; i < vsp1->pdata->rpf_count; ++i) vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED); - for (i = 0; i < VPS1_MAX_UDS; ++i) + for (i = 0; i < vsp1->pdata->uds_count; ++i) vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED); -- cgit v0.10.2 From 8514611438e9e324a9faeac69393c62ef76a5832 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 24 Jul 2013 02:04:12 -0300 Subject: [media] e4000: implement DC offset correction I did some tests against modulator (television signal generator) and this seems to improve sensitivity a little bit on channel I used. Used device was able to receive transmission just as weak signal as with Windows XP. Thanks to Jacek for donating his non-working device. I cannot ask he to test anymore... Reported-by: Jacek Konieczny Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index 1b33ed3..a3a9c87 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -140,14 +140,12 @@ static int e4000_init(struct dvb_frontend *fe) if (ret < 0) goto err; - /* - * TODO: Implement DC offset control correctly. - * DC offsets has quite much effect for received signal quality in case - * of direct conversion tuners (Zero-IF). Surely we will now lose few - * decimals or even decibels from SNR... - */ /* DC offset control */ - ret = e4000_wr_reg(priv, 0x2d, 0x0c); + ret = e4000_wr_reg(priv, 0x2d, 0x1f); + if (ret < 0) + goto err; + + ret = e4000_wr_regs(priv, 0x70, "\x01\x01", 2); if (ret < 0) goto err; @@ -204,7 +202,7 @@ static int e4000_set_params(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i, sigma_delta; unsigned int f_VCO; - u8 buf[5]; + u8 buf[5], i_data[4], q_data[4]; dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \ "bandwidth_hz=%d\n", __func__, @@ -292,6 +290,48 @@ static int e4000_set_params(struct dvb_frontend *fe) if (ret < 0) goto err; + /* DC offset */ + for (i = 0; i < 4; i++) { + if (i == 0) + ret = e4000_wr_regs(priv, 0x15, "\x00\x7e\x24", 3); + else if (i == 1) + ret = e4000_wr_regs(priv, 0x15, "\x00\x7f", 2); + else if (i == 2) + ret = e4000_wr_regs(priv, 0x15, "\x01", 1); + else + ret = e4000_wr_regs(priv, 0x16, "\x7e", 1); + + if (ret < 0) + goto err; + + ret = e4000_wr_reg(priv, 0x29, 0x01); + if (ret < 0) + goto err; + + ret = e4000_rd_regs(priv, 0x2a, buf, 3); + if (ret < 0) + goto err; + + i_data[i] = (((buf[2] >> 0) & 0x3) << 6) | (buf[0] & 0x3f); + q_data[i] = (((buf[2] >> 4) & 0x3) << 6) | (buf[1] & 0x3f); + } + + buf[0] = q_data[0]; + buf[1] = q_data[1]; + buf[2] = q_data[3]; + buf[3] = q_data[2]; + ret = e4000_wr_regs(priv, 0x50, buf, 4); + if (ret < 0) + goto err; + + buf[0] = i_data[0]; + buf[1] = i_data[1]; + buf[2] = i_data[3]; + buf[3] = i_data[2]; + ret = e4000_wr_regs(priv, 0x60, buf, 4); + if (ret < 0) + goto err; + /* gain control auto */ ret = e4000_wr_reg(priv, 0x1a, 0x17); if (ret < 0) -- cgit v0.10.2 From d4992da3872d1826f6dfe7f7c8add32fda99a5d1 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 24 Jul 2013 18:33:51 -0300 Subject: [media] e4000: use swap() macro Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index a3a9c87..f3b872a 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -316,19 +316,14 @@ static int e4000_set_params(struct dvb_frontend *fe) q_data[i] = (((buf[2] >> 4) & 0x3) << 6) | (buf[1] & 0x3f); } - buf[0] = q_data[0]; - buf[1] = q_data[1]; - buf[2] = q_data[3]; - buf[3] = q_data[2]; - ret = e4000_wr_regs(priv, 0x50, buf, 4); + swap(q_data[2], q_data[3]); + swap(i_data[2], i_data[3]); + + ret = e4000_wr_regs(priv, 0x50, q_data, 4); if (ret < 0) goto err; - buf[0] = i_data[0]; - buf[1] = i_data[1]; - buf[2] = i_data[3]; - buf[3] = i_data[2]; - ret = e4000_wr_regs(priv, 0x60, buf, 4); + ret = e4000_wr_regs(priv, 0x60, i_data, 4); if (ret < 0) goto err; -- cgit v0.10.2 From 4a337d5cdf723ee37f5533ad4a75df97f01bf32a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 24 Jul 2013 18:38:29 -0300 Subject: [media] e4000: make checkpatch.pl happy Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index f3b872a..ad9309d 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -41,8 +41,9 @@ static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len) if (ret == 1) { ret = 0; } else { - dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); ret = -EREMOTEIO; } return ret; @@ -72,8 +73,9 @@ static int e4000_rd_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len) memcpy(val, buf, len); ret = 0; } else { - dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); ret = -EREMOTEIO; } @@ -201,12 +203,13 @@ static int e4000_set_params(struct dvb_frontend *fe) struct e4000_priv *priv = fe->tuner_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i, sigma_delta; - unsigned int f_VCO; + unsigned int f_vco; u8 buf[5], i_data[4], q_data[4]; - dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \ - "bandwidth_hz=%d\n", __func__, - c->delivery_system, c->frequency, c->bandwidth_hz); + dev_dbg(&priv->i2c->dev, + "%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", + __func__, c->delivery_system, c->frequency, + c->bandwidth_hz); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -226,19 +229,19 @@ static int e4000_set_params(struct dvb_frontend *fe) goto err; /* - * Note: Currently f_VCO overflows when c->frequency is 1 073 741 824 Hz + * Note: Currently f_vco overflows when c->frequency is 1 073 741 824 Hz * or more. */ - f_VCO = c->frequency * e4000_pll_lut[i].mul; - sigma_delta = 0x10000UL * (f_VCO % priv->cfg->clock) / priv->cfg->clock; - buf[0] = f_VCO / priv->cfg->clock; + f_vco = c->frequency * e4000_pll_lut[i].mul; + sigma_delta = 0x10000UL * (f_vco % priv->cfg->clock) / priv->cfg->clock; + buf[0] = f_vco / priv->cfg->clock; buf[1] = (sigma_delta >> 0) & 0xff; buf[2] = (sigma_delta >> 8) & 0xff; buf[3] = 0x00; buf[4] = e4000_pll_lut[i].div; - dev_dbg(&priv->i2c->dev, "%s: f_VCO=%u pll div=%d sigma_delta=%04x\n", - __func__, f_VCO, buf[0], sigma_delta); + dev_dbg(&priv->i2c->dev, "%s: f_vco=%u pll div=%d sigma_delta=%04x\n", + __func__, f_vco, buf[0], sigma_delta); ret = e4000_wr_regs(priv, 0x09, buf, 5); if (ret < 0) -- cgit v0.10.2 From 00db719264ba937157c1dffd83cf25f9fbcc708a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 26 Jul 2013 06:57:51 -0300 Subject: [media] e4000: change remaining pr_warn to dev_warn Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/tuners/e4000.h b/drivers/media/tuners/e4000.h index 3783a0b..25ee7c0 100644 --- a/drivers/media/tuners/e4000.h +++ b/drivers/media/tuners/e4000.h @@ -44,7 +44,7 @@ extern struct dvb_frontend *e4000_attach(struct dvb_frontend *fe, static inline struct dvb_frontend *e4000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct e4000_config *cfg) { - pr_warn("%s: driver disabled by Kconfig\n", __func__); + dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif -- cgit v0.10.2 From e42efde4e4ce6bae9b728e8cc12ca73440204650 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 30 Jul 2013 16:33:16 -0300 Subject: [media] lme2510: do not use bInterfaceNumber from dvb_usb_v2 No need to access bInterfaceNumber via dvb_usb_v2 internals as driver has it already. That patch is prepare for dvb_usb_v2 deferred probe hack removal. It was added due to udev firmware loading problems, but things are fixed after that and it is not needed anymore. Signed-off-by: Antti Palosaari 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 b3fd0ff..f674dc0 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -1225,7 +1225,7 @@ static int lme2510_identify_state(struct dvb_usb_device *d, const char **name) usb_reset_configuration(d->udev); usb_set_interface(d->udev, - d->intf->cur_altsetting->desc.bInterfaceNumber, 1); + d->props->bInterfaceNumber, 1); st->dvb_usb_lme2510_firmware = dvb_usb_lme2510_firmware; -- cgit v0.10.2 From dd0f5e0b75540f7bfca99c5a664c61ac90e52514 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 30 Jul 2013 17:22:25 -0300 Subject: [media] dvb_usb_v2: get rid of deferred probe Deferred probe was added in order to avoid udev vs. Kernel firmware download problems. It is not needed anymore. https://bugzilla.redhat.com/show_bug.cgi?id=827538 Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 399916b..124b4ba 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -352,9 +352,7 @@ struct dvb_usb_adapter { * @rc_map: name of rc codes table * @rc_polling_active: set when RC polling is active * @udev: pointer to the device's struct usb_device - * @intf: pointer to the device's usb interface * @rc: remote controller configuration - * @probe_work: work to defer .probe() * @powered: indicated whether the device is power or not * @usb_mutex: mutex for usb control messages * @i2c_mutex: mutex for i2c-transfers @@ -370,10 +368,7 @@ struct dvb_usb_device { const char *rc_map; bool rc_polling_active; struct usb_device *udev; - struct usb_interface *intf; struct dvb_usb_rc rc; - struct work_struct probe_work; - pid_t work_pid; int powered; /* locking */ 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 19f6737..8a054d6 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -833,20 +833,44 @@ err: return ret; } -/* - * udev, which is used for the firmware downloading, requires we cannot - * block during module_init(). module_init() calls USB probe() which - * is this routine. Due to that we delay actual operation using workqueue - * and return always success here. - */ -static void dvb_usbv2_init_work(struct work_struct *work) +int dvb_usbv2_probe(struct usb_interface *intf, + const struct usb_device_id *id) { int ret; - struct dvb_usb_device *d = - container_of(work, struct dvb_usb_device, probe_work); + struct dvb_usb_device *d; + struct usb_device *udev = interface_to_usbdev(intf); + struct dvb_usb_driver_info *driver_info = + (struct dvb_usb_driver_info *) id->driver_info; + + dev_dbg(&udev->dev, "%s: bInterfaceNumber=%d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + + if (!id->driver_info) { + dev_err(&udev->dev, "%s: driver_info failed\n", KBUILD_MODNAME); + ret = -ENODEV; + goto err; + } + + d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL); + if (!d) { + dev_err(&udev->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + ret = -ENOMEM; + goto err; + } - d->work_pid = current->pid; - dev_dbg(&d->udev->dev, "%s: work_pid=%d\n", __func__, d->work_pid); + d->name = driver_info->name; + d->rc_map = driver_info->rc_map; + d->udev = udev; + d->props = driver_info->props; + + if (intf->cur_altsetting->desc.bInterfaceNumber != + d->props->bInterfaceNumber) { + ret = -ENODEV; + goto err_free_all; + } + + mutex_init(&d->usb_mutex); + mutex_init(&d->i2c_mutex); if (d->props->size_of_priv) { d->priv = kzalloc(d->props->size_of_priv, GFP_KERNEL); @@ -854,7 +878,7 @@ static void dvb_usbv2_init_work(struct work_struct *work) dev_err(&d->udev->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); ret = -ENOMEM; - goto err_usb_driver_release_interface; + goto err_free_all; } } @@ -884,20 +908,12 @@ static void dvb_usbv2_init_work(struct work_struct *work) * device. As 'new' device is warm we should * never go here again. */ - return; + goto exit; } else { - /* - * Unexpected error. We must unregister driver - * manually from the device, because device is - * already register by returning from probe() - * with success. usb_driver_release_interface() - * finally calls disconnect() in order to free - * resources. - */ - goto err_usb_driver_release_interface; + goto err_free_all; } } else { - goto err_usb_driver_release_interface; + goto err_free_all; } } @@ -906,73 +922,17 @@ static void dvb_usbv2_init_work(struct work_struct *work) ret = dvb_usbv2_init(d); if (ret < 0) - goto err_usb_driver_release_interface; + goto err_free_all; dev_info(&d->udev->dev, "%s: '%s' successfully initialized and connected\n", KBUILD_MODNAME, d->name); - - return; -err_usb_driver_release_interface: - dev_info(&d->udev->dev, "%s: '%s' error while loading driver (%d)\n", - KBUILD_MODNAME, d->name, ret); - usb_driver_release_interface(to_usb_driver(d->intf->dev.driver), - d->intf); - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); - return; -} - -int dvb_usbv2_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - int ret; - struct dvb_usb_device *d; - struct usb_device *udev = interface_to_usbdev(intf); - struct dvb_usb_driver_info *driver_info = - (struct dvb_usb_driver_info *) id->driver_info; - - dev_dbg(&udev->dev, "%s: bInterfaceNumber=%d\n", __func__, - intf->cur_altsetting->desc.bInterfaceNumber); - - if (!id->driver_info) { - dev_err(&udev->dev, "%s: driver_info failed\n", KBUILD_MODNAME); - ret = -ENODEV; - goto err; - } - - d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL); - if (!d) { - dev_err(&udev->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); - ret = -ENOMEM; - goto err; - } - - d->name = driver_info->name; - d->rc_map = driver_info->rc_map; - d->udev = udev; - d->intf = intf; - d->props = driver_info->props; - - if (d->intf->cur_altsetting->desc.bInterfaceNumber != - d->props->bInterfaceNumber) { - ret = -ENODEV; - goto err_kfree; - } - - mutex_init(&d->usb_mutex); - mutex_init(&d->i2c_mutex); - INIT_WORK(&d->probe_work, dvb_usbv2_init_work); +exit: usb_set_intfdata(intf, d); - ret = schedule_work(&d->probe_work); - if (ret < 0) { - dev_err(&d->udev->dev, "%s: schedule_work() failed\n", - KBUILD_MODNAME); - goto err_kfree; - } return 0; -err_kfree: - kfree(d); +err_free_all: + dvb_usbv2_exit(d); err: dev_dbg(&udev->dev, "%s: failed=%d\n", __func__, ret); return ret; @@ -984,12 +944,8 @@ void dvb_usbv2_disconnect(struct usb_interface *intf) struct dvb_usb_device *d = usb_get_intfdata(intf); const char *name = d->name; struct device dev = d->udev->dev; - dev_dbg(&d->udev->dev, "%s: pid=%d work_pid=%d\n", __func__, - current->pid, d->work_pid); - - /* ensure initialization work is finished until release resources */ - if (d->work_pid != current->pid) - cancel_work_sync(&d->probe_work); + dev_dbg(&d->udev->dev, "%s: bInterfaceNumber=%d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); if (d->props->exit) d->props->exit(d); -- cgit v0.10.2 From 59b564599bc66f086856b09e480f89555d47b35c Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Sun, 28 Jul 2013 16:01:43 -0300 Subject: [media] tea575x: Move header from sound to media Move include/sound/tea575x-tuner.h to include/media/tea575x.h and update files that include it. Signed-off-by: Ondrej Zary Acked-by: Takashi Iwai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c index 1d1c9e1..5236035 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index 9c09904..f1e3714 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -14,7 +14,7 @@ #include /* outb, outb_p */ #include #include -#include +#include MODULE_AUTHOR("Ondrej Zary"); MODULE_DESCRIPTION("MediaForte SF16-FMR2 and SF16-FMD2 FM radio card driver"); diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c index 8fa18ab..b914772 100644 --- a/drivers/media/radio/radio-shark.c +++ b/drivers/media/radio/radio-shark.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #if defined(CONFIG_LEDS_CLASS) || \ (defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK_MODULE)) diff --git a/include/media/tea575x.h b/include/media/tea575x.h new file mode 100644 index 0000000..2d4fa59 --- /dev/null +++ b/include/media/tea575x.h @@ -0,0 +1,79 @@ +#ifndef __SOUND_TEA575X_TUNER_H +#define __SOUND_TEA575X_TUNER_H + +/* + * ALSA driver for TEA5757/5759 Philips AM/FM tuner chips + * + * Copyright (c) 2004 Jaroslav Kysela + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#define TEA575X_FMIF 10700 +#define TEA575X_AMIF 450 + +#define TEA575X_DATA (1 << 0) +#define TEA575X_CLK (1 << 1) +#define TEA575X_WREN (1 << 2) +#define TEA575X_MOST (1 << 3) + +struct snd_tea575x; + +struct snd_tea575x_ops { + /* Drivers using snd_tea575x must either define read_ and write_val */ + void (*write_val)(struct snd_tea575x *tea, u32 val); + u32 (*read_val)(struct snd_tea575x *tea); + /* Or define the 3 pin functions */ + void (*set_pins)(struct snd_tea575x *tea, u8 pins); + u8 (*get_pins)(struct snd_tea575x *tea); + void (*set_direction)(struct snd_tea575x *tea, bool output); +}; + +struct snd_tea575x { + struct v4l2_device *v4l2_dev; + struct v4l2_file_operations fops; + struct video_device vd; /* video device */ + int radio_nr; /* radio_nr */ + bool tea5759; /* 5759 chip is present */ + bool has_am; /* Device can tune to AM freqs */ + bool cannot_read_data; /* Device cannot read the data pin */ + bool cannot_mute; /* Device cannot mute */ + bool mute; /* Device is muted? */ + bool stereo; /* receiving stereo */ + bool tuned; /* tuned to a station */ + unsigned int val; /* hw value */ + u32 band; /* 0: FM, 1: FM-Japan, 2: AM */ + u32 freq; /* frequency */ + struct mutex mutex; + struct snd_tea575x_ops *ops; + void *private_data; + u8 card[32]; + u8 bus_info[32]; + struct v4l2_ctrl_handler ctrl_handler; + int (*ext_init)(struct snd_tea575x *tea); +}; + +int snd_tea575x_hw_init(struct snd_tea575x *tea); +int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner); +void snd_tea575x_exit(struct snd_tea575x *tea); +void snd_tea575x_set_freq(struct snd_tea575x *tea); + +#endif /* __SOUND_TEA575X_TUNER_H */ diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h deleted file mode 100644 index 2d4fa59..0000000 --- a/include/sound/tea575x-tuner.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef __SOUND_TEA575X_TUNER_H -#define __SOUND_TEA575X_TUNER_H - -/* - * ALSA driver for TEA5757/5759 Philips AM/FM tuner chips - * - * Copyright (c) 2004 Jaroslav Kysela - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include - -#define TEA575X_FMIF 10700 -#define TEA575X_AMIF 450 - -#define TEA575X_DATA (1 << 0) -#define TEA575X_CLK (1 << 1) -#define TEA575X_WREN (1 << 2) -#define TEA575X_MOST (1 << 3) - -struct snd_tea575x; - -struct snd_tea575x_ops { - /* Drivers using snd_tea575x must either define read_ and write_val */ - void (*write_val)(struct snd_tea575x *tea, u32 val); - u32 (*read_val)(struct snd_tea575x *tea); - /* Or define the 3 pin functions */ - void (*set_pins)(struct snd_tea575x *tea, u8 pins); - u8 (*get_pins)(struct snd_tea575x *tea); - void (*set_direction)(struct snd_tea575x *tea, bool output); -}; - -struct snd_tea575x { - struct v4l2_device *v4l2_dev; - struct v4l2_file_operations fops; - struct video_device vd; /* video device */ - int radio_nr; /* radio_nr */ - bool tea5759; /* 5759 chip is present */ - bool has_am; /* Device can tune to AM freqs */ - bool cannot_read_data; /* Device cannot read the data pin */ - bool cannot_mute; /* Device cannot mute */ - bool mute; /* Device is muted? */ - bool stereo; /* receiving stereo */ - bool tuned; /* tuned to a station */ - unsigned int val; /* hw value */ - u32 band; /* 0: FM, 1: FM-Japan, 2: AM */ - u32 freq; /* frequency */ - struct mutex mutex; - struct snd_tea575x_ops *ops; - void *private_data; - u8 card[32]; - u8 bus_info[32]; - struct v4l2_ctrl_handler ctrl_handler; - int (*ext_init)(struct snd_tea575x *tea); -}; - -int snd_tea575x_hw_init(struct snd_tea575x *tea); -int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner); -void snd_tea575x_exit(struct snd_tea575x *tea); -void snd_tea575x_set_freq(struct snd_tea575x *tea); - -#endif /* __SOUND_TEA575X_TUNER_H */ diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 46ec4dff..cef0698 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 5e2ec96..b0e3d92 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -113,7 +113,7 @@ #include #ifdef CONFIG_SND_ES1968_RADIO -#include +#include #endif #define CARD_NAME "ESS Maestro1/2" diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 706c5b6..45bc8a9 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -37,7 +37,7 @@ #include #ifdef CONFIG_SND_FM801_TEA575X_BOOL -#include +#include #endif MODULE_AUTHOR("Jaroslav Kysela "); -- cgit v0.10.2 From 338c658a646721e94ca827c74163703612e164c5 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Sun, 28 Jul 2013 16:01:44 -0300 Subject: [media] tea575x: Move from sound to media Move tea575x from sound/i2c/other to drivers/media/radio Includes Kconfig changes by Hans Verkuil. Signed-off-by: Hans Verkuil Signed-off-by: Ondrej Zary Acked-by: Takashi Iwai Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index d529ba7..39882dd 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -12,6 +12,9 @@ menuconfig RADIO_ADAPTERS if RADIO_ADAPTERS && VIDEO_V4L2 +config RADIO_TEA575X + tristate + config RADIO_SI470X bool "Silicon Labs Si470x FM Radio Receiver support" depends on VIDEO_V4L2 @@ -61,7 +64,8 @@ config USB_DSBR config RADIO_MAXIRADIO tristate "Guillemot MAXI Radio FM 2000 radio" - depends on VIDEO_V4L2 && PCI && SND + depends on VIDEO_V4L2 && PCI + select RADIO_TEA575X ---help--- Choose Y here if you have this radio card. This card may also be found as Gemtek PCI FM. @@ -76,7 +80,8 @@ config RADIO_MAXIRADIO config RADIO_SHARK tristate "Griffin radioSHARK USB radio receiver" - depends on USB && SND + depends on USB + select RADIO_TEA575X ---help--- Choose Y here if you have this radio receiver. @@ -393,7 +398,8 @@ config RADIO_SF16FMI config RADIO_SF16FMR2 tristate "SF16-FMR2/SF16-FMD2 Radio" - depends on ISA && VIDEO_V4L2 && SND + depends on ISA && VIDEO_V4L2 + select RADIO_TEA575X ---help--- Choose Y here if you have one of these FM radio cards. diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 0dcdb32..3b64560 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_RADIO_TEF6862) += tef6862.o obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o obj-$(CONFIG_RADIO_WL128X) += wl128x/ +obj-$(CONFIG_RADIO_TEA575X) += tea575x.o shark2-objs := radio-shark2.o radio-tea5777.o diff --git a/drivers/media/radio/tea575x.c b/drivers/media/radio/tea575x.c new file mode 100644 index 0000000..cef0698 --- /dev/null +++ b/drivers/media/radio/tea575x.c @@ -0,0 +1,584 @@ +/* + * ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips + * + * Copyright (c) 2004 Jaroslav Kysela + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); +MODULE_LICENSE("GPL"); + +/* + * definitions + */ + +#define TEA575X_BIT_SEARCH (1<<24) /* 1 = search action, 0 = tuned */ +#define TEA575X_BIT_UPDOWN (1<<23) /* 0 = search down, 1 = search up */ +#define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */ +#define TEA575X_BIT_BAND_MASK (3<<20) +#define TEA575X_BIT_BAND_FM (0<<20) +#define TEA575X_BIT_BAND_MW (1<<20) +#define TEA575X_BIT_BAND_LW (2<<20) +#define TEA575X_BIT_BAND_SW (3<<20) +#define TEA575X_BIT_PORT_0 (1<<19) /* user bit */ +#define TEA575X_BIT_PORT_1 (1<<18) /* user bit */ +#define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */ +#define TEA575X_BIT_SEARCH_5_28 (0<<16) /* FM >5uV, AM >28uV */ +#define TEA575X_BIT_SEARCH_10_40 (1<<16) /* FM >10uV, AM > 40uV */ +#define TEA575X_BIT_SEARCH_30_63 (2<<16) /* FM >30uV, AM > 63uV */ +#define TEA575X_BIT_SEARCH_150_1000 (3<<16) /* FM > 150uV, AM > 1000uV */ +#define TEA575X_BIT_DUMMY (1<<15) /* buffer */ +#define TEA575X_BIT_FREQ_MASK 0x7fff + +enum { BAND_FM, BAND_FM_JAPAN, BAND_AM }; + +static const struct v4l2_frequency_band bands[] = { + { + .type = V4L2_TUNER_RADIO, + .index = 0, + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 87500 * 16, + .rangehigh = 108000 * 16, + .modulation = V4L2_BAND_MODULATION_FM, + }, + { + .type = V4L2_TUNER_RADIO, + .index = 0, + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 76000 * 16, + .rangehigh = 91000 * 16, + .modulation = V4L2_BAND_MODULATION_FM, + }, + { + .type = V4L2_TUNER_RADIO, + .index = 1, + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 530 * 16, + .rangehigh = 1710 * 16, + .modulation = V4L2_BAND_MODULATION_AM, + }, +}; + +/* + * lowlevel part + */ + +static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val) +{ + u16 l; + u8 data; + + if (tea->ops->write_val) + return tea->ops->write_val(tea, val); + + tea->ops->set_direction(tea, 1); + udelay(16); + + for (l = 25; l > 0; l--) { + data = (val >> 24) & TEA575X_DATA; + val <<= 1; /* shift data */ + tea->ops->set_pins(tea, data | TEA575X_WREN); + udelay(2); + tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK); + udelay(2); + tea->ops->set_pins(tea, data | TEA575X_WREN); + udelay(2); + } + + if (!tea->mute) + tea->ops->set_pins(tea, 0); +} + +static u32 snd_tea575x_read(struct snd_tea575x *tea) +{ + u16 l, rdata; + u32 data = 0; + + if (tea->ops->read_val) + return tea->ops->read_val(tea); + + tea->ops->set_direction(tea, 0); + tea->ops->set_pins(tea, 0); + udelay(16); + + for (l = 24; l--;) { + tea->ops->set_pins(tea, TEA575X_CLK); + udelay(2); + if (!l) + tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1; + tea->ops->set_pins(tea, 0); + udelay(2); + data <<= 1; /* shift data */ + rdata = tea->ops->get_pins(tea); + if (!l) + tea->stereo = (rdata & TEA575X_MOST) ? 0 : 1; + if (rdata & TEA575X_DATA) + data++; + udelay(2); + } + + if (tea->mute) + tea->ops->set_pins(tea, TEA575X_WREN); + + return data; +} + +static u32 snd_tea575x_val_to_freq(struct snd_tea575x *tea, u32 val) +{ + u32 freq = val & TEA575X_BIT_FREQ_MASK; + + if (freq == 0) + return freq; + + switch (tea->band) { + case BAND_FM: + /* freq *= 12.5 */ + freq *= 125; + freq /= 10; + /* crystal fixup */ + freq -= TEA575X_FMIF; + break; + case BAND_FM_JAPAN: + /* freq *= 12.5 */ + freq *= 125; + freq /= 10; + /* crystal fixup */ + freq += TEA575X_FMIF; + break; + case BAND_AM: + /* crystal fixup */ + freq -= TEA575X_AMIF; + break; + } + + return clamp(freq * 16, bands[tea->band].rangelow, + bands[tea->band].rangehigh); /* from kHz */ +} + +static u32 snd_tea575x_get_freq(struct snd_tea575x *tea) +{ + return snd_tea575x_val_to_freq(tea, snd_tea575x_read(tea)); +} + +void snd_tea575x_set_freq(struct snd_tea575x *tea) +{ + u32 freq = tea->freq / 16; /* to kHz */ + u32 band = 0; + + switch (tea->band) { + case BAND_FM: + band = TEA575X_BIT_BAND_FM; + /* crystal fixup */ + freq += TEA575X_FMIF; + /* freq /= 12.5 */ + freq *= 10; + freq /= 125; + break; + case BAND_FM_JAPAN: + band = TEA575X_BIT_BAND_FM; + /* crystal fixup */ + freq -= TEA575X_FMIF; + /* freq /= 12.5 */ + freq *= 10; + freq /= 125; + break; + case BAND_AM: + band = TEA575X_BIT_BAND_MW; + /* crystal fixup */ + freq += TEA575X_AMIF; + break; + } + + tea->val &= ~(TEA575X_BIT_FREQ_MASK | TEA575X_BIT_BAND_MASK); + tea->val |= band; + tea->val |= freq & TEA575X_BIT_FREQ_MASK; + snd_tea575x_write(tea, tea->val); + tea->freq = snd_tea575x_val_to_freq(tea, tea->val); +} + +/* + * Linux Video interface + */ + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + struct snd_tea575x *tea = video_drvdata(file); + + strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver)); + strlcpy(v->card, tea->card, sizeof(v->card)); + strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card)); + strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info)); + v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + if (!tea->cannot_read_data) + v->device_caps |= V4L2_CAP_HW_FREQ_SEEK; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int vidioc_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + struct snd_tea575x *tea = video_drvdata(file); + int index; + + if (band->tuner != 0) + return -EINVAL; + + switch (band->index) { + case 0: + if (tea->tea5759) + index = BAND_FM_JAPAN; + else + index = BAND_FM; + break; + case 1: + if (tea->has_am) { + index = BAND_AM; + break; + } + /* Fall through */ + default: + return -EINVAL; + } + + *band = bands[index]; + if (!tea->cannot_read_data) + band->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED; + + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct snd_tea575x *tea = video_drvdata(file); + struct v4l2_frequency_band band_fm = { 0, }; + + if (v->index > 0) + return -EINVAL; + + snd_tea575x_read(tea); + vidioc_enum_freq_bands(file, priv, &band_fm); + + memset(v, 0, sizeof(*v)); + strlcpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name)); + v->type = V4L2_TUNER_RADIO; + v->capability = band_fm.capability; + v->rangelow = tea->has_am ? bands[BAND_AM].rangelow : band_fm.rangelow; + v->rangehigh = band_fm.rangehigh; + v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; + v->audmode = (tea->val & TEA575X_BIT_MONO) ? + V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO; + v->signal = tea->tuned ? 0xffff : 0; + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *v) +{ + struct snd_tea575x *tea = video_drvdata(file); + u32 orig_val = tea->val; + + if (v->index) + return -EINVAL; + tea->val &= ~TEA575X_BIT_MONO; + if (v->audmode == V4L2_TUNER_MODE_MONO) + tea->val |= TEA575X_BIT_MONO; + /* Only apply changes if currently tuning FM */ + if (tea->band != BAND_AM && tea->val != orig_val) + snd_tea575x_set_freq(tea); + + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct snd_tea575x *tea = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = tea->freq; + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct snd_tea575x *tea = video_drvdata(file); + + if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) + return -EINVAL; + + if (tea->has_am && f->frequency < (20000 * 16)) + tea->band = BAND_AM; + else if (tea->tea5759) + tea->band = BAND_FM_JAPAN; + else + tea->band = BAND_FM; + + tea->freq = clamp_t(u32, f->frequency, bands[tea->band].rangelow, + bands[tea->band].rangehigh); + snd_tea575x_set_freq(tea); + return 0; +} + +static int vidioc_s_hw_freq_seek(struct file *file, void *fh, + const struct v4l2_hw_freq_seek *a) +{ + struct snd_tea575x *tea = video_drvdata(file); + unsigned long timeout; + int i, spacing; + + if (tea->cannot_read_data) + return -ENOTTY; + if (a->tuner || a->wrap_around) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + if (a->rangelow || a->rangehigh) { + for (i = 0; i < ARRAY_SIZE(bands); i++) { + if ((i == BAND_FM && tea->tea5759) || + (i == BAND_FM_JAPAN && !tea->tea5759) || + (i == BAND_AM && !tea->has_am)) + continue; + if (bands[i].rangelow == a->rangelow && + bands[i].rangehigh == a->rangehigh) + break; + } + if (i == ARRAY_SIZE(bands)) + return -EINVAL; /* No matching band found */ + if (i != tea->band) { + tea->band = i; + tea->freq = clamp(tea->freq, bands[i].rangelow, + bands[i].rangehigh); + snd_tea575x_set_freq(tea); + } + } + + spacing = (tea->band == BAND_AM) ? 5 : 50; /* kHz */ + + /* clear the frequency, HW will fill it in */ + tea->val &= ~TEA575X_BIT_FREQ_MASK; + tea->val |= TEA575X_BIT_SEARCH; + if (a->seek_upward) + tea->val |= TEA575X_BIT_UPDOWN; + else + tea->val &= ~TEA575X_BIT_UPDOWN; + snd_tea575x_write(tea, tea->val); + timeout = jiffies + msecs_to_jiffies(10000); + for (;;) { + if (time_after(jiffies, timeout)) + break; + if (schedule_timeout_interruptible(msecs_to_jiffies(10))) { + /* some signal arrived, stop search */ + tea->val &= ~TEA575X_BIT_SEARCH; + snd_tea575x_set_freq(tea); + return -ERESTARTSYS; + } + if (!(snd_tea575x_read(tea) & TEA575X_BIT_SEARCH)) { + u32 freq; + + /* Found a frequency, wait until it can be read */ + for (i = 0; i < 100; i++) { + msleep(10); + freq = snd_tea575x_get_freq(tea); + if (freq) /* available */ + break; + } + if (freq == 0) /* shouldn't happen */ + break; + /* + * if we moved by less than the spacing, or in the + * wrong direction, continue seeking + */ + if (abs(tea->freq - freq) < 16 * spacing || + (a->seek_upward && freq < tea->freq) || + (!a->seek_upward && freq > tea->freq)) { + snd_tea575x_write(tea, tea->val); + continue; + } + tea->freq = freq; + tea->val &= ~TEA575X_BIT_SEARCH; + return 0; + } + } + tea->val &= ~TEA575X_BIT_SEARCH; + snd_tea575x_set_freq(tea); + return -ENODATA; +} + +static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + tea->mute = ctrl->val; + snd_tea575x_set_freq(tea); + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_file_operations tea575x_fops = { + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, +}; + +static const struct v4l2_ioctl_ops tea575x_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .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_s_hw_freq_seek = vidioc_s_hw_freq_seek, + .vidioc_enum_freq_bands = vidioc_enum_freq_bands, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct video_device tea575x_radio = { + .ioctl_ops = &tea575x_ioctl_ops, + .release = video_device_release_empty, +}; + +static const struct v4l2_ctrl_ops tea575x_ctrl_ops = { + .s_ctrl = tea575x_s_ctrl, +}; + + +int snd_tea575x_hw_init(struct snd_tea575x *tea) +{ + tea->mute = true; + + /* Not all devices can or know how to read the data back. + Such devices can set cannot_read_data to true. */ + if (!tea->cannot_read_data) { + snd_tea575x_write(tea, 0x55AA); + if (snd_tea575x_read(tea) != 0x55AA) + return -ENODEV; + } + + tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_5_28; + tea->freq = 90500 * 16; /* 90.5Mhz default */ + snd_tea575x_set_freq(tea); + + return 0; +} +EXPORT_SYMBOL(snd_tea575x_hw_init); + +int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner) +{ + int retval = snd_tea575x_hw_init(tea); + + if (retval) + return retval; + + tea->vd = tea575x_radio; + video_set_drvdata(&tea->vd, tea); + mutex_init(&tea->mutex); + strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name)); + tea->vd.lock = &tea->mutex; + tea->vd.v4l2_dev = tea->v4l2_dev; + 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); + + if (!tea->cannot_mute) { + tea->vd.ctrl_handler = &tea->ctrl_handler; + v4l2_ctrl_handler_init(&tea->ctrl_handler, 1); + v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + retval = tea->ctrl_handler.error; + if (retval) { + v4l2_err(tea->v4l2_dev, "can't initialize controls\n"); + v4l2_ctrl_handler_free(&tea->ctrl_handler); + return retval; + } + + if (tea->ext_init) { + retval = tea->ext_init(tea); + if (retval) { + v4l2_ctrl_handler_free(&tea->ctrl_handler); + return retval; + } + } + + v4l2_ctrl_handler_setup(&tea->ctrl_handler); + } + + retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->radio_nr); + if (retval) { + v4l2_err(tea->v4l2_dev, "can't register video device!\n"); + v4l2_ctrl_handler_free(tea->vd.ctrl_handler); + return retval; + } + + return 0; +} + +void snd_tea575x_exit(struct snd_tea575x *tea) +{ + video_unregister_device(&tea->vd); + v4l2_ctrl_handler_free(tea->vd.ctrl_handler); +} + +static int __init alsa_tea575x_module_init(void) +{ + return 0; +} + +static void __exit alsa_tea575x_module_exit(void) +{ +} + +module_init(alsa_tea575x_module_init) +module_exit(alsa_tea575x_module_exit) + +EXPORT_SYMBOL(snd_tea575x_init); +EXPORT_SYMBOL(snd_tea575x_exit); +EXPORT_SYMBOL(snd_tea575x_set_freq); diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile index c95d8f1..5526b03 100644 --- a/sound/i2c/other/Makefile +++ b/sound/i2c/other/Makefile @@ -8,10 +8,8 @@ snd-ak4117-objs := ak4117.o snd-ak4113-objs := ak4113.o snd-ak4xxx-adda-objs := ak4xxx-adda.o snd-pt2258-objs := pt2258.o -snd-tea575x-tuner-objs := tea575x-tuner.o # Module Dependency obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o -obj-$(CONFIG_SND_TEA575X) += snd-tea575x-tuner.o diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c deleted file mode 100644 index cef0698..0000000 --- a/sound/i2c/other/tea575x-tuner.c +++ /dev/null @@ -1,584 +0,0 @@ -/* - * ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips - * - * Copyright (c) 2004 Jaroslav Kysela - * - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Jaroslav Kysela "); -MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); -MODULE_LICENSE("GPL"); - -/* - * definitions - */ - -#define TEA575X_BIT_SEARCH (1<<24) /* 1 = search action, 0 = tuned */ -#define TEA575X_BIT_UPDOWN (1<<23) /* 0 = search down, 1 = search up */ -#define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */ -#define TEA575X_BIT_BAND_MASK (3<<20) -#define TEA575X_BIT_BAND_FM (0<<20) -#define TEA575X_BIT_BAND_MW (1<<20) -#define TEA575X_BIT_BAND_LW (2<<20) -#define TEA575X_BIT_BAND_SW (3<<20) -#define TEA575X_BIT_PORT_0 (1<<19) /* user bit */ -#define TEA575X_BIT_PORT_1 (1<<18) /* user bit */ -#define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */ -#define TEA575X_BIT_SEARCH_5_28 (0<<16) /* FM >5uV, AM >28uV */ -#define TEA575X_BIT_SEARCH_10_40 (1<<16) /* FM >10uV, AM > 40uV */ -#define TEA575X_BIT_SEARCH_30_63 (2<<16) /* FM >30uV, AM > 63uV */ -#define TEA575X_BIT_SEARCH_150_1000 (3<<16) /* FM > 150uV, AM > 1000uV */ -#define TEA575X_BIT_DUMMY (1<<15) /* buffer */ -#define TEA575X_BIT_FREQ_MASK 0x7fff - -enum { BAND_FM, BAND_FM_JAPAN, BAND_AM }; - -static const struct v4l2_frequency_band bands[] = { - { - .type = V4L2_TUNER_RADIO, - .index = 0, - .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 87500 * 16, - .rangehigh = 108000 * 16, - .modulation = V4L2_BAND_MODULATION_FM, - }, - { - .type = V4L2_TUNER_RADIO, - .index = 0, - .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 76000 * 16, - .rangehigh = 91000 * 16, - .modulation = V4L2_BAND_MODULATION_FM, - }, - { - .type = V4L2_TUNER_RADIO, - .index = 1, - .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, - .rangelow = 530 * 16, - .rangehigh = 1710 * 16, - .modulation = V4L2_BAND_MODULATION_AM, - }, -}; - -/* - * lowlevel part - */ - -static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val) -{ - u16 l; - u8 data; - - if (tea->ops->write_val) - return tea->ops->write_val(tea, val); - - tea->ops->set_direction(tea, 1); - udelay(16); - - for (l = 25; l > 0; l--) { - data = (val >> 24) & TEA575X_DATA; - val <<= 1; /* shift data */ - tea->ops->set_pins(tea, data | TEA575X_WREN); - udelay(2); - tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK); - udelay(2); - tea->ops->set_pins(tea, data | TEA575X_WREN); - udelay(2); - } - - if (!tea->mute) - tea->ops->set_pins(tea, 0); -} - -static u32 snd_tea575x_read(struct snd_tea575x *tea) -{ - u16 l, rdata; - u32 data = 0; - - if (tea->ops->read_val) - return tea->ops->read_val(tea); - - tea->ops->set_direction(tea, 0); - tea->ops->set_pins(tea, 0); - udelay(16); - - for (l = 24; l--;) { - tea->ops->set_pins(tea, TEA575X_CLK); - udelay(2); - if (!l) - tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1; - tea->ops->set_pins(tea, 0); - udelay(2); - data <<= 1; /* shift data */ - rdata = tea->ops->get_pins(tea); - if (!l) - tea->stereo = (rdata & TEA575X_MOST) ? 0 : 1; - if (rdata & TEA575X_DATA) - data++; - udelay(2); - } - - if (tea->mute) - tea->ops->set_pins(tea, TEA575X_WREN); - - return data; -} - -static u32 snd_tea575x_val_to_freq(struct snd_tea575x *tea, u32 val) -{ - u32 freq = val & TEA575X_BIT_FREQ_MASK; - - if (freq == 0) - return freq; - - switch (tea->band) { - case BAND_FM: - /* freq *= 12.5 */ - freq *= 125; - freq /= 10; - /* crystal fixup */ - freq -= TEA575X_FMIF; - break; - case BAND_FM_JAPAN: - /* freq *= 12.5 */ - freq *= 125; - freq /= 10; - /* crystal fixup */ - freq += TEA575X_FMIF; - break; - case BAND_AM: - /* crystal fixup */ - freq -= TEA575X_AMIF; - break; - } - - return clamp(freq * 16, bands[tea->band].rangelow, - bands[tea->band].rangehigh); /* from kHz */ -} - -static u32 snd_tea575x_get_freq(struct snd_tea575x *tea) -{ - return snd_tea575x_val_to_freq(tea, snd_tea575x_read(tea)); -} - -void snd_tea575x_set_freq(struct snd_tea575x *tea) -{ - u32 freq = tea->freq / 16; /* to kHz */ - u32 band = 0; - - switch (tea->band) { - case BAND_FM: - band = TEA575X_BIT_BAND_FM; - /* crystal fixup */ - freq += TEA575X_FMIF; - /* freq /= 12.5 */ - freq *= 10; - freq /= 125; - break; - case BAND_FM_JAPAN: - band = TEA575X_BIT_BAND_FM; - /* crystal fixup */ - freq -= TEA575X_FMIF; - /* freq /= 12.5 */ - freq *= 10; - freq /= 125; - break; - case BAND_AM: - band = TEA575X_BIT_BAND_MW; - /* crystal fixup */ - freq += TEA575X_AMIF; - break; - } - - tea->val &= ~(TEA575X_BIT_FREQ_MASK | TEA575X_BIT_BAND_MASK); - tea->val |= band; - tea->val |= freq & TEA575X_BIT_FREQ_MASK; - snd_tea575x_write(tea, tea->val); - tea->freq = snd_tea575x_val_to_freq(tea, tea->val); -} - -/* - * Linux Video interface - */ - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - struct snd_tea575x *tea = video_drvdata(file); - - strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver)); - strlcpy(v->card, tea->card, sizeof(v->card)); - strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card)); - strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - if (!tea->cannot_read_data) - v->device_caps |= V4L2_CAP_HW_FREQ_SEEK; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int vidioc_enum_freq_bands(struct file *file, void *priv, - struct v4l2_frequency_band *band) -{ - struct snd_tea575x *tea = video_drvdata(file); - int index; - - if (band->tuner != 0) - return -EINVAL; - - switch (band->index) { - case 0: - if (tea->tea5759) - index = BAND_FM_JAPAN; - else - index = BAND_FM; - break; - case 1: - if (tea->has_am) { - index = BAND_AM; - break; - } - /* Fall through */ - default: - return -EINVAL; - } - - *band = bands[index]; - if (!tea->cannot_read_data) - band->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED; - - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - struct snd_tea575x *tea = video_drvdata(file); - struct v4l2_frequency_band band_fm = { 0, }; - - if (v->index > 0) - return -EINVAL; - - snd_tea575x_read(tea); - vidioc_enum_freq_bands(file, priv, &band_fm); - - memset(v, 0, sizeof(*v)); - strlcpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->capability = band_fm.capability; - v->rangelow = tea->has_am ? bands[BAND_AM].rangelow : band_fm.rangelow; - v->rangehigh = band_fm.rangehigh; - v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; - v->audmode = (tea->val & TEA575X_BIT_MONO) ? - V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO; - v->signal = tea->tuned ? 0xffff : 0; - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - const struct v4l2_tuner *v) -{ - struct snd_tea575x *tea = video_drvdata(file); - u32 orig_val = tea->val; - - if (v->index) - return -EINVAL; - tea->val &= ~TEA575X_BIT_MONO; - if (v->audmode == V4L2_TUNER_MODE_MONO) - tea->val |= TEA575X_BIT_MONO; - /* Only apply changes if currently tuning FM */ - if (tea->band != BAND_AM && tea->val != orig_val) - snd_tea575x_set_freq(tea); - - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct snd_tea575x *tea = video_drvdata(file); - - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = tea->freq; - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - const struct v4l2_frequency *f) -{ - struct snd_tea575x *tea = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - - if (tea->has_am && f->frequency < (20000 * 16)) - tea->band = BAND_AM; - else if (tea->tea5759) - tea->band = BAND_FM_JAPAN; - else - tea->band = BAND_FM; - - tea->freq = clamp_t(u32, f->frequency, bands[tea->band].rangelow, - bands[tea->band].rangehigh); - snd_tea575x_set_freq(tea); - return 0; -} - -static int vidioc_s_hw_freq_seek(struct file *file, void *fh, - const struct v4l2_hw_freq_seek *a) -{ - struct snd_tea575x *tea = video_drvdata(file); - unsigned long timeout; - int i, spacing; - - if (tea->cannot_read_data) - return -ENOTTY; - if (a->tuner || a->wrap_around) - return -EINVAL; - - if (file->f_flags & O_NONBLOCK) - return -EWOULDBLOCK; - - if (a->rangelow || a->rangehigh) { - for (i = 0; i < ARRAY_SIZE(bands); i++) { - if ((i == BAND_FM && tea->tea5759) || - (i == BAND_FM_JAPAN && !tea->tea5759) || - (i == BAND_AM && !tea->has_am)) - continue; - if (bands[i].rangelow == a->rangelow && - bands[i].rangehigh == a->rangehigh) - break; - } - if (i == ARRAY_SIZE(bands)) - return -EINVAL; /* No matching band found */ - if (i != tea->band) { - tea->band = i; - tea->freq = clamp(tea->freq, bands[i].rangelow, - bands[i].rangehigh); - snd_tea575x_set_freq(tea); - } - } - - spacing = (tea->band == BAND_AM) ? 5 : 50; /* kHz */ - - /* clear the frequency, HW will fill it in */ - tea->val &= ~TEA575X_BIT_FREQ_MASK; - tea->val |= TEA575X_BIT_SEARCH; - if (a->seek_upward) - tea->val |= TEA575X_BIT_UPDOWN; - else - tea->val &= ~TEA575X_BIT_UPDOWN; - snd_tea575x_write(tea, tea->val); - timeout = jiffies + msecs_to_jiffies(10000); - for (;;) { - if (time_after(jiffies, timeout)) - break; - if (schedule_timeout_interruptible(msecs_to_jiffies(10))) { - /* some signal arrived, stop search */ - tea->val &= ~TEA575X_BIT_SEARCH; - snd_tea575x_set_freq(tea); - return -ERESTARTSYS; - } - if (!(snd_tea575x_read(tea) & TEA575X_BIT_SEARCH)) { - u32 freq; - - /* Found a frequency, wait until it can be read */ - for (i = 0; i < 100; i++) { - msleep(10); - freq = snd_tea575x_get_freq(tea); - if (freq) /* available */ - break; - } - if (freq == 0) /* shouldn't happen */ - break; - /* - * if we moved by less than the spacing, or in the - * wrong direction, continue seeking - */ - if (abs(tea->freq - freq) < 16 * spacing || - (a->seek_upward && freq < tea->freq) || - (!a->seek_upward && freq > tea->freq)) { - snd_tea575x_write(tea, tea->val); - continue; - } - tea->freq = freq; - tea->val &= ~TEA575X_BIT_SEARCH; - return 0; - } - } - tea->val &= ~TEA575X_BIT_SEARCH; - snd_tea575x_set_freq(tea); - return -ENODATA; -} - -static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - tea->mute = ctrl->val; - snd_tea575x_set_freq(tea); - return 0; - } - - return -EINVAL; -} - -static const struct v4l2_file_operations tea575x_fops = { - .unlocked_ioctl = video_ioctl2, - .open = v4l2_fh_open, - .release = v4l2_fh_release, - .poll = v4l2_ctrl_poll, -}; - -static const struct v4l2_ioctl_ops tea575x_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .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_s_hw_freq_seek = vidioc_s_hw_freq_seek, - .vidioc_enum_freq_bands = vidioc_enum_freq_bands, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct video_device tea575x_radio = { - .ioctl_ops = &tea575x_ioctl_ops, - .release = video_device_release_empty, -}; - -static const struct v4l2_ctrl_ops tea575x_ctrl_ops = { - .s_ctrl = tea575x_s_ctrl, -}; - - -int snd_tea575x_hw_init(struct snd_tea575x *tea) -{ - tea->mute = true; - - /* Not all devices can or know how to read the data back. - Such devices can set cannot_read_data to true. */ - if (!tea->cannot_read_data) { - snd_tea575x_write(tea, 0x55AA); - if (snd_tea575x_read(tea) != 0x55AA) - return -ENODEV; - } - - tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_5_28; - tea->freq = 90500 * 16; /* 90.5Mhz default */ - snd_tea575x_set_freq(tea); - - return 0; -} -EXPORT_SYMBOL(snd_tea575x_hw_init); - -int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner) -{ - int retval = snd_tea575x_hw_init(tea); - - if (retval) - return retval; - - tea->vd = tea575x_radio; - video_set_drvdata(&tea->vd, tea); - mutex_init(&tea->mutex); - strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name)); - tea->vd.lock = &tea->mutex; - tea->vd.v4l2_dev = tea->v4l2_dev; - 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); - - if (!tea->cannot_mute) { - tea->vd.ctrl_handler = &tea->ctrl_handler; - v4l2_ctrl_handler_init(&tea->ctrl_handler, 1); - v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); - retval = tea->ctrl_handler.error; - if (retval) { - v4l2_err(tea->v4l2_dev, "can't initialize controls\n"); - v4l2_ctrl_handler_free(&tea->ctrl_handler); - return retval; - } - - if (tea->ext_init) { - retval = tea->ext_init(tea); - if (retval) { - v4l2_ctrl_handler_free(&tea->ctrl_handler); - return retval; - } - } - - v4l2_ctrl_handler_setup(&tea->ctrl_handler); - } - - retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->radio_nr); - if (retval) { - v4l2_err(tea->v4l2_dev, "can't register video device!\n"); - v4l2_ctrl_handler_free(tea->vd.ctrl_handler); - return retval; - } - - return 0; -} - -void snd_tea575x_exit(struct snd_tea575x *tea) -{ - video_unregister_device(&tea->vd); - v4l2_ctrl_handler_free(tea->vd.ctrl_handler); -} - -static int __init alsa_tea575x_module_init(void) -{ - return 0; -} - -static void __exit alsa_tea575x_module_exit(void) -{ -} - -module_init(alsa_tea575x_module_init) -module_exit(alsa_tea575x_module_exit) - -EXPORT_SYMBOL(snd_tea575x_init); -EXPORT_SYMBOL(snd_tea575x_exit); -EXPORT_SYMBOL(snd_tea575x_set_freq); diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index fe6fa93..9df80ef 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -1,10 +1,5 @@ # ALSA PCI drivers -config SND_TEA575X - tristate - depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO || RADIO_SF16FMR2 || RADIO_MAXIRADIO || RADIO_SHARK - default SND_FM801 || SND_ES1968 || RADIO_SF16FMR2 || RADIO_MAXIRADIO || RADIO_SHARK - menuconfig SND_PCI bool "PCI sound devices" depends on PCI @@ -542,7 +537,9 @@ config SND_ES1968_INPUT config SND_ES1968_RADIO bool "Enable TEA5757 radio tuner support for es1968" depends on SND_ES1968 + depends on MEDIA_RADIO_SUPPORT depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_ES1968 + select RADIO_TEA575X help Say Y here to include support for TEA5757 radio tuner integrated on some MediaForte cards (e.g. SF64-PCE2). @@ -562,7 +559,9 @@ config SND_FM801 config SND_FM801_TEA575X_BOOL bool "ForteMedia FM801 + TEA5757 tuner" depends on SND_FM801 + depends on MEDIA_RADIO_SUPPORT depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801 + select RADIO_TEA575X help Say Y here to include support for soundcards based on the ForteMedia FM801 chip with a TEA5757 tuner (MediaForte SF256-PCS, SF256-PCP and -- cgit v0.10.2 From 299878fa3c373dbf74edf5872c79ef4c65b80a04 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:54 -0300 Subject: [media] v4l2-dv-timings.h: remove duplicate V4L2_DV_BT_DMT_1366X768P60 This particular DMT timing definition was duplicated in the header. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/v4l2-dv-timings.h b/include/uapi/linux/v4l2-dv-timings.h index 4e0c58d..be709fe 100644 --- a/include/uapi/linux/v4l2-dv-timings.h +++ b/include/uapi/linux/v4l2-dv-timings.h @@ -823,12 +823,4 @@ V4L2_DV_FL_REDUCED_BLANKING) \ } -#define V4L2_DV_BT_DMT_1366X768P60 { \ - .type = V4L2_DV_BT_656_1120, \ - V4L2_INIT_BT_TIMINGS(1366, 768, 0, \ - V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ - 85500000, 70, 143, 213, 3, 3, 24, 0, 0, 0, \ - V4L2_DV_BT_STD_DMT, 0) \ -} - #endif -- cgit v0.10.2 From b18787ed1ce32eb0c2ce2323220abd4ed93c4b97 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:55 -0300 Subject: [media] v4l2-dv-timings: add new helper module This module makes it easy to filter valid timings from the full list of CEA and DMT timings based on the timings capabilities. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 4c33b8d..1a85eee 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -17,6 +17,7 @@ endif obj-$(CONFIG_VIDEO_V4L2) += videodev.o obj-$(CONFIG_VIDEO_V4L2_INT_DEVICE) += v4l2-int-device.o obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o +obj-$(CONFIG_VIDEO_V4L2) += v4l2-dv-timings.o obj-$(CONFIG_VIDEO_TUNER) += tuner.o diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c new file mode 100644 index 0000000..5827946 --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -0,0 +1,192 @@ +/* + * v4l2-dv-timings - dv-timings helper functions + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct v4l2_dv_timings timings[] = { + V4L2_DV_BT_CEA_640X480P59_94, + V4L2_DV_BT_CEA_720X480I59_94, + V4L2_DV_BT_CEA_720X480P59_94, + V4L2_DV_BT_CEA_720X576I50, + V4L2_DV_BT_CEA_720X576P50, + V4L2_DV_BT_CEA_1280X720P24, + V4L2_DV_BT_CEA_1280X720P25, + V4L2_DV_BT_CEA_1280X720P30, + V4L2_DV_BT_CEA_1280X720P50, + V4L2_DV_BT_CEA_1280X720P60, + V4L2_DV_BT_CEA_1920X1080P24, + V4L2_DV_BT_CEA_1920X1080P25, + V4L2_DV_BT_CEA_1920X1080P30, + V4L2_DV_BT_CEA_1920X1080I50, + V4L2_DV_BT_CEA_1920X1080P50, + V4L2_DV_BT_CEA_1920X1080I60, + V4L2_DV_BT_CEA_1920X1080P60, + V4L2_DV_BT_DMT_640X350P85, + V4L2_DV_BT_DMT_640X400P85, + V4L2_DV_BT_DMT_720X400P85, + V4L2_DV_BT_DMT_640X480P72, + V4L2_DV_BT_DMT_640X480P75, + V4L2_DV_BT_DMT_640X480P85, + V4L2_DV_BT_DMT_800X600P56, + V4L2_DV_BT_DMT_800X600P60, + V4L2_DV_BT_DMT_800X600P72, + V4L2_DV_BT_DMT_800X600P75, + V4L2_DV_BT_DMT_800X600P85, + V4L2_DV_BT_DMT_800X600P120_RB, + V4L2_DV_BT_DMT_848X480P60, + V4L2_DV_BT_DMT_1024X768I43, + V4L2_DV_BT_DMT_1024X768P60, + V4L2_DV_BT_DMT_1024X768P70, + V4L2_DV_BT_DMT_1024X768P75, + V4L2_DV_BT_DMT_1024X768P85, + V4L2_DV_BT_DMT_1024X768P120_RB, + V4L2_DV_BT_DMT_1152X864P75, + V4L2_DV_BT_DMT_1280X768P60_RB, + V4L2_DV_BT_DMT_1280X768P60, + V4L2_DV_BT_DMT_1280X768P75, + V4L2_DV_BT_DMT_1280X768P85, + V4L2_DV_BT_DMT_1280X768P120_RB, + V4L2_DV_BT_DMT_1280X800P60_RB, + V4L2_DV_BT_DMT_1280X800P60, + V4L2_DV_BT_DMT_1280X800P75, + V4L2_DV_BT_DMT_1280X800P85, + V4L2_DV_BT_DMT_1280X800P120_RB, + V4L2_DV_BT_DMT_1280X960P60, + V4L2_DV_BT_DMT_1280X960P85, + V4L2_DV_BT_DMT_1280X960P120_RB, + V4L2_DV_BT_DMT_1280X1024P60, + V4L2_DV_BT_DMT_1280X1024P75, + V4L2_DV_BT_DMT_1280X1024P85, + V4L2_DV_BT_DMT_1280X1024P120_RB, + V4L2_DV_BT_DMT_1360X768P60, + V4L2_DV_BT_DMT_1360X768P120_RB, + V4L2_DV_BT_DMT_1366X768P60, + V4L2_DV_BT_DMT_1366X768P60_RB, + V4L2_DV_BT_DMT_1400X1050P60_RB, + V4L2_DV_BT_DMT_1400X1050P60, + V4L2_DV_BT_DMT_1400X1050P75, + V4L2_DV_BT_DMT_1400X1050P85, + V4L2_DV_BT_DMT_1400X1050P120_RB, + V4L2_DV_BT_DMT_1440X900P60_RB, + V4L2_DV_BT_DMT_1440X900P60, + V4L2_DV_BT_DMT_1440X900P75, + V4L2_DV_BT_DMT_1440X900P85, + V4L2_DV_BT_DMT_1440X900P120_RB, + V4L2_DV_BT_DMT_1600X900P60_RB, + V4L2_DV_BT_DMT_1600X1200P60, + V4L2_DV_BT_DMT_1600X1200P65, + V4L2_DV_BT_DMT_1600X1200P70, + V4L2_DV_BT_DMT_1600X1200P75, + V4L2_DV_BT_DMT_1600X1200P85, + V4L2_DV_BT_DMT_1600X1200P120_RB, + V4L2_DV_BT_DMT_1680X1050P60_RB, + V4L2_DV_BT_DMT_1680X1050P60, + V4L2_DV_BT_DMT_1680X1050P75, + V4L2_DV_BT_DMT_1680X1050P85, + V4L2_DV_BT_DMT_1680X1050P120_RB, + V4L2_DV_BT_DMT_1792X1344P60, + V4L2_DV_BT_DMT_1792X1344P75, + V4L2_DV_BT_DMT_1792X1344P120_RB, + V4L2_DV_BT_DMT_1856X1392P60, + V4L2_DV_BT_DMT_1856X1392P75, + V4L2_DV_BT_DMT_1856X1392P120_RB, + V4L2_DV_BT_DMT_1920X1200P60_RB, + V4L2_DV_BT_DMT_1920X1200P60, + V4L2_DV_BT_DMT_1920X1200P75, + V4L2_DV_BT_DMT_1920X1200P85, + V4L2_DV_BT_DMT_1920X1200P120_RB, + V4L2_DV_BT_DMT_1920X1440P60, + V4L2_DV_BT_DMT_1920X1440P75, + V4L2_DV_BT_DMT_1920X1440P120_RB, + V4L2_DV_BT_DMT_2048X1152P60_RB, + V4L2_DV_BT_DMT_2560X1600P60_RB, + V4L2_DV_BT_DMT_2560X1600P60, + V4L2_DV_BT_DMT_2560X1600P75, + V4L2_DV_BT_DMT_2560X1600P85, + V4L2_DV_BT_DMT_2560X1600P120_RB, +}; + +bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *dvcap) +{ + const struct v4l2_bt_timings *bt = &t->bt; + const struct v4l2_bt_timings_cap *cap = &dvcap->bt; + u32 caps = cap->capabilities; + + if (t->type != V4L2_DV_BT_656_1120) + return false; + if (t->type != dvcap->type || + bt->height < cap->min_height || + bt->height > cap->max_height || + bt->width < cap->min_width || + bt->width > cap->max_width || + bt->pixelclock < cap->min_pixelclock || + bt->pixelclock > cap->max_pixelclock || + (cap->standards && !(bt->standards & cap->standards)) || + (bt->interlaced && !(caps & V4L2_DV_BT_CAP_INTERLACED)) || + (!bt->interlaced && !(caps & V4L2_DV_BT_CAP_PROGRESSIVE))) + return false; + return true; +} +EXPORT_SYMBOL_GPL(v4l2_dv_valid_timings); + +int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, + const struct v4l2_dv_timings_cap *cap) +{ + u32 i, idx; + + memset(t->reserved, 0, sizeof(t->reserved)); + for (i = idx = 0; i < ARRAY_SIZE(timings); i++) { + if (v4l2_dv_valid_timings(timings + i, cap) && + idx++ == t->index) { + t->timings = timings[i]; + return 0; + } + } + return -EINVAL; +} +EXPORT_SYMBOL_GPL(v4l2_enum_dv_timings_cap); + +bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *cap, + unsigned pclock_delta) +{ + int i; + + if (!v4l2_dv_valid_timings(t, cap)) + return false; + + for (i = 0; i < ARRAY_SIZE(timings); i++) { + if (v4l2_dv_valid_timings(timings + i, cap) && + v4l_match_dv_timings(t, timings + i, pclock_delta)) { + *t = timings[i]; + return true; + } + } + return false; +} +EXPORT_SYMBOL_GPL(v4l2_find_dv_timings_cap); diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h new file mode 100644 index 0000000..41075fa --- /dev/null +++ b/include/media/v4l2-dv-timings.h @@ -0,0 +1,67 @@ +/* + * v4l2-dv-timings - Internal header with dv-timings helper functions + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * 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. + * + */ + +#ifndef __V4L2_DV_TIMINGS_H +#define __V4L2_DV_TIMINGS_H + +#include + +/** v4l2_dv_valid_timings() - are these timings valid? + * @t: the v4l2_dv_timings struct. + * @cap: the v4l2_dv_timings_cap capabilities. + * + * Returns true if the given dv_timings struct is supported by the + * hardware capabilities, returns false otherwise. + */ +bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *cap); + +/** v4l2_enum_dv_timings_cap() - Helper function to enumerate possible DV timings based on capabilities + * @t: the v4l2_enum_dv_timings struct. + * @cap: the v4l2_dv_timings_cap capabilities. + * + * This enumerates dv_timings using the full list of possible CEA-861 and DMT + * timings, filtering out any timings that are not supported based on the + * hardware capabilities. + * + * If a valid timing for the given index is found, it will fill in @t and + * return 0, otherwise it returns -EINVAL. + */ +int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, + const struct v4l2_dv_timings_cap *cap); + +/** v4l2_find_dv_timings_cap() - Find the closest timings struct + * @t: the v4l2_enum_dv_timings struct. + * @cap: the v4l2_dv_timings_cap capabilities. + * @pclock_delta: maximum delta between t->pixelclock and the timing struct + * under consideration. + * + * This function tries to map the given timings to an entry in the + * full list of possible CEA-861 and DMT timings, filtering out any timings + * that are not supported based on the hardware capabilities. + * + * On success it will fill in @t with the found timings and it returns true. + * On failure it will return false. + */ +bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *cap, + unsigned pclock_delta); + +#endif -- cgit v0.10.2 From 2576415846bcbad3c0a6885fc44f950837106364 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:56 -0300 Subject: [media] v4l2: move dv-timings related code to v4l2-dv-timings.c v4l2-common.c contained a bunch of dv-timings related functions. Move that to the new v4l2-dv-timings.c which is a more appropriate place for them. There aren't many drivers that do HDTV, so it is a good idea to separate common code related to that into a module of its own. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index ba4364d..2fa8d72 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 1d675b5..181a6c3 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -38,6 +38,7 @@ #include #include #include +#include #include static int debug; diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 8a29810..aef7c0e 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -21,6 +21,7 @@ #include #include +#include #include #include diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 4f8567a..9c67b6e 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "hdpvr.h" diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index a95e5e2..037d7a5 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -495,363 +495,6 @@ void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, } EXPORT_SYMBOL_GPL(v4l_bound_align_image); -/** - * v4l_match_dv_timings - check if two timings match - * @t1 - compare this v4l2_dv_timings struct... - * @t2 - with this struct. - * @pclock_delta - the allowed pixelclock deviation. - * - * Compare t1 with t2 with a given margin of error for the pixelclock. - */ -bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, - const struct v4l2_dv_timings *t2, - unsigned pclock_delta) -{ - if (t1->type != t2->type || t1->type != V4L2_DV_BT_656_1120) - return false; - if (t1->bt.width == t2->bt.width && - t1->bt.height == t2->bt.height && - t1->bt.interlaced == t2->bt.interlaced && - t1->bt.polarities == t2->bt.polarities && - t1->bt.pixelclock >= t2->bt.pixelclock - pclock_delta && - t1->bt.pixelclock <= t2->bt.pixelclock + pclock_delta && - t1->bt.hfrontporch == t2->bt.hfrontporch && - t1->bt.vfrontporch == t2->bt.vfrontporch && - t1->bt.vsync == t2->bt.vsync && - t1->bt.vbackporch == t2->bt.vbackporch && - (!t1->bt.interlaced || - (t1->bt.il_vfrontporch == t2->bt.il_vfrontporch && - t1->bt.il_vsync == t2->bt.il_vsync && - t1->bt.il_vbackporch == t2->bt.il_vbackporch))) - return true; - return false; -} -EXPORT_SYMBOL_GPL(v4l_match_dv_timings); - -/* - * CVT defines - * Based on Coordinated Video Timings Standard - * version 1.1 September 10, 2003 - */ - -#define CVT_PXL_CLK_GRAN 250000 /* pixel clock granularity */ - -/* Normal blanking */ -#define CVT_MIN_V_BPORCH 7 /* lines */ -#define CVT_MIN_V_PORCH_RND 3 /* lines */ -#define CVT_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ - -/* Normal blanking for CVT uses GTF to calculate horizontal blanking */ -#define CVT_CELL_GRAN 8 /* character cell granularity */ -#define CVT_M 600 /* blanking formula gradient */ -#define CVT_C 40 /* blanking formula offset */ -#define CVT_K 128 /* blanking formula scaling factor */ -#define CVT_J 20 /* blanking formula scaling factor */ -#define CVT_C_PRIME (((CVT_C - CVT_J) * CVT_K / 256) + CVT_J) -#define CVT_M_PRIME (CVT_K * CVT_M / 256) - -/* Reduced Blanking */ -#define CVT_RB_MIN_V_BPORCH 7 /* lines */ -#define CVT_RB_V_FPORCH 3 /* lines */ -#define CVT_RB_MIN_V_BLANK 460 /* us */ -#define CVT_RB_H_SYNC 32 /* pixels */ -#define CVT_RB_H_BPORCH 80 /* pixels */ -#define CVT_RB_H_BLANK 160 /* pixels */ - -/** v4l2_detect_cvt - detect if the given timings follow the CVT standard - * @frame_height - the total height of the frame (including blanking) in lines. - * @hfreq - the horizontal frequency in Hz. - * @vsync - the height of the vertical sync in lines. - * @polarities - the horizontal and vertical polarities (same as struct - * v4l2_bt_timings polarities). - * @fmt - the resulting timings. - * - * This function will attempt to detect if the given values correspond to a - * valid CVT format. If so, then it will return true, and fmt will be filled - * in with the found CVT timings. - */ -bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, struct v4l2_dv_timings *fmt) -{ - int v_fp, v_bp, h_fp, h_bp, hsync; - int frame_width, image_height, image_width; - bool reduced_blanking; - unsigned pix_clk; - - if (vsync < 4 || vsync > 7) - return false; - - if (polarities == V4L2_DV_VSYNC_POS_POL) - reduced_blanking = false; - else if (polarities == V4L2_DV_HSYNC_POS_POL) - reduced_blanking = true; - else - return false; - - /* Vertical */ - if (reduced_blanking) { - v_fp = CVT_RB_V_FPORCH; - v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 999999) / 1000000; - v_bp -= vsync + v_fp; - - if (v_bp < CVT_RB_MIN_V_BPORCH) - v_bp = CVT_RB_MIN_V_BPORCH; - } else { - v_fp = CVT_MIN_V_PORCH_RND; - v_bp = (CVT_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; - - if (v_bp < CVT_MIN_V_BPORCH) - v_bp = CVT_MIN_V_BPORCH; - } - image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; - - /* Aspect ratio based on vsync */ - switch (vsync) { - case 4: - image_width = (image_height * 4) / 3; - break; - case 5: - image_width = (image_height * 16) / 9; - break; - case 6: - image_width = (image_height * 16) / 10; - break; - case 7: - /* special case */ - if (image_height == 1024) - image_width = (image_height * 5) / 4; - else if (image_height == 768) - image_width = (image_height * 15) / 9; - else - return false; - break; - default: - return false; - } - - image_width = image_width & ~7; - - /* Horizontal */ - if (reduced_blanking) { - pix_clk = (image_width + CVT_RB_H_BLANK) * hfreq; - pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; - - h_bp = CVT_RB_H_BPORCH; - hsync = CVT_RB_H_SYNC; - h_fp = CVT_RB_H_BLANK - h_bp - hsync; - - frame_width = image_width + CVT_RB_H_BLANK; - } else { - int h_blank; - unsigned ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * 1000) / hfreq; - - h_blank = (image_width * ideal_duty_cycle + (100 - ideal_duty_cycle) / 2) / - (100 - ideal_duty_cycle); - h_blank = h_blank - h_blank % (2 * CVT_CELL_GRAN); - - if (h_blank * 100 / image_width < 20) { - h_blank = image_width / 5; - h_blank = (h_blank + 0x7) & ~0x7; - } - - pix_clk = (image_width + h_blank) * hfreq; - pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; - - h_bp = h_blank / 2; - frame_width = image_width + h_blank; - - hsync = (frame_width * 8 + 50) / 100; - hsync = hsync - hsync % CVT_CELL_GRAN; - h_fp = h_blank - hsync - h_bp; - } - - fmt->bt.polarities = polarities; - fmt->bt.width = image_width; - fmt->bt.height = image_height; - fmt->bt.hfrontporch = h_fp; - fmt->bt.vfrontporch = v_fp; - fmt->bt.hsync = hsync; - fmt->bt.vsync = vsync; - fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; - fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; - fmt->bt.pixelclock = pix_clk; - fmt->bt.standards = V4L2_DV_BT_STD_CVT; - if (reduced_blanking) - fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; - return true; -} -EXPORT_SYMBOL_GPL(v4l2_detect_cvt); - -/* - * GTF defines - * Based on Generalized Timing Formula Standard - * Version 1.1 September 2, 1999 - */ - -#define GTF_PXL_CLK_GRAN 250000 /* pixel clock granularity */ - -#define GTF_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ -#define GTF_V_FP 1 /* vertical front porch (lines) */ -#define GTF_CELL_GRAN 8 /* character cell granularity */ - -/* Default */ -#define GTF_D_M 600 /* blanking formula gradient */ -#define GTF_D_C 40 /* blanking formula offset */ -#define GTF_D_K 128 /* blanking formula scaling factor */ -#define GTF_D_J 20 /* blanking formula scaling factor */ -#define GTF_D_C_PRIME ((((GTF_D_C - GTF_D_J) * GTF_D_K) / 256) + GTF_D_J) -#define GTF_D_M_PRIME ((GTF_D_K * GTF_D_M) / 256) - -/* Secondary */ -#define GTF_S_M 3600 /* blanking formula gradient */ -#define GTF_S_C 40 /* blanking formula offset */ -#define GTF_S_K 128 /* blanking formula scaling factor */ -#define GTF_S_J 35 /* blanking formula scaling factor */ -#define GTF_S_C_PRIME ((((GTF_S_C - GTF_S_J) * GTF_S_K) / 256) + GTF_S_J) -#define GTF_S_M_PRIME ((GTF_S_K * GTF_S_M) / 256) - -/** v4l2_detect_gtf - detect if the given timings follow the GTF standard - * @frame_height - the total height of the frame (including blanking) in lines. - * @hfreq - the horizontal frequency in Hz. - * @vsync - the height of the vertical sync in lines. - * @polarities - the horizontal and vertical polarities (same as struct - * v4l2_bt_timings polarities). - * @aspect - preferred aspect ratio. GTF has no method of determining the - * aspect ratio in order to derive the image width from the - * image height, so it has to be passed explicitly. Usually - * the native screen aspect ratio is used for this. If it - * is not filled in correctly, then 16:9 will be assumed. - * @fmt - the resulting timings. - * - * This function will attempt to detect if the given values correspond to a - * valid GTF format. If so, then it will return true, and fmt will be filled - * in with the found GTF timings. - */ -bool v4l2_detect_gtf(unsigned frame_height, - unsigned hfreq, - unsigned vsync, - u32 polarities, - struct v4l2_fract aspect, - struct v4l2_dv_timings *fmt) -{ - int pix_clk; - int v_fp, v_bp, h_fp, hsync; - int frame_width, image_height, image_width; - bool default_gtf; - int h_blank; - - if (vsync != 3) - return false; - - if (polarities == V4L2_DV_VSYNC_POS_POL) - default_gtf = true; - else if (polarities == V4L2_DV_HSYNC_POS_POL) - default_gtf = false; - else - return false; - - /* Vertical */ - v_fp = GTF_V_FP; - v_bp = (GTF_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; - image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; - - if (aspect.numerator == 0 || aspect.denominator == 0) { - aspect.numerator = 16; - aspect.denominator = 9; - } - image_width = ((image_height * aspect.numerator) / aspect.denominator); - - /* Horizontal */ - if (default_gtf) - h_blank = ((image_width * GTF_D_C_PRIME * hfreq) - - (image_width * GTF_D_M_PRIME * 1000) + - (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000) / 2) / - (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000); - else - h_blank = ((image_width * GTF_S_C_PRIME * hfreq) - - (image_width * GTF_S_M_PRIME * 1000) + - (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000) / 2) / - (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000); - - h_blank = h_blank - h_blank % (2 * GTF_CELL_GRAN); - frame_width = image_width + h_blank; - - pix_clk = (image_width + h_blank) * hfreq; - pix_clk = pix_clk / GTF_PXL_CLK_GRAN * GTF_PXL_CLK_GRAN; - - hsync = (frame_width * 8 + 50) / 100; - hsync = hsync - hsync % GTF_CELL_GRAN; - - h_fp = h_blank / 2 - hsync; - - fmt->bt.polarities = polarities; - fmt->bt.width = image_width; - fmt->bt.height = image_height; - fmt->bt.hfrontporch = h_fp; - fmt->bt.vfrontporch = v_fp; - fmt->bt.hsync = hsync; - fmt->bt.vsync = vsync; - fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; - fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; - fmt->bt.pixelclock = pix_clk; - fmt->bt.standards = V4L2_DV_BT_STD_GTF; - if (!default_gtf) - fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; - return true; -} -EXPORT_SYMBOL_GPL(v4l2_detect_gtf); - -/** v4l2_calc_aspect_ratio - calculate the aspect ratio based on bytes - * 0x15 and 0x16 from the EDID. - * @hor_landscape - byte 0x15 from the EDID. - * @vert_portrait - byte 0x16 from the EDID. - * - * Determines the aspect ratio from the EDID. - * See VESA Enhanced EDID standard, release A, rev 2, section 3.6.2: - * "Horizontal and Vertical Screen Size or Aspect Ratio" - */ -struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait) -{ - struct v4l2_fract aspect = { 16, 9 }; - u32 tmp; - u8 ratio; - - /* Nothing filled in, fallback to 16:9 */ - if (!hor_landscape && !vert_portrait) - return aspect; - /* Both filled in, so they are interpreted as the screen size in cm */ - if (hor_landscape && vert_portrait) { - aspect.numerator = hor_landscape; - aspect.denominator = vert_portrait; - return aspect; - } - /* Only one is filled in, so interpret them as a ratio: - (val + 99) / 100 */ - ratio = hor_landscape | vert_portrait; - /* Change some rounded values into the exact aspect ratio */ - if (ratio == 79) { - aspect.numerator = 16; - aspect.denominator = 9; - } else if (ratio == 34) { - aspect.numerator = 4; - aspect.numerator = 3; - } else if (ratio == 68) { - aspect.numerator = 15; - aspect.numerator = 9; - } else { - aspect.numerator = hor_landscape + 99; - aspect.denominator = 100; - } - if (hor_landscape) - return aspect; - /* The aspect ratio is for portrait, so swap numerator and denominator */ - tmp = aspect.denominator; - aspect.denominator = aspect.numerator; - aspect.numerator = tmp; - return aspect; -} -EXPORT_SYMBOL_GPL(v4l2_calc_aspect_ratio); - const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( const struct v4l2_discrete_probe *probe, s32 width, s32 height) diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 5827946..f20b316 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -24,7 +24,6 @@ #include #include #include -#include #include static const struct v4l2_dv_timings timings[] = { @@ -190,3 +189,360 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, return false; } EXPORT_SYMBOL_GPL(v4l2_find_dv_timings_cap); + +/** + * v4l_match_dv_timings - check if two timings match + * @t1 - compare this v4l2_dv_timings struct... + * @t2 - with this struct. + * @pclock_delta - the allowed pixelclock deviation. + * + * Compare t1 with t2 with a given margin of error for the pixelclock. + */ +bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, + const struct v4l2_dv_timings *t2, + unsigned pclock_delta) +{ + if (t1->type != t2->type || t1->type != V4L2_DV_BT_656_1120) + return false; + if (t1->bt.width == t2->bt.width && + t1->bt.height == t2->bt.height && + t1->bt.interlaced == t2->bt.interlaced && + t1->bt.polarities == t2->bt.polarities && + t1->bt.pixelclock >= t2->bt.pixelclock - pclock_delta && + t1->bt.pixelclock <= t2->bt.pixelclock + pclock_delta && + t1->bt.hfrontporch == t2->bt.hfrontporch && + t1->bt.vfrontporch == t2->bt.vfrontporch && + t1->bt.vsync == t2->bt.vsync && + t1->bt.vbackporch == t2->bt.vbackporch && + (!t1->bt.interlaced || + (t1->bt.il_vfrontporch == t2->bt.il_vfrontporch && + t1->bt.il_vsync == t2->bt.il_vsync && + t1->bt.il_vbackporch == t2->bt.il_vbackporch))) + return true; + return false; +} +EXPORT_SYMBOL_GPL(v4l_match_dv_timings); + +/* + * CVT defines + * Based on Coordinated Video Timings Standard + * version 1.1 September 10, 2003 + */ + +#define CVT_PXL_CLK_GRAN 250000 /* pixel clock granularity */ + +/* Normal blanking */ +#define CVT_MIN_V_BPORCH 7 /* lines */ +#define CVT_MIN_V_PORCH_RND 3 /* lines */ +#define CVT_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ + +/* Normal blanking for CVT uses GTF to calculate horizontal blanking */ +#define CVT_CELL_GRAN 8 /* character cell granularity */ +#define CVT_M 600 /* blanking formula gradient */ +#define CVT_C 40 /* blanking formula offset */ +#define CVT_K 128 /* blanking formula scaling factor */ +#define CVT_J 20 /* blanking formula scaling factor */ +#define CVT_C_PRIME (((CVT_C - CVT_J) * CVT_K / 256) + CVT_J) +#define CVT_M_PRIME (CVT_K * CVT_M / 256) + +/* Reduced Blanking */ +#define CVT_RB_MIN_V_BPORCH 7 /* lines */ +#define CVT_RB_V_FPORCH 3 /* lines */ +#define CVT_RB_MIN_V_BLANK 460 /* us */ +#define CVT_RB_H_SYNC 32 /* pixels */ +#define CVT_RB_H_BPORCH 80 /* pixels */ +#define CVT_RB_H_BLANK 160 /* pixels */ + +/** v4l2_detect_cvt - detect if the given timings follow the CVT standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid CVT format. If so, then it will return true, and fmt will be filled + * in with the found CVT timings. + */ +bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, + u32 polarities, struct v4l2_dv_timings *fmt) +{ + int v_fp, v_bp, h_fp, h_bp, hsync; + int frame_width, image_height, image_width; + bool reduced_blanking; + unsigned pix_clk; + + if (vsync < 4 || vsync > 7) + return false; + + if (polarities == V4L2_DV_VSYNC_POS_POL) + reduced_blanking = false; + else if (polarities == V4L2_DV_HSYNC_POS_POL) + reduced_blanking = true; + else + return false; + + /* Vertical */ + if (reduced_blanking) { + v_fp = CVT_RB_V_FPORCH; + v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 999999) / 1000000; + v_bp -= vsync + v_fp; + + if (v_bp < CVT_RB_MIN_V_BPORCH) + v_bp = CVT_RB_MIN_V_BPORCH; + } else { + v_fp = CVT_MIN_V_PORCH_RND; + v_bp = (CVT_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; + + if (v_bp < CVT_MIN_V_BPORCH) + v_bp = CVT_MIN_V_BPORCH; + } + image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; + + /* Aspect ratio based on vsync */ + switch (vsync) { + case 4: + image_width = (image_height * 4) / 3; + break; + case 5: + image_width = (image_height * 16) / 9; + break; + case 6: + image_width = (image_height * 16) / 10; + break; + case 7: + /* special case */ + if (image_height == 1024) + image_width = (image_height * 5) / 4; + else if (image_height == 768) + image_width = (image_height * 15) / 9; + else + return false; + break; + default: + return false; + } + + image_width = image_width & ~7; + + /* Horizontal */ + if (reduced_blanking) { + pix_clk = (image_width + CVT_RB_H_BLANK) * hfreq; + pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; + + h_bp = CVT_RB_H_BPORCH; + hsync = CVT_RB_H_SYNC; + h_fp = CVT_RB_H_BLANK - h_bp - hsync; + + frame_width = image_width + CVT_RB_H_BLANK; + } else { + int h_blank; + unsigned ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * 1000) / hfreq; + + h_blank = (image_width * ideal_duty_cycle + (100 - ideal_duty_cycle) / 2) / + (100 - ideal_duty_cycle); + h_blank = h_blank - h_blank % (2 * CVT_CELL_GRAN); + + if (h_blank * 100 / image_width < 20) { + h_blank = image_width / 5; + h_blank = (h_blank + 0x7) & ~0x7; + } + + pix_clk = (image_width + h_blank) * hfreq; + pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; + + h_bp = h_blank / 2; + frame_width = image_width + h_blank; + + hsync = (frame_width * 8 + 50) / 100; + hsync = hsync - hsync % CVT_CELL_GRAN; + h_fp = h_blank - hsync - h_bp; + } + + fmt->bt.polarities = polarities; + fmt->bt.width = image_width; + fmt->bt.height = image_height; + fmt->bt.hfrontporch = h_fp; + fmt->bt.vfrontporch = v_fp; + fmt->bt.hsync = hsync; + fmt->bt.vsync = vsync; + fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; + fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; + fmt->bt.pixelclock = pix_clk; + fmt->bt.standards = V4L2_DV_BT_STD_CVT; + if (reduced_blanking) + fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; + return true; +} +EXPORT_SYMBOL_GPL(v4l2_detect_cvt); + +/* + * GTF defines + * Based on Generalized Timing Formula Standard + * Version 1.1 September 2, 1999 + */ + +#define GTF_PXL_CLK_GRAN 250000 /* pixel clock granularity */ + +#define GTF_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ +#define GTF_V_FP 1 /* vertical front porch (lines) */ +#define GTF_CELL_GRAN 8 /* character cell granularity */ + +/* Default */ +#define GTF_D_M 600 /* blanking formula gradient */ +#define GTF_D_C 40 /* blanking formula offset */ +#define GTF_D_K 128 /* blanking formula scaling factor */ +#define GTF_D_J 20 /* blanking formula scaling factor */ +#define GTF_D_C_PRIME ((((GTF_D_C - GTF_D_J) * GTF_D_K) / 256) + GTF_D_J) +#define GTF_D_M_PRIME ((GTF_D_K * GTF_D_M) / 256) + +/* Secondary */ +#define GTF_S_M 3600 /* blanking formula gradient */ +#define GTF_S_C 40 /* blanking formula offset */ +#define GTF_S_K 128 /* blanking formula scaling factor */ +#define GTF_S_J 35 /* blanking formula scaling factor */ +#define GTF_S_C_PRIME ((((GTF_S_C - GTF_S_J) * GTF_S_K) / 256) + GTF_S_J) +#define GTF_S_M_PRIME ((GTF_S_K * GTF_S_M) / 256) + +/** v4l2_detect_gtf - detect if the given timings follow the GTF standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @aspect - preferred aspect ratio. GTF has no method of determining the + * aspect ratio in order to derive the image width from the + * image height, so it has to be passed explicitly. Usually + * the native screen aspect ratio is used for this. If it + * is not filled in correctly, then 16:9 will be assumed. + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid GTF format. If so, then it will return true, and fmt will be filled + * in with the found GTF timings. + */ +bool v4l2_detect_gtf(unsigned frame_height, + unsigned hfreq, + unsigned vsync, + u32 polarities, + struct v4l2_fract aspect, + struct v4l2_dv_timings *fmt) +{ + int pix_clk; + int v_fp, v_bp, h_fp, hsync; + int frame_width, image_height, image_width; + bool default_gtf; + int h_blank; + + if (vsync != 3) + return false; + + if (polarities == V4L2_DV_VSYNC_POS_POL) + default_gtf = true; + else if (polarities == V4L2_DV_HSYNC_POS_POL) + default_gtf = false; + else + return false; + + /* Vertical */ + v_fp = GTF_V_FP; + v_bp = (GTF_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; + image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; + + if (aspect.numerator == 0 || aspect.denominator == 0) { + aspect.numerator = 16; + aspect.denominator = 9; + } + image_width = ((image_height * aspect.numerator) / aspect.denominator); + + /* Horizontal */ + if (default_gtf) + h_blank = ((image_width * GTF_D_C_PRIME * hfreq) - + (image_width * GTF_D_M_PRIME * 1000) + + (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000) / 2) / + (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000); + else + h_blank = ((image_width * GTF_S_C_PRIME * hfreq) - + (image_width * GTF_S_M_PRIME * 1000) + + (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000) / 2) / + (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000); + + h_blank = h_blank - h_blank % (2 * GTF_CELL_GRAN); + frame_width = image_width + h_blank; + + pix_clk = (image_width + h_blank) * hfreq; + pix_clk = pix_clk / GTF_PXL_CLK_GRAN * GTF_PXL_CLK_GRAN; + + hsync = (frame_width * 8 + 50) / 100; + hsync = hsync - hsync % GTF_CELL_GRAN; + + h_fp = h_blank / 2 - hsync; + + fmt->bt.polarities = polarities; + fmt->bt.width = image_width; + fmt->bt.height = image_height; + fmt->bt.hfrontporch = h_fp; + fmt->bt.vfrontporch = v_fp; + fmt->bt.hsync = hsync; + fmt->bt.vsync = vsync; + fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; + fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; + fmt->bt.pixelclock = pix_clk; + fmt->bt.standards = V4L2_DV_BT_STD_GTF; + if (!default_gtf) + fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; + return true; +} +EXPORT_SYMBOL_GPL(v4l2_detect_gtf); + +/** v4l2_calc_aspect_ratio - calculate the aspect ratio based on bytes + * 0x15 and 0x16 from the EDID. + * @hor_landscape - byte 0x15 from the EDID. + * @vert_portrait - byte 0x16 from the EDID. + * + * Determines the aspect ratio from the EDID. + * See VESA Enhanced EDID standard, release A, rev 2, section 3.6.2: + * "Horizontal and Vertical Screen Size or Aspect Ratio" + */ +struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait) +{ + struct v4l2_fract aspect = { 16, 9 }; + u32 tmp; + u8 ratio; + + /* Nothing filled in, fallback to 16:9 */ + if (!hor_landscape && !vert_portrait) + return aspect; + /* Both filled in, so they are interpreted as the screen size in cm */ + if (hor_landscape && vert_portrait) { + aspect.numerator = hor_landscape; + aspect.denominator = vert_portrait; + return aspect; + } + /* Only one is filled in, so interpret them as a ratio: + (val + 99) / 100 */ + ratio = hor_landscape | vert_portrait; + /* Change some rounded values into the exact aspect ratio */ + if (ratio == 79) { + aspect.numerator = 16; + aspect.denominator = 9; + } else if (ratio == 34) { + aspect.numerator = 4; + aspect.numerator = 3; + } else if (ratio == 68) { + aspect.numerator = 15; + aspect.numerator = 9; + } else { + aspect.numerator = hor_landscape + 99; + aspect.denominator = 100; + } + if (hor_landscape) + return aspect; + /* The aspect ratio is for portrait, so swap numerator and denominator */ + tmp = aspect.denominator; + aspect.denominator = aspect.numerator; + aspect.numerator = tmp; + return aspect; +} +EXPORT_SYMBOL_GPL(v4l2_calc_aspect_ratio); diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 015ff82..0e1d010 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -201,19 +201,6 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( const struct v4l2_discrete_probe *probe, s32 width, s32 height); -bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, - const struct v4l2_dv_timings *t2, - unsigned pclock_delta); - -bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, struct v4l2_dv_timings *fmt); - -bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, struct v4l2_fract aspect, - struct v4l2_dv_timings *fmt); - -struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait); - void v4l2_get_timestamp(struct timeval *tv); #endif /* V4L2_COMMON_H_ */ diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 41075fa..4c7bb54 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -64,4 +64,63 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, unsigned pclock_delta); +/** v4l_match_dv_timings() - do two timings match? + * @measured: the measured timings data. + * @standard: the timings according to the standard. + * @pclock_delta: maximum delta in Hz between standard->pixelclock and + * the measured timings. + * + * Returns true if the two timings match, returns false otherwise. + */ +bool v4l_match_dv_timings(const struct v4l2_dv_timings *measured, + const struct v4l2_dv_timings *standard, + unsigned pclock_delta); + +/** v4l2_detect_cvt - detect if the given timings follow the CVT standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid CVT format. If so, then it will return true, and fmt will be filled + * in with the found CVT timings. + */ +bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, + u32 polarities, struct v4l2_dv_timings *fmt); + +/** v4l2_detect_gtf - detect if the given timings follow the GTF standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @aspect - preferred aspect ratio. GTF has no method of determining the + * aspect ratio in order to derive the image width from the + * image height, so it has to be passed explicitly. Usually + * the native screen aspect ratio is used for this. If it + * is not filled in correctly, then 16:9 will be assumed. + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid GTF format. If so, then it will return true, and fmt will be filled + * in with the found GTF timings. + */ +bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync, + u32 polarities, struct v4l2_fract aspect, + struct v4l2_dv_timings *fmt); + +/** v4l2_calc_aspect_ratio - calculate the aspect ratio based on bytes + * 0x15 and 0x16 from the EDID. + * @hor_landscape - byte 0x15 from the EDID. + * @vert_portrait - byte 0x16 from the EDID. + * + * Determines the aspect ratio from the EDID. + * See VESA Enhanced EDID standard, release A, rev 2, section 3.6.2: + * "Horizontal and Vertical Screen Size or Aspect Ratio" + */ +struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait); + #endif -- cgit v0.10.2 From 5d2db817df059aa1873560ea4d6f13624cbc823d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:57 -0300 Subject: [media] DocBook/media/v4l: il_* fields always 0 for progressive formats Clarify that the il_vfrontporch, il_vsync and il_vbackporch fields must always be 0 for progressive formats. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml index 7236970..c433657 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml @@ -156,19 +156,19 @@ bit 0 (V4L2_DV_VSYNC_POS_POL) is for vertical sync polarity and bit 1 (V4L2_DV_H __u32 il_vfrontporch Vertical front porch in lines for the even field (aka field 2) of - interlaced field formats. + interlaced field formats. Must be 0 for progressive formats.
__u32 il_vsync Vertical sync length in lines for the even field (aka field 2) of - interlaced field formats. + interlaced field formats. Must be 0 for progressive formats. __u32 il_vbackporch Vertical back porch in lines for the even field (aka field 2) of - interlaced field formats. + interlaced field formats. Must be 0 for progressive formats. __u32 -- cgit v0.10.2 From 7f68127fa11f08c5468537eb28ca6b8b95d70f08 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:58 -0300 Subject: [media] videodev2.h: defines to calculate blanking and frame sizes It is very common to have to calculate the total width and height of the blanking and the full frame, so add a few defines that deal with that. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index fec0c20..437f1b0 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1057,6 +1057,16 @@ struct v4l2_bt_timings { or used depends on the hardware. */ #define V4L2_DV_FL_HALF_LINE (1 << 3) +/* A few useful defines to calculate the total blanking and frame sizes */ +#define V4L2_DV_BT_BLANKING_WIDTH(bt) \ + (bt->hfrontporch + bt->hsync + bt->hbackporch) +#define V4L2_DV_BT_FRAME_WIDTH(bt) \ + (bt->width + V4L2_DV_BT_BLANKING_WIDTH(bt)) +#define V4L2_DV_BT_BLANKING_HEIGHT(bt) \ + (bt->vfrontporch + bt->vsync + bt->vbackporch + \ + bt->il_vfrontporch + bt->il_vsync + bt->il_vbackporch) +#define V4L2_DV_BT_FRAME_HEIGHT(bt) \ + (bt->height + V4L2_DV_BT_BLANKING_HEIGHT(bt)) /** struct v4l2_dv_timings - DV timings * @type: the type of the timings -- cgit v0.10.2 From eacf8f9aa80e2231af43b589cfc18c054e417220 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:59 -0300 Subject: [media] v4l2: use new V4L2_DV_BT_BLANKING/FRAME defines Use the new defines to calculate the full blanking and frame sizes. Signed-off-by: Hans Verkuil Acked-by: Scott Jiang Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 2fa8d72..5295234 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -445,10 +445,8 @@ static int ad9389b_log_status(struct v4l2_subdev *sd) } if (state->dv_timings.type == V4L2_DV_BT_656_1120) { struct v4l2_bt_timings *bt = bt = &state->dv_timings.bt; - u32 frame_width = bt->width + bt->hfrontporch + - bt->hsync + bt->hbackporch; - u32 frame_height = bt->height + bt->vfrontporch + - bt->vsync + bt->vbackporch; + u32 frame_width = V4L2_DV_BT_FRAME_WIDTH(bt); + u32 frame_height = V4L2_DV_BT_FRAME_HEIGHT(bt); u32 frame_size = frame_width * frame_height; v4l2_info(sd, "timings: %ux%u%s%u (%ux%u). Pix freq. = %u Hz. Polarities = 0x%x\n", diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 181a6c3..3ec7ec0 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -261,22 +261,22 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) static inline unsigned hblanking(const struct v4l2_bt_timings *t) { - return t->hfrontporch + t->hsync + t->hbackporch; + return V4L2_DV_BT_BLANKING_WIDTH(t); } static inline unsigned htotal(const struct v4l2_bt_timings *t) { - return t->width + t->hfrontporch + t->hsync + t->hbackporch; + return V4L2_DV_BT_FRAME_WIDTH(t); } static inline unsigned vblanking(const struct v4l2_bt_timings *t) { - return t->vfrontporch + t->vsync + t->vbackporch; + return V4L2_DV_BT_BLANKING_HEIGHT(t); } static inline unsigned vtotal(const struct v4l2_bt_timings *t) { - return t->height + t->vfrontporch + t->vsync + t->vbackporch; + return V4L2_DV_BT_FRAME_HEIGHT(t); } /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index 0a2dacb..42276d9 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -291,10 +291,8 @@ static int ths7303_log_status(struct v4l2_subdev *sd) struct v4l2_bt_timings *bt = bt = &state->bt; u32 frame_width, frame_height; - frame_width = bt->width + bt->hfrontporch + - bt->hsync + bt->hbackporch; - frame_height = bt->height + bt->vfrontporch + - bt->vsync + bt->vbackporch; + frame_width = V4L2_DV_BT_FRAME_WIDTH(bt); + frame_height = V4L2_DV_BT_FRAME_HEIGHT(bt); v4l2_info(sd, "timings: %dx%d%s%d (%dx%d). Pix freq. = %d Hz. Polarities = 0x%x\n", bt->width, bt->height, bt->interlaced ? "i" : "p", diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index aef7c0e..28932e9 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -65,22 +65,22 @@ static inline struct ths8200_state *to_state(struct v4l2_subdev *sd) static inline unsigned hblanking(const struct v4l2_bt_timings *t) { - return t->hfrontporch + t->hsync + t->hbackporch; + return V4L2_DV_BT_BLANKING_WIDTH(t); } static inline unsigned htotal(const struct v4l2_bt_timings *t) { - return t->width + t->hfrontporch + t->hsync + t->hbackporch; + return V4L2_DV_BT_FRAME_WIDTH(t); } static inline unsigned vblanking(const struct v4l2_bt_timings *t) { - return t->vfrontporch + t->vsync + t->vbackporch; + return V4L2_DV_BT_BLANKING_HEIGHT(t); } static inline unsigned vtotal(const struct v4l2_bt_timings *t) { - return t->height + t->vfrontporch + t->vsync + t->vbackporch; + return V4L2_DV_BT_FRAME_HEIGHT(t); } static int ths8200_read(struct v4l2_subdev *sd, u8 reg) diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 7f838c6..4c11059 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -388,13 +388,8 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) params.hdelay = bt->hsync + bt->hbackporch; params.vdelay = bt->vsync + bt->vbackporch; - params.line = bt->hfrontporch + bt->hsync - + bt->hbackporch + bt->width; - params.frame = bt->vfrontporch + bt->vsync - + bt->vbackporch + bt->height; - if (bt->interlaced) - params.frame += bt->il_vfrontporch + bt->il_vsync - + bt->il_vbackporch; + params.line = V4L2_DV_BT_FRAME_WIDTH(bt); + params.frame = V4L2_DV_BT_FRAME_HEIGHT(bt); } else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities & V4L2_IN_CAP_STD) { params.hdelay = 0; diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 9c67b6e..e68245a 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -690,10 +690,8 @@ static int vidioc_query_dv_timings(struct file *file, void *_fh, unsigned vsize; unsigned fps; - hsize = bt->hfrontporch + bt->hsync + bt->hbackporch + bt->width; - vsize = bt->vfrontporch + bt->vsync + bt->vbackporch + - bt->il_vfrontporch + bt->il_vsync + bt->il_vbackporch + - bt->height; + hsize = V4L2_DV_BT_FRAME_WIDTH(bt); + vsize = V4L2_DV_BT_FRAME_HEIGHT(bt); fps = (unsigned)bt->pixelclock / (hsize * vsize); if (bt->width != vid_info.width || bt->height != vid_info.height || -- cgit v0.10.2 From e36552684e24f8b21569dec47381c36b50a7966b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:41:00 -0300 Subject: [media] v4l2: use new V4L2_DV_BT_BLANKING/FRAME defines Use the new blanking and frame size defines. This also fixed a bug in these drivers: they assumed that the height for interlaced formats was the field height, however height is the frame height. So the height for a field is actually bt->height / 2. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar 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 7fbde6d..b79280f 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1799,19 +1799,15 @@ static int vpif_s_dv_timings(struct file *file, void *priv, /* Configure video port timings */ - std_info->eav2sav = bt->hbackporch + bt->hfrontporch + - bt->hsync - 8; + std_info->eav2sav = V4L2_DV_BT_BLANKING_WIDTH(bt) - 8; std_info->sav2eav = bt->width; std_info->l1 = 1; std_info->l3 = bt->vsync + bt->vbackporch + 1; + std_info->vsize = V4L2_DV_BT_FRAME_HEIGHT(bt); if (bt->interlaced) { if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) { - std_info->vsize = bt->height * 2 + - bt->vfrontporch + bt->vsync + bt->vbackporch + - bt->il_vfrontporch + bt->il_vsync + - bt->il_vbackporch; std_info->l5 = std_info->vsize/2 - (bt->vfrontporch - 1); std_info->l7 = std_info->vsize/2 + 1; @@ -1825,8 +1821,6 @@ static int vpif_s_dv_timings(struct file *file, void *priv, return -EINVAL; } } else { - std_info->vsize = bt->height + bt->vfrontporch + - bt->vsync + bt->vbackporch; std_info->l5 = std_info->vsize - (bt->vfrontporch - 1); } strncpy(std_info->name, "Custom timings BT656/1120", VPIF_MAX_NAME); diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 6336dfc..f2b4ae1 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1436,19 +1436,15 @@ static int vpif_s_dv_timings(struct file *file, void *priv, /* Configure video port timings */ - std_info->eav2sav = bt->hbackporch + bt->hfrontporch + - bt->hsync - 8; + std_info->eav2sav = V4L2_DV_BT_BLANKING_WIDTH(bt) - 8; std_info->sav2eav = bt->width; std_info->l1 = 1; std_info->l3 = bt->vsync + bt->vbackporch + 1; + std_info->vsize = V4L2_DV_BT_FRAME_HEIGHT(bt); if (bt->interlaced) { if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) { - std_info->vsize = bt->height * 2 + - bt->vfrontporch + bt->vsync + bt->vbackporch + - bt->il_vfrontporch + bt->il_vsync + - bt->il_vbackporch; std_info->l5 = std_info->vsize/2 - (bt->vfrontporch - 1); std_info->l7 = std_info->vsize/2 + 1; @@ -1462,8 +1458,6 @@ static int vpif_s_dv_timings(struct file *file, void *priv, return -EINVAL; } } else { - std_info->vsize = bt->height + bt->vfrontporch + - bt->vsync + bt->vbackporch; std_info->l5 = std_info->vsize - (bt->vfrontporch - 1); } strncpy(std_info->name, "Custom timings BT656/1120", -- cgit v0.10.2 From 041649048149b3e4afa56428e6d1b081b9dd49f5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:41:01 -0300 Subject: [media] ths8200/ad9389b: use new dv_timings helpers Simplify the code by using the new dv_timings helpers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 5295234..7e68d8f 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -635,95 +635,34 @@ static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static const struct v4l2_dv_timings ad9389b_timings[] = { - V4L2_DV_BT_CEA_720X480P59_94, - V4L2_DV_BT_CEA_720X576P50, - V4L2_DV_BT_CEA_1280X720P24, - V4L2_DV_BT_CEA_1280X720P25, - V4L2_DV_BT_CEA_1280X720P30, - V4L2_DV_BT_CEA_1280X720P50, - V4L2_DV_BT_CEA_1280X720P60, - V4L2_DV_BT_CEA_1920X1080P24, - V4L2_DV_BT_CEA_1920X1080P25, - V4L2_DV_BT_CEA_1920X1080P30, - V4L2_DV_BT_CEA_1920X1080P50, - V4L2_DV_BT_CEA_1920X1080P60, - - V4L2_DV_BT_DMT_640X350P85, - V4L2_DV_BT_DMT_640X400P85, - V4L2_DV_BT_DMT_720X400P85, - V4L2_DV_BT_DMT_640X480P60, - V4L2_DV_BT_DMT_640X480P72, - V4L2_DV_BT_DMT_640X480P75, - V4L2_DV_BT_DMT_640X480P85, - V4L2_DV_BT_DMT_800X600P56, - V4L2_DV_BT_DMT_800X600P60, - V4L2_DV_BT_DMT_800X600P72, - V4L2_DV_BT_DMT_800X600P75, - V4L2_DV_BT_DMT_800X600P85, - V4L2_DV_BT_DMT_848X480P60, - V4L2_DV_BT_DMT_1024X768P60, - V4L2_DV_BT_DMT_1024X768P70, - V4L2_DV_BT_DMT_1024X768P75, - V4L2_DV_BT_DMT_1024X768P85, - V4L2_DV_BT_DMT_1152X864P75, - V4L2_DV_BT_DMT_1280X768P60_RB, - V4L2_DV_BT_DMT_1280X768P60, - V4L2_DV_BT_DMT_1280X768P75, - V4L2_DV_BT_DMT_1280X768P85, - V4L2_DV_BT_DMT_1280X800P60_RB, - V4L2_DV_BT_DMT_1280X800P60, - V4L2_DV_BT_DMT_1280X800P75, - V4L2_DV_BT_DMT_1280X800P85, - V4L2_DV_BT_DMT_1280X960P60, - V4L2_DV_BT_DMT_1280X960P85, - V4L2_DV_BT_DMT_1280X1024P60, - V4L2_DV_BT_DMT_1280X1024P75, - V4L2_DV_BT_DMT_1280X1024P85, - V4L2_DV_BT_DMT_1360X768P60, - V4L2_DV_BT_DMT_1400X1050P60_RB, - V4L2_DV_BT_DMT_1400X1050P60, - V4L2_DV_BT_DMT_1400X1050P75, - V4L2_DV_BT_DMT_1400X1050P85, - V4L2_DV_BT_DMT_1440X900P60_RB, - V4L2_DV_BT_DMT_1440X900P60, - V4L2_DV_BT_DMT_1600X1200P60, - V4L2_DV_BT_DMT_1680X1050P60_RB, - V4L2_DV_BT_DMT_1680X1050P60, - V4L2_DV_BT_DMT_1792X1344P60, - V4L2_DV_BT_DMT_1856X1392P60, - V4L2_DV_BT_DMT_1920X1200P60_RB, - V4L2_DV_BT_DMT_1366X768P60, - V4L2_DV_BT_DMT_1920X1080P60, - {}, +static const struct v4l2_dv_timings_cap ad9389b_timings_cap = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .max_width = 1920, + .max_height = 1200, + .min_pixelclock = 27000000, + .max_pixelclock = 170000000, + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM, + }, }; static int ad9389b_s_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { struct ad9389b_state *state = get_ad9389b_state(sd); - int i; v4l2_dbg(1, debug, sd, "%s:\n", __func__); /* quick sanity check */ - if (timings->type != V4L2_DV_BT_656_1120) - return -EINVAL; - - if (timings->bt.interlaced) - return -EINVAL; - if (timings->bt.pixelclock < 27000000 || - timings->bt.pixelclock > 170000000) + if (!v4l2_dv_valid_timings(timings, &ad9389b_timings_cap)) return -EINVAL; /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings - if the format is listed in ad9389b_timings[] */ - for (i = 0; ad9389b_timings[i].bt.width; i++) { - if (v4l_match_dv_timings(timings, &ad9389b_timings[i], 0)) { - *timings = ad9389b_timings[i]; - break; - } - } + if the format is one of the CEA or DMT timings. */ + v4l2_find_dv_timings_cap(timings, &ad9389b_timings_cap, 0); timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS; @@ -761,26 +700,13 @@ static int ad9389b_g_dv_timings(struct v4l2_subdev *sd, static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { - if (timings->index >= ARRAY_SIZE(ad9389b_timings)) - return -EINVAL; - - memset(timings->reserved, 0, sizeof(timings->reserved)); - timings->timings = ad9389b_timings[timings->index]; - return 0; + return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap); } static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { - cap->type = V4L2_DV_BT_656_1120; - cap->bt.max_width = 1920; - cap->bt.max_height = 1200; - cap->bt.min_pixelclock = 27000000; - cap->bt.max_pixelclock = 170000000; - cap->bt.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | - V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT; - cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | - V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM; + *cap = ad9389b_timings_cap; return 0; } diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 28932e9..7a60a8f 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -44,18 +44,16 @@ struct ths8200_state { struct v4l2_dv_timings dv_timings; }; -static const struct v4l2_dv_timings ths8200_timings[] = { - V4L2_DV_BT_CEA_720X480P59_94, - V4L2_DV_BT_CEA_1280X720P24, - V4L2_DV_BT_CEA_1280X720P25, - V4L2_DV_BT_CEA_1280X720P30, - V4L2_DV_BT_CEA_1280X720P50, - V4L2_DV_BT_CEA_1280X720P60, - V4L2_DV_BT_CEA_1920X1080P24, - V4L2_DV_BT_CEA_1920X1080P25, - V4L2_DV_BT_CEA_1920X1080P30, - V4L2_DV_BT_CEA_1920X1080P50, - V4L2_DV_BT_CEA_1920X1080P60, +static const struct v4l2_dv_timings_cap ths8200_timings_cap = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .max_width = 1920, + .max_height = 1080, + .min_pixelclock = 27000000, + .max_pixelclock = 148500000, + .standards = V4L2_DV_BT_STD_CEA861, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE, + }, }; static inline struct ths8200_state *to_state(struct v4l2_subdev *sd) @@ -411,25 +409,13 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { struct ths8200_state *state = to_state(sd); - int i; v4l2_dbg(1, debug, sd, "%s:\n", __func__); - if (timings->type != V4L2_DV_BT_656_1120) + if (!v4l2_dv_valid_timings(timings, &ths8200_timings_cap)) return -EINVAL; - /* TODO Support interlaced formats */ - if (timings->bt.interlaced) { - v4l2_dbg(1, debug, sd, "TODO Support interlaced formats\n"); - return -EINVAL; - } - - for (i = 0; i < ARRAY_SIZE(ths8200_timings); i++) { - if (v4l_match_dv_timings(&ths8200_timings[i], timings, 10)) - break; - } - - if (i == ARRAY_SIZE(ths8200_timings)) { + if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10)) { v4l2_dbg(1, debug, sd, "Unsupported format\n"); return -EINVAL; } @@ -459,26 +445,13 @@ static int ths8200_g_dv_timings(struct v4l2_subdev *sd, static int ths8200_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { - /* Check requested format index is within range */ - if (timings->index >= ARRAY_SIZE(ths8200_timings)) - return -EINVAL; - - timings->timings = ths8200_timings[timings->index]; - - return 0; + return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap); } static int ths8200_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { - cap->type = V4L2_DV_BT_656_1120; - cap->bt.max_width = 1920; - cap->bt.max_height = 1080; - cap->bt.min_pixelclock = 27000000; - cap->bt.max_pixelclock = 148500000; - cap->bt.standards = V4L2_DV_BT_STD_CEA861; - cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE; - + *cap = ths8200_timings_cap; return 0; } -- cgit v0.10.2 From 8b77dfdd8c5838939251cf135ea8363dff108e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Arne=20J=C3=B8rgensen?= Date: Sat, 3 Aug 2013 09:19:35 -0300 Subject: [media] saa7115: Fix saa711x_set_v4lstd for gm7113c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit saa711x_set_v4lstd would toggle several bits that should not be touched when changing std. This patch fixes this. Signed-off-by: Jon Arne Jørgensen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 7fd766e..41408dd 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -462,24 +462,6 @@ static const unsigned char saa7115_cfg_50hz_video[] = { /* ============== SAA7715 VIDEO templates (end) ======= */ -/* ============== GM7113C VIDEO templates ============= */ -static const unsigned char gm7113c_cfg_60hz_video[] = { - R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */ - R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */ - - 0x00, 0x00 -}; - -static const unsigned char gm7113c_cfg_50hz_video[] = { - R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */ - R_0E_CHROMA_CNTL_1, 0x07, - - 0x00, 0x00 -}; - -/* ============== GM7113C VIDEO templates (end) ======= */ - - static const unsigned char saa7115_cfg_vbi_on[] = { R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ @@ -964,17 +946,24 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. if (std & V4L2_STD_525_60) { v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n"); - if (state->ident == GM7113C) - saa711x_writeregs(sd, gm7113c_cfg_60hz_video); - else + if (state->ident == GM7113C) { + u8 reg = saa711x_read(sd, R_08_SYNC_CNTL); + reg &= ~(SAA7113_R_08_FSEL | SAA7113_R_08_AUFD); + reg |= SAA7113_R_08_FSEL; + saa711x_write(sd, R_08_SYNC_CNTL, reg); + } else { saa711x_writeregs(sd, saa7115_cfg_60hz_video); + } saa711x_set_size(sd, 720, 480); } else { v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n"); - if (state->ident == GM7113C) - saa711x_writeregs(sd, gm7113c_cfg_50hz_video); - else + if (state->ident == GM7113C) { + u8 reg = saa711x_read(sd, R_08_SYNC_CNTL); + reg &= ~(SAA7113_R_08_FSEL | SAA7113_R_08_AUFD); + saa711x_write(sd, R_08_SYNC_CNTL, reg); + } else { saa711x_writeregs(sd, saa7115_cfg_50hz_video); + } saa711x_set_size(sd, 720, 576); } diff --git a/drivers/media/i2c/saa711x_regs.h b/drivers/media/i2c/saa711x_regs.h index 4e5f2eb..70c56d1 100644 --- a/drivers/media/i2c/saa711x_regs.h +++ b/drivers/media/i2c/saa711x_regs.h @@ -201,6 +201,10 @@ #define R_FB_PULSE_C_POS_MSB 0xfb #define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff +/* SAA7113 bit-masks */ +#define SAA7113_R_08_FSEL 0x40 +#define SAA7113_R_08_AUFD 0x80 + #if 0 /* Those structs will be used in the future for debug purposes */ struct saa711x_reg_descr { -- cgit v0.10.2 From b9798bc160968107c82e119f2a59de25f6e70291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Arne=20J=C3=B8rgensen?= Date: Sat, 3 Aug 2013 09:19:36 -0300 Subject: [media] saa7115: Do not load saa7115_init_misc for gm7113c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the registers changed in saa7115_init_misc table are out of range for the gm7113c chip. The only register that's within range, don't need to be changed here. Signed-off-by: Jon Arne Jørgensen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 41408dd..17a464d 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1769,7 +1769,7 @@ static int saa711x_probe(struct i2c_client *client, state->crystal_freq = SAA7115_FREQ_32_11_MHZ; saa711x_writeregs(sd, saa7115_init_auto_input); } - if (state->ident > SAA7111A) + if (state->ident > SAA7111A && state->ident != GM7113C) saa711x_writeregs(sd, saa7115_init_misc); saa711x_set_v4lstd(sd, V4L2_STD_NTSC); v4l2_ctrl_handler_setup(hdl); -- cgit v0.10.2 From 2ccf12afe6da2145085056cebaae2149899f4f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Arne=20J=C3=B8rgensen?= Date: Sat, 3 Aug 2013 09:19:37 -0300 Subject: [media] saa7115: Implement i2c_board_info.platform_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements i2c_board_info.platform_data, and some options to override the default initialization table for the GM7113C and SAA7113 chips. Signed-off-by: Jon Arne Jørgensen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 17a464d..5cc48f7 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -225,19 +225,59 @@ static const unsigned char saa7111_init[] = { 0x00, 0x00 }; -/* SAA7113/GM7113C init codes - * It's important that R_14... R_17 == 0x00 - * for the gm7113c chip to deliver stable video - */ +/* This table has one illegal value, and some values that are not + correct according to the datasheet initialization table. + + If you need a table with legal/default values tell the driver in + i2c_board_info.platform_data, and you will get the gm7113c_init + table instead. */ + +/* SAA7113 Init codes */ static const unsigned char saa7113_init[] = { R_01_INC_DELAY, 0x08, R_02_INPUT_CNTL_1, 0xc2, R_03_INPUT_CNTL_2, 0x30, R_04_INPUT_CNTL_3, 0x00, R_05_INPUT_CNTL_4, 0x00, - R_06_H_SYNC_START, 0x89, + R_06_H_SYNC_START, 0x89, /* Illegal value -119, + * min. value = -108 (0x94) */ + R_07_H_SYNC_STOP, 0x0d, + R_08_SYNC_CNTL, 0x88, /* Not datasheet default. + * HTC = VTR mode, should be 0x98 */ + R_09_LUMA_CNTL, 0x01, + R_0A_LUMA_BRIGHT_CNTL, 0x80, + R_0B_LUMA_CONTRAST_CNTL, 0x47, + R_0C_CHROMA_SAT_CNTL, 0x40, + R_0D_CHROMA_HUE_CNTL, 0x00, + R_0E_CHROMA_CNTL_1, 0x01, + R_0F_CHROMA_GAIN_CNTL, 0x2a, + R_10_CHROMA_CNTL_2, 0x08, /* Not datsheet default. + * VRLN enabled, should be 0x00 */ + R_11_MODE_DELAY_CNTL, 0x0c, + R_12_RT_SIGNAL_CNTL, 0x07, /* Not datasheet default, + * should be 0x01 */ + R_13_RT_X_PORT_OUT_CNTL, 0x00, + R_14_ANAL_ADC_COMPAT_CNTL, 0x00, + R_15_VGATE_START_FID_CHG, 0x00, + R_16_VGATE_STOP, 0x00, + R_17_MISC_VGATE_CONF_AND_MSB, 0x00, + + 0x00, 0x00 +}; + +/* GM7113C is a clone of the SAA7113 chip + This init table is copied out of the saa7113 datasheet. + In R_08 we enable "Automatic Field Detection" [AUFD], + this is disabled when saa711x_set_v4lstd is called. */ +static const unsigned char gm7113c_init[] = { + R_01_INC_DELAY, 0x08, + R_02_INPUT_CNTL_1, 0xc0, + R_03_INPUT_CNTL_2, 0x33, + R_04_INPUT_CNTL_3, 0x00, + R_05_INPUT_CNTL_4, 0x00, + R_06_H_SYNC_START, 0xe9, R_07_H_SYNC_STOP, 0x0d, - R_08_SYNC_CNTL, 0x88, + R_08_SYNC_CNTL, 0x98, R_09_LUMA_CNTL, 0x01, R_0A_LUMA_BRIGHT_CNTL, 0x80, R_0B_LUMA_CONTRAST_CNTL, 0x47, @@ -245,9 +285,9 @@ static const unsigned char saa7113_init[] = { R_0D_CHROMA_HUE_CNTL, 0x00, R_0E_CHROMA_CNTL_1, 0x01, R_0F_CHROMA_GAIN_CNTL, 0x2a, - R_10_CHROMA_CNTL_2, 0x08, + R_10_CHROMA_CNTL_2, 0x00, R_11_MODE_DELAY_CNTL, 0x0c, - R_12_RT_SIGNAL_CNTL, 0x07, + R_12_RT_SIGNAL_CNTL, 0x01, R_13_RT_X_PORT_OUT_CNTL, 0x00, R_14_ANAL_ADC_COMPAT_CNTL, 0x00, R_15_VGATE_START_FID_CHG, 0x00, @@ -1585,6 +1625,65 @@ static const struct v4l2_subdev_ops saa711x_ops = { /* ----------------------------------------------------------------------- */ +static void saa711x_write_platform_data(struct saa711x_state *state, + struct saa7115_platform_data *data) +{ + struct v4l2_subdev *sd = &state->sd; + u8 work; + + if (state->ident != GM7113C && + state->ident != SAA7113) + return; + + if (data->saa7113_r08_htc) { + work = saa711x_read(sd, R_08_SYNC_CNTL); + work &= ~SAA7113_R_08_HTC_MASK; + work |= ((*data->saa7113_r08_htc) << SAA7113_R_08_HTC_OFFSET); + saa711x_write(sd, R_08_SYNC_CNTL, work); + } + + if (data->saa7113_r10_vrln) { + work = saa711x_read(sd, R_10_CHROMA_CNTL_2); + work &= ~SAA7113_R_10_VRLN_MASK; + if (*data->saa7113_r10_vrln) + work |= (1 << SAA7113_R_10_VRLN_OFFSET); + saa711x_write(sd, R_10_CHROMA_CNTL_2, work); + } + + if (data->saa7113_r10_ofts) { + work = saa711x_read(sd, R_10_CHROMA_CNTL_2); + work &= ~SAA7113_R_10_OFTS_MASK; + work |= (*data->saa7113_r10_ofts << SAA7113_R_10_OFTS_OFFSET); + saa711x_write(sd, R_10_CHROMA_CNTL_2, work); + } + + if (data->saa7113_r12_rts0) { + work = saa711x_read(sd, R_12_RT_SIGNAL_CNTL); + work &= ~SAA7113_R_12_RTS0_MASK; + work |= (*data->saa7113_r12_rts0 << SAA7113_R_12_RTS0_OFFSET); + + /* According to the datasheet, + * SAA7113_RTS_DOT_IN should only be used on RTS1 */ + WARN_ON(*data->saa7113_r12_rts0 == SAA7113_RTS_DOT_IN); + saa711x_write(sd, R_12_RT_SIGNAL_CNTL, work); + } + + if (data->saa7113_r12_rts1) { + work = saa711x_read(sd, R_12_RT_SIGNAL_CNTL); + work &= ~SAA7113_R_12_RTS1_MASK; + work |= (*data->saa7113_r12_rts1 << SAA7113_R_12_RTS1_OFFSET); + saa711x_write(sd, R_12_RT_SIGNAL_CNTL, work); + } + + if (data->saa7113_r13_adlsb) { + work = saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL); + work &= ~SAA7113_R_13_ADLSB_MASK; + if (*data->saa7113_r13_adlsb) + work |= (1 << SAA7113_R_13_ADLSB_OFFSET); + saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, work); + } +} + /** * saa711x_detect_chip - Detects the saa711x (or clone) variant * @client: I2C client structure. @@ -1693,6 +1792,7 @@ static int saa711x_probe(struct i2c_client *client, struct saa711x_state *state; struct v4l2_subdev *sd; struct v4l2_ctrl_handler *hdl; + struct saa7115_platform_data *pdata; int ident; char name[CHIP_VER_SIZE + 1]; @@ -1756,14 +1856,20 @@ static int saa711x_probe(struct i2c_client *client, /* init to 60hz/48khz */ state->crystal_freq = SAA7115_FREQ_24_576_MHZ; + pdata = client->dev.platform_data; switch (state->ident) { case SAA7111: case SAA7111A: saa711x_writeregs(sd, saa7111_init); break; case GM7113C: + saa711x_writeregs(sd, gm7113c_init); + break; case SAA7113: - saa711x_writeregs(sd, saa7113_init); + if (pdata && pdata->saa7113_force_gm7113c_init) + saa711x_writeregs(sd, gm7113c_init); + else + saa711x_writeregs(sd, saa7113_init); break; default: state->crystal_freq = SAA7115_FREQ_32_11_MHZ; @@ -1771,6 +1877,10 @@ static int saa711x_probe(struct i2c_client *client, } if (state->ident > SAA7111A && state->ident != GM7113C) saa711x_writeregs(sd, saa7115_init_misc); + + if (pdata) + saa711x_write_platform_data(state, pdata); + saa711x_set_v4lstd(sd, V4L2_STD_NTSC); v4l2_ctrl_handler_setup(hdl); diff --git a/drivers/media/i2c/saa711x_regs.h b/drivers/media/i2c/saa711x_regs.h index 70c56d1..730ca90 100644 --- a/drivers/media/i2c/saa711x_regs.h +++ b/drivers/media/i2c/saa711x_regs.h @@ -202,9 +202,24 @@ #define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff /* SAA7113 bit-masks */ +#define SAA7113_R_08_HTC_OFFSET 3 +#define SAA7113_R_08_HTC_MASK (0x3 << SAA7113_R_08_HTC_OFFSET) #define SAA7113_R_08_FSEL 0x40 #define SAA7113_R_08_AUFD 0x80 +#define SAA7113_R_10_VRLN_OFFSET 3 +#define SAA7113_R_10_VRLN_MASK (0x1 << SAA7113_R_10_VRLN_OFFSET) +#define SAA7113_R_10_OFTS_OFFSET 6 +#define SAA7113_R_10_OFTS_MASK (0x3 << SAA7113_R_10_OFTS_OFFSET) + +#define SAA7113_R_12_RTS0_OFFSET 0 +#define SAA7113_R_12_RTS0_MASK (0xf << SAA7113_R_12_RTS0_OFFSET) +#define SAA7113_R_12_RTS1_OFFSET 4 +#define SAA7113_R_12_RTS1_MASK (0xf << SAA7113_R_12_RTS1_OFFSET) + +#define SAA7113_R_13_ADLSB_OFFSET 7 +#define SAA7113_R_13_ADLSB_MASK (0x1 << SAA7113_R_13_ADLSB_OFFSET) + #if 0 /* Those structs will be used in the future for debug purposes */ struct saa711x_reg_descr { diff --git a/include/media/saa7115.h b/include/media/saa7115.h index 4079186..e8d512a 100644 --- a/include/media/saa7115.h +++ b/include/media/saa7115.h @@ -64,5 +64,69 @@ #define SAA7115_FREQ_FL_APLL (1 << 2) /* SA 3A[3], APLL, SAA7114/5 only */ #define SAA7115_FREQ_FL_DOUBLE_ASCLK (1 << 3) /* SA 39, LRDIV, SAA7114/5 only */ +/* ===== SAA7113 Config enums ===== */ + +/* Register 0x08 "Horizontal time constant" [Bit 3..4]: + * Should be set to "Fast Locking Mode" according to the datasheet, + * and that is the default setting in the gm7113c_init table. + * saa7113_init sets this value to "VTR Mode". */ +enum saa7113_r08_htc { + SAA7113_HTC_TV_MODE = 0x00, + SAA7113_HTC_VTR_MODE, /* Default for saa7113_init */ + SAA7113_HTC_FAST_LOCKING_MODE = 0x03 /* Default for gm7113c_init */ +}; + +/* Register 0x10 "Output format selection" [Bit 6..7]: + * Defaults to ITU_656 as specified in datasheet. */ +enum saa7113_r10_ofts { + SAA7113_OFTS_ITU_656 = 0x0, /* Default */ + SAA7113_OFTS_VFLAG_BY_VREF, + SAA7113_OFTS_VFLAG_BY_DATA_TYPE +}; + +/* Register 0x12 "Output control" [Bit 0..3 Or Bit 4..7]: + * This is used to select what data is output on the RTS0 and RTS1 pins. + * RTS1 [Bit 4..7] Defaults to DOT_IN. (This value can not be set for RTS0) + * RTS0 [Bit 0..3] Defaults to VIPB in gm7113c_init as specified + * in the datasheet, but is set to HREF_HS in the saa7113_init table. */ +enum saa7113_r12_rts { + SAA7113_RTS_DOT_IN = 0, /* OBS: Only for RTS1 (Default RTS1) */ + SAA7113_RTS_VIPB, /* Default RTS0 For gm7113c_init */ + SAA7113_RTS_GPSW, + SAA7115_RTS_HL, + SAA7113_RTS_VL, + SAA7113_RTS_DL, + SAA7113_RTS_PLIN, + SAA7113_RTS_HREF_HS, /* Default RTS0 For saa7113_init */ + SAA7113_RTS_HS, + SAA7113_RTS_HQ, + SAA7113_RTS_ODD, + SAA7113_RTS_VS, + SAA7113_RTS_V123, + SAA7113_RTS_VGATE, + SAA7113_RTS_VREF, + SAA7113_RTS_FID +}; + +struct saa7115_platform_data { + /* saa7113 only: Force the use of the gm7113c_init table, + * instead of the old saa7113_init table. */ + bool saa7113_force_gm7113c_init; + + /* SAA7113/GM7113C Specific configurations */ + enum saa7113_r08_htc *saa7113_r08_htc; /* [R_08 - Bit 3..4] */ + + bool *saa7113_r10_vrln; /* [R_10 - Bit 3] + Disabled for gm7113c_init + Enabled for saa7113c_init */ + enum saa7113_r10_ofts *saa7113_r10_ofts; /* [R_10 - Bit 6..7] */ + + enum saa7113_r12_rts *saa7113_r12_rts0; /* [R_12 - Bit 0..3] */ + enum saa7113_r12_rts *saa7113_r12_rts1; /* [R_12 - Bit 4..7] */ + + bool *saa7113_r13_adlsb; /* [R_13 - Bit 7] + Default disabled */ +}; + #endif -- cgit v0.10.2 From 04074f1fdfe9eefc51bded7f45fafd8cc5d3779c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 18 Aug 2013 08:35:36 -0300 Subject: [media] saa7115: make multi-line comments compliant with CodingStyle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit changeset 2ccf12a did a crappy job when added multi-line comment lines, violating CodingStyle. Change the comments added there to fulfill CodingStyle, and document the platform_data using Documentation/kernel-doc-nano-HOWTO.txt. Cc: Jon Arne Jørgensen Cc: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 5cc48f7..637d026 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -225,12 +225,14 @@ static const unsigned char saa7111_init[] = { 0x00, 0x00 }; -/* This table has one illegal value, and some values that are not - correct according to the datasheet initialization table. - - If you need a table with legal/default values tell the driver in - i2c_board_info.platform_data, and you will get the gm7113c_init - table instead. */ +/* + * This table has one illegal value, and some values that are not + * correct according to the datasheet initialization table. + * + * If you need a table with legal/default values tell the driver in + * i2c_board_info.platform_data, and you will get the gm7113c_init + * table instead. + */ /* SAA7113 Init codes */ static const unsigned char saa7113_init[] = { @@ -265,10 +267,12 @@ static const unsigned char saa7113_init[] = { 0x00, 0x00 }; -/* GM7113C is a clone of the SAA7113 chip - This init table is copied out of the saa7113 datasheet. - In R_08 we enable "Automatic Field Detection" [AUFD], - this is disabled when saa711x_set_v4lstd is called. */ +/* + * GM7113C is a clone of the SAA7113 chip + * This init table is copied out of the saa7113 datasheet. + * In R_08 we enable "Automatic Field Detection" [AUFD], + * this is disabled when saa711x_set_v4lstd is called. + */ static const unsigned char gm7113c_init[] = { R_01_INC_DELAY, 0x08, R_02_INPUT_CNTL_1, 0xc0, diff --git a/include/media/saa7115.h b/include/media/saa7115.h index e8d512a..76911e7 100644 --- a/include/media/saa7115.h +++ b/include/media/saa7115.h @@ -47,9 +47,11 @@ #define SAA7111_FMT_YUV411 0xc0 /* config flags */ -/* Register 0x85 should set bit 0 to 0 (it's 1 by default). This bit +/* + * Register 0x85 should set bit 0 to 0 (it's 1 by default). This bit * controls the IDQ signal polarity which is set to 'inverted' if the bit - * it 1 and to 'default' if it is 0. */ + * it 1 and to 'default' if it is 0. + */ #define SAA7115_IDQ_IS_DEFAULT (1 << 0) /* s_crystal_freq values and flags */ @@ -84,11 +86,13 @@ enum saa7113_r10_ofts { SAA7113_OFTS_VFLAG_BY_DATA_TYPE }; -/* Register 0x12 "Output control" [Bit 0..3 Or Bit 4..7]: +/* + * Register 0x12 "Output control" [Bit 0..3 Or Bit 4..7]: * This is used to select what data is output on the RTS0 and RTS1 pins. * RTS1 [Bit 4..7] Defaults to DOT_IN. (This value can not be set for RTS0) * RTS0 [Bit 0..3] Defaults to VIPB in gm7113c_init as specified - * in the datasheet, but is set to HREF_HS in the saa7113_init table. */ + * in the datasheet, but is set to HREF_HS in the saa7113_init table. + */ enum saa7113_r12_rts { SAA7113_RTS_DOT_IN = 0, /* OBS: Only for RTS1 (Default RTS1) */ SAA7113_RTS_VIPB, /* Default RTS0 For gm7113c_init */ @@ -108,24 +112,29 @@ enum saa7113_r12_rts { SAA7113_RTS_FID }; +/** + * struct saa7115_platform_data - Allow overriding default initialization + * + * @saa7113_force_gm7113c_init: Force the use of the gm7113c_init table + * instead of saa7113_init table + * (saa7113 only) + * @saa7113_r08_htc: [R_08 - Bit 3..4] + * @saa7113_r10_vrln: [R_10 - Bit 3] + * default: Disabled for gm7113c_init + * Enabled for saa7113c_init + * @saa7113_r10_ofts: [R_10 - Bit 6..7] + * @saa7113_r12_rts0: [R_12 - Bit 0..3] + * @saa7113_r12_rts1: [R_12 - Bit 4..7] + * @saa7113_r13_adlsb: [R_13 - Bit 7] - default: disabled + */ struct saa7115_platform_data { - /* saa7113 only: Force the use of the gm7113c_init table, - * instead of the old saa7113_init table. */ bool saa7113_force_gm7113c_init; - - /* SAA7113/GM7113C Specific configurations */ - enum saa7113_r08_htc *saa7113_r08_htc; /* [R_08 - Bit 3..4] */ - - bool *saa7113_r10_vrln; /* [R_10 - Bit 3] - Disabled for gm7113c_init - Enabled for saa7113c_init */ - enum saa7113_r10_ofts *saa7113_r10_ofts; /* [R_10 - Bit 6..7] */ - - enum saa7113_r12_rts *saa7113_r12_rts0; /* [R_12 - Bit 0..3] */ - enum saa7113_r12_rts *saa7113_r12_rts1; /* [R_12 - Bit 4..7] */ - - bool *saa7113_r13_adlsb; /* [R_13 - Bit 7] - Default disabled */ + enum saa7113_r08_htc *saa7113_r08_htc; + bool *saa7113_r10_vrln; + enum saa7113_r10_ofts *saa7113_r10_ofts; + enum saa7113_r12_rts *saa7113_r12_rts0; + enum saa7113_r12_rts *saa7113_r12_rts1; + bool *saa7113_r13_adlsb; }; #endif -- cgit v0.10.2 From 8023ed09cb278004a28f49844602adf23bd3615e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 10 Jul 2012 10:41:40 -0300 Subject: [media] videobuf2-core: Verify planes lengths for output buffers For output buffers application provide to the kernel the number of bytes they stored in each plane of the buffer. Verify that the value is smaller than or equal to the plane length. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Pawel Osciak 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 c9b50c7..35a5b8f 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -334,6 +334,41 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer } /** + * __verify_length() - Verify that the bytesused value for each plane fits in + * the plane length and that the data offset doesn't exceed the bytesused value. + */ +static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) +{ + unsigned int length; + unsigned int plane; + + if (!V4L2_TYPE_IS_OUTPUT(b->type)) + return 0; + + if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { + for (plane = 0; plane < vb->num_planes; ++plane) { + length = (b->memory == V4L2_MEMORY_USERPTR) + ? b->m.planes[plane].length + : vb->v4l2_planes[plane].length; + + if (b->m.planes[plane].bytesused > length) + return -EINVAL; + if (b->m.planes[plane].data_offset >= + b->m.planes[plane].bytesused) + return -EINVAL; + } + } else { + length = (b->memory == V4L2_MEMORY_USERPTR) + ? b->length : vb->v4l2_planes[0].length; + + if (b->bytesused > length) + return -EINVAL; + } + + return 0; +} + +/** * __buffer_in_use() - return true if the buffer is in use and * the queue cannot be freed (by the means of REQBUFS(0)) call */ @@ -1167,6 +1202,10 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) struct vb2_queue *q = vb->vb2_queue; int ret; + ret = __verify_length(vb, b); + if (ret < 0) + return ret; + switch (q->memory) { case V4L2_MEMORY_MMAP: ret = __qbuf_mmap(vb, b); -- cgit v0.10.2 From 91f9241fd3487cd959abcf8c210af620ebfeb2cd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 2 Jul 2013 23:04:25 -0300 Subject: [media] v4l: of: Use of_get_child_by_name() Replace a manual loop through child nodes with a call to of_get_child_by_name(). Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index aa59639..f64d953 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -173,12 +173,8 @@ struct device_node *v4l2_of_get_next_endpoint(const struct device_node *parent, if (node) parent = node; - for_each_child_of_node(parent, node) { - if (!of_node_cmp(node->name, "port")) { - port = node; - break; - } - } + port = of_get_child_by_name(parent, "port"); + if (port) { /* Found a port, get an endpoint. */ endpoint = of_get_next_child(port, NULL); -- cgit v0.10.2 From 5045b495196a624c17a1ef271cbdaa6d3e2d1097 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 2 Jul 2013 23:05:51 -0300 Subject: [media] v4l: of: Drop acquired reference to node when getting next endpoint The of_get_child_by_name() function takes a reference to the node it returns. Make sure to drop it when looking for the ports node in v4l2_of_get_next_endpoint(). Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index f64d953..ed305d8 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -186,6 +186,7 @@ struct device_node *v4l2_of_get_next_endpoint(const struct device_node *parent, if (!endpoint) pr_err("%s(): no endpoint nodes specified for %s\n", __func__, parent->full_name); + of_node_put(node); } else { port = of_get_parent(prev); if (!port) -- cgit v0.10.2 From 3f359a7cb1847900bc87b3498f72c1ee848942a7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 18 Jun 2013 21:41:59 -0300 Subject: [media] v4l: Fix colorspace conversion error in sample code The sample code erroneously scales the y1, pb and pr variables from the [0.0 .. 1.0] and [-0.5 .. 0.5] ranges to [0 .. 255] and [-128 .. 127]. Fix it. Signed-off-by: Laurent Pinchart Acked-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 16db350..72d72bd 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -391,9 +391,9 @@ clamp (double x) else return r; } -y1 = (255 / 219.0) * (Y1 - 16); -pb = (255 / 224.0) * (Cb - 128); -pr = (255 / 224.0) * (Cr - 128); +y1 = (Y1 - 16) / 219.0; +pb = (Cb - 128) / 224.0; +pr = (Cr - 128) / 224.0; r = 1.0 * y1 + 0 * pb + 1.402 * pr; g = 1.0 * y1 - 0.344 * pb - 0.714 * pr; -- cgit v0.10.2 From 8e3fbfee23fffa2f50a36ef7b7d18092d0f57f6c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 3 Jul 2013 07:49:06 -0300 Subject: [media] v4l: async: Make it safe to unregister unregistered notifier Calling v4l2_async_notifier_unregister() on a notifier that hasn't been registered leads to a crash. To simplify drivers, make it safe to unregister a notifier that has not been registered. Signed-off-by: Laurent Pinchart Tested-by: Sylwester Nawrocki Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index b350ab9..10bb62c 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -192,6 +192,9 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) struct device *dev[n_subdev]; int i = 0; + if (!notifier->v4l2_dev) + return; + mutex_lock(&list_lock); list_del(¬ifier->list); @@ -225,6 +228,9 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) } put_device(d); } + + notifier->v4l2_dev = NULL; + /* * Don't care about the waiting list, it is initialised and populated * upon notifier registration. -- cgit v0.10.2 From 73135e969970304a474c18c9f732fa3e36d88514 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 25 Jul 2013 17:23:10 -0300 Subject: [media] V4L2: soc_camera: Renesas R-Car VIN driver Add Renesas R-Car VIN (Video In) V4L2 driver. Based on the patch by Phil Edworthy . [Sergei: removed deprecated IRQF_DISABLED flag, reordered/renamed 'enum chip_id' values, reordered rcar_vin_id_table[] entries, removed senseless parens from to_buf_list() macro, used ALIGN() macro in rcar_vin_setup(), added {} to the *if* statement and used 'bool' values instead of 0/1 where necessary, removed unused macros, done some reformatting and clarified some comments.] Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov 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 626dccc..af39c46 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -44,6 +44,14 @@ config VIDEO_PXA27x ---help--- This is a v4l2 driver for the PXA27x Quick Capture Interface +config VIDEO_RCAR_VIN + tristate "R-Car Video Input (VIN) support" + depends on VIDEO_DEV && SOC_CAMERA + select VIDEOBUF2_DMA_CONTIG + select SOC_CAMERA_SCALE_CROP + ---help--- + This is a v4l2 driver for the R-Car VIN Interface + config VIDEO_SH_MOBILE_CSI2 tristate "SuperH Mobile MIPI CSI-2 Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 3918622..8aed26d 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o +obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar_vin.o diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c new file mode 100644 index 0000000..d02a7e0 --- /dev/null +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -0,0 +1,1486 @@ +/* + * SoC-camera host driver for Renesas R-Car VIN unit + * + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., + * + * Based on V4L2 Driver for SuperH Mobile CEU interface "sh_mobile_ceu_camera.c" + * + * Copyright (C) 2008 Magnus Damm + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "soc_scale_crop.h" + +#define DRV_NAME "rcar_vin" + +/* Register offsets for R-Car VIN */ +#define VNMC_REG 0x00 /* Video n Main Control Register */ +#define VNMS_REG 0x04 /* Video n Module Status Register */ +#define VNFC_REG 0x08 /* Video n Frame Capture Register */ +#define VNSLPRC_REG 0x0C /* Video n Start Line Pre-Clip Register */ +#define VNELPRC_REG 0x10 /* Video n End Line Pre-Clip Register */ +#define VNSPPRC_REG 0x14 /* Video n Start Pixel Pre-Clip Register */ +#define VNEPPRC_REG 0x18 /* Video n End Pixel Pre-Clip Register */ +#define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */ +#define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */ +#define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */ +#define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */ +#define VNIS_REG 0x2C /* Video n Image Stride Register */ +#define VNMB_REG(m) (0x30 + ((m) << 2)) /* Video n Memory Base m Register */ +#define VNIE_REG 0x40 /* Video n Interrupt Enable Register */ +#define VNINTS_REG 0x44 /* Video n Interrupt Status Register */ +#define VNSI_REG 0x48 /* Video n Scanline Interrupt Register */ +#define VNMTC_REG 0x4C /* Video n Memory Transfer Control Register */ +#define VNYS_REG 0x50 /* Video n Y Scale Register */ +#define VNXS_REG 0x54 /* Video n X Scale Register */ +#define VNDMR_REG 0x58 /* Video n Data Mode Register */ +#define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */ +#define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */ + +/* Register bit fields for R-Car VIN */ +/* Video n Main Control Register bits */ +#define VNMC_FOC (1 << 21) +#define VNMC_YCAL (1 << 19) +#define VNMC_INF_YUV8_BT656 (0 << 16) +#define VNMC_INF_YUV8_BT601 (1 << 16) +#define VNMC_INF_YUV16 (5 << 16) +#define VNMC_VUP (1 << 10) +#define VNMC_IM_ODD (0 << 3) +#define VNMC_IM_ODD_EVEN (1 << 3) +#define VNMC_IM_EVEN (2 << 3) +#define VNMC_IM_FULL (3 << 3) +#define VNMC_BPS (1 << 1) +#define VNMC_ME (1 << 0) + +/* Video n Module Status Register bits */ +#define VNMS_FBS_MASK (3 << 3) +#define VNMS_FBS_SHIFT 3 +#define VNMS_AV (1 << 1) +#define VNMS_CA (1 << 0) + +/* Video n Frame Capture Register bits */ +#define VNFC_C_FRAME (1 << 1) +#define VNFC_S_FRAME (1 << 0) + +/* Video n Interrupt Enable Register bits */ +#define VNIE_FIE (1 << 4) +#define VNIE_EFE (1 << 1) + +/* Video n Data Mode Register bits */ +#define VNDMR_EXRGB (1 << 8) +#define VNDMR_BPSM (1 << 4) +#define VNDMR_DTMD_YCSEP (1 << 1) +#define VNDMR_DTMD_ARGB1555 (1 << 0) + +/* Video n Data Mode Register 2 bits */ +#define VNDMR2_VPS (1 << 30) +#define VNDMR2_HPS (1 << 29) +#define VNDMR2_FTEV (1 << 17) + +#define VIN_MAX_WIDTH 2048 +#define VIN_MAX_HEIGHT 2048 + +enum chip_id { + RCAR_H1, + RCAR_M1, + RCAR_E1, +}; + +enum rcar_vin_state { + STOPPED = 0, + RUNNING, + STOPPING, +}; + +struct rcar_vin_priv { + void __iomem *base; + spinlock_t lock; + 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 vb_count; + unsigned int nr_hw_slots; + bool request_to_stop; + struct completion capture_stop; + enum chip_id chip; +}; + +#define is_continuous_transfer(priv) (priv->vb_count > MAX_BUFFER_NUM) + +struct rcar_vin_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \ + struct rcar_vin_buffer, \ + vb)->list) + +struct rcar_vin_cam { + /* VIN offsets within the camera output, before the VIN scaler */ + unsigned int vin_left; + unsigned int vin_top; + /* Client output, as seen by the VIN */ + unsigned int width; + unsigned int height; + /* + * User window from S_CROP / G_CROP, produced by client cropping and + * scaling, VIN scaling and VIN cropping, mapped back onto the client + * input window + */ + struct v4l2_rect subrect; + /* Camera cropping rectangle */ + struct v4l2_rect rect; + const struct soc_mbus_pixelfmt *extra_fmt; +}; + +/* + * .queue_setup() is called to check whether the driver can accept the requested + * number of buffers and to fill in plane sizes for the current frame format if + * required + */ +static int rcar_vin_videobuf_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *count, + unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(vq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + + if (fmt) { + const struct soc_camera_format_xlate *xlate; + unsigned int bytes_per_line; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, + fmt->fmt.pix.pixelformat); + if (!xlate) + return -EINVAL; + ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width, + xlate->host_fmt); + if (ret < 0) + return ret; + + bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret); + + ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line, + fmt->fmt.pix.height); + if (ret < 0) + return ret; + + sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret); + } else { + /* Called from VIDIOC_REQBUFS or in compatibility mode */ + sizes[0] = icd->sizeimage; + } + + alloc_ctxs[0] = priv->alloc_ctx; + + if (!vq->num_buffers) + priv->sequence = 0; + + if (!*count) + *count = 2; + priv->vb_count = *count; + + *num_planes = 1; + + /* Number of hardware slots */ + if (is_continuous_transfer(priv)) + priv->nr_hw_slots = MAX_BUFFER_NUM; + else + priv->nr_hw_slots = 1; + + dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]); + + return 0; +} + +static int rcar_vin_setup(struct rcar_vin_priv *priv) +{ + struct soc_camera_device *icd = priv->ici.icd; + struct rcar_vin_cam *cam = icd->host_priv; + u32 vnmc, dmr, interrupts; + bool progressive = false, output_is_yuv = false; + + switch (priv->field) { + case V4L2_FIELD_TOP: + vnmc = VNMC_IM_ODD; + break; + case V4L2_FIELD_BOTTOM: + vnmc = VNMC_IM_EVEN; + break; + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + vnmc = VNMC_IM_FULL; + break; + case V4L2_FIELD_INTERLACED_BT: + vnmc = VNMC_IM_FULL | VNMC_FOC; + break; + case V4L2_FIELD_NONE: + if (is_continuous_transfer(priv)) { + vnmc = VNMC_IM_ODD_EVEN; + progressive = true; + } else { + vnmc = VNMC_IM_ODD; + } + break; + default: + vnmc = VNMC_IM_ODD; + break; + } + + /* input interface */ + switch (icd->current_fmt->code) { + case V4L2_MBUS_FMT_YUYV8_1X16: + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ + vnmc |= priv->pdata->flags & RCAR_VIN_BT656 ? + VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + default: + break; + } + + /* output format */ + switch (icd->current_fmt->host_fmt->fourcc) { + case V4L2_PIX_FMT_NV16: + iowrite32(ALIGN(cam->width * cam->height, 0x80), + priv->base + VNUVAOF_REG); + dmr = VNDMR_DTMD_YCSEP; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_YUYV: + dmr = VNDMR_BPSM; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_UYVY: + dmr = 0; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_RGB555X: + dmr = VNDMR_DTMD_ARGB1555; + break; + case V4L2_PIX_FMT_RGB565: + dmr = 0; + break; + case V4L2_PIX_FMT_RGB32: + if (priv->chip == RCAR_H1 || priv->chip == RCAR_E1) { + dmr = VNDMR_EXRGB; + break; + } + default: + dev_warn(icd->parent, "Invalid fourcc format (0x%x)\n", + icd->current_fmt->host_fmt->fourcc); + return -EINVAL; + } + + /* Always update on field change */ + vnmc |= VNMC_VUP; + + /* If input and output use the same colorspace, use bypass mode */ + if (output_is_yuv) + vnmc |= VNMC_BPS; + + /* progressive or interlaced mode */ + interrupts = progressive ? VNIE_FIE | VNIE_EFE : VNIE_EFE; + + /* ack interrupts */ + iowrite32(interrupts, priv->base + VNINTS_REG); + /* enable interrupts */ + iowrite32(interrupts, priv->base + VNIE_REG); + /* start capturing */ + iowrite32(dmr, priv->base + VNDMR_REG); + iowrite32(vnmc | VNMC_ME, priv->base + VNMC_REG); + + return 0; +} + +static void rcar_vin_capture(struct rcar_vin_priv *priv) +{ + if (is_continuous_transfer(priv)) + /* Continuous Frame Capture Mode */ + iowrite32(VNFC_C_FRAME, priv->base + VNFC_REG); + else + /* Single Frame Capture Mode */ + iowrite32(VNFC_S_FRAME, priv->base + VNFC_REG); +} + +static void rcar_vin_request_capture_stop(struct rcar_vin_priv *priv) +{ + priv->state = STOPPING; + + /* set continuous & single transfer off */ + iowrite32(0, priv->base + VNFC_REG); + /* disable capture (release DMA buffer), reset */ + iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME, + priv->base + VNMC_REG); + + /* update the status if stopped already */ + if (!(ioread32(priv->base + VNMS_REG) & VNMS_CA)) + priv->state = STOPPED; +} + +static int rcar_vin_get_free_hw_slot(struct rcar_vin_priv *priv) +{ + int slot; + + for (slot = 0; slot < priv->nr_hw_slots; slot++) + if (priv->queue_buf[slot] == NULL) + return slot; + + return -1; +} + +static int rcar_vin_hw_ready(struct rcar_vin_priv *priv) +{ + /* Ensure all HW slots are filled */ + return rcar_vin_get_free_hw_slot(priv) < 0 ? 1 : 0; +} + +/* Moves a buffer from the queue to the HW slots */ +static int rcar_vin_fill_hw_slot(struct rcar_vin_priv *priv) +{ + struct vb2_buffer *vb; + dma_addr_t phys_addr_top; + int slot; + + if (list_empty(&priv->capture)) + return 0; + + /* Find a free HW slot */ + slot = rcar_vin_get_free_hw_slot(priv); + if (slot < 0) + return 0; + + vb = &list_entry(priv->capture.next, struct rcar_vin_buffer, list)->vb; + list_del_init(to_buf_list(vb)); + priv->queue_buf[slot] = vb; + phys_addr_top = vb2_dma_contig_plane_dma_addr(vb, 0); + iowrite32(phys_addr_top, priv->base + VNMB_REG(slot)); + + return 1; +} + +static void rcar_vin_videobuf_queue(struct vb2_buffer *vb) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + unsigned long size; + + size = icd->sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n", + vb->v4l2_buf.index, vb2_plane_size(vb, 0), size); + goto error; + } + + vb2_set_plane_payload(vb, 0, size); + + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); + + spin_lock_irq(&priv->lock); + + list_add_tail(to_buf_list(vb), &priv->capture); + rcar_vin_fill_hw_slot(priv); + + /* If we weren't running, and have enough buffers, start capturing! */ + if (priv->state != RUNNING && rcar_vin_hw_ready(priv)) { + if (rcar_vin_setup(priv)) { + /* Submit error */ + list_del_init(to_buf_list(vb)); + spin_unlock_irq(&priv->lock); + goto error; + } + priv->request_to_stop = false; + init_completion(&priv->capture_stop); + priv->state = RUNNING; + rcar_vin_capture(priv); + } + + spin_unlock_irq(&priv->lock); + + return; + +error: + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); +} + +static void rcar_vin_videobuf_release(struct vb2_buffer *vb) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + unsigned int i; + int buf_in_use = 0; + + spin_lock_irq(&priv->lock); + + /* Is the buffer in use by the VIN hardware? */ + for (i = 0; i < MAX_BUFFER_NUM; i++) { + if (priv->queue_buf[i] == vb) { + buf_in_use = 1; + break; + } + } + + if (buf_in_use) { + while (priv->state != STOPPED) { + + /* issue stop if running */ + if (priv->state == RUNNING) + rcar_vin_request_capture_stop(priv); + + /* wait until capturing has been stopped */ + if (priv->state == STOPPING) { + priv->request_to_stop = true; + spin_unlock_irq(&priv->lock); + wait_for_completion(&priv->capture_stop); + spin_lock_irq(&priv->lock); + } + } + /* + * Capturing has now stopped. The buffer we have been asked + * to release could be any of the current buffers in use, so + * release all buffers that are in use by HW + */ + for (i = 0; i < MAX_BUFFER_NUM; i++) { + if (priv->queue_buf[i]) { + vb2_buffer_done(priv->queue_buf[i], + VB2_BUF_STATE_ERROR); + priv->queue_buf[i] = NULL; + } + } + } else { + list_del_init(to_buf_list(vb)); + } + + spin_unlock_irq(&priv->lock); +} + +static int rcar_vin_videobuf_init(struct vb2_buffer *vb) +{ + INIT_LIST_HEAD(to_buf_list(vb)); + return 0; +} + +static int rcar_vin_stop_streaming(struct vb2_queue *vq) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(vq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct list_head *buf_head, *tmp; + + spin_lock_irq(&priv->lock); + list_for_each_safe(buf_head, tmp, &priv->capture) + list_del_init(buf_head); + spin_unlock_irq(&priv->lock); + + return 0; +} + +static struct vb2_ops rcar_vin_vb2_ops = { + .queue_setup = rcar_vin_videobuf_setup, + .buf_init = rcar_vin_videobuf_init, + .buf_cleanup = rcar_vin_videobuf_release, + .buf_queue = rcar_vin_videobuf_queue, + .stop_streaming = rcar_vin_stop_streaming, + .wait_prepare = soc_camera_unlock, + .wait_finish = soc_camera_lock, +}; + +static irqreturn_t rcar_vin_irq(int irq, void *data) +{ + struct rcar_vin_priv *priv = data; + u32 int_status; + bool can_run = false, hw_stopped; + int slot; + unsigned int handled = 0; + + spin_lock(&priv->lock); + + int_status = ioread32(priv->base + VNINTS_REG); + if (!int_status) + goto done; + /* ack interrupts */ + iowrite32(int_status, priv->base + VNINTS_REG); + handled = 1; + + /* nothing to do if capture status is 'STOPPED' */ + if (priv->state == STOPPED) + goto done; + + hw_stopped = !(ioread32(priv->base + VNMS_REG) & VNMS_CA); + + if (!priv->request_to_stop) { + if (is_continuous_transfer(priv)) + slot = (ioread32(priv->base + VNMS_REG) & + VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; + else + slot = 0; + + priv->queue_buf[slot]->v4l2_buf.field = priv->field; + priv->queue_buf[slot]->v4l2_buf.sequence = priv->sequence++; + do_gettimeofday(&priv->queue_buf[slot]->v4l2_buf.timestamp); + vb2_buffer_done(priv->queue_buf[slot], VB2_BUF_STATE_DONE); + priv->queue_buf[slot] = NULL; + + if (priv->state != STOPPING) + can_run = rcar_vin_fill_hw_slot(priv); + + if (hw_stopped || !can_run) { + priv->state = STOPPED; + } else if (is_continuous_transfer(priv) && + list_empty(&priv->capture) && + priv->state == RUNNING) { + /* + * The continuous capturing requires an explicit stop + * operation when there is no buffer to be set into + * the VnMBm registers. + */ + rcar_vin_request_capture_stop(priv); + } else { + rcar_vin_capture(priv); + } + + } else if (hw_stopped) { + priv->state = STOPPED; + priv->request_to_stop = false; + complete(&priv->capture_stop); + } + +done: + spin_unlock(&priv->lock); + + return IRQ_RETVAL(handled); +} + +static int rcar_vin_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + int i; + + for (i = 0; i < MAX_BUFFER_NUM; i++) + priv->queue_buf[i] = NULL; + + pm_runtime_get_sync(ici->v4l2_dev.dev); + + dev_dbg(icd->parent, "R-Car VIN driver attached to camera %d\n", + icd->devnum); + + return 0; +} + +static void rcar_vin_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct vb2_buffer *vb; + int i; + + /* disable capture, disable interrupts */ + iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME, + priv->base + VNMC_REG); + iowrite32(0, priv->base + VNIE_REG); + + priv->state = STOPPED; + priv->request_to_stop = false; + + /* make sure active buffer is cancelled */ + spin_lock_irq(&priv->lock); + for (i = 0; i < MAX_BUFFER_NUM; i++) { + vb = priv->queue_buf[i]; + if (vb) { + list_del_init(to_buf_list(vb)); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + } + } + spin_unlock_irq(&priv->lock); + + pm_runtime_put(ici->v4l2_dev.dev); + + dev_dbg(icd->parent, "R-Car VIN driver detached from camera %d\n", + icd->devnum); +} + +/* Called with .host_lock held */ +static int rcar_vin_clock_start(struct soc_camera_host *ici) +{ + /* VIN does not have "mclk" */ + return 0; +} + +/* Called with .host_lock held */ +static void rcar_vin_clock_stop(struct soc_camera_host *ici) +{ + /* VIN does not have "mclk" */ +} + +/* rect is guaranteed to not exceed the scaled camera rectangle */ +static int rcar_vin_set_rect(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_cam *cam = icd->host_priv; + struct rcar_vin_priv *priv = ici->priv; + unsigned int left_offset, top_offset; + unsigned char dsize = 0; + struct v4l2_rect *cam_subrect = &cam->subrect; + + dev_dbg(icd->parent, "Crop %ux%u@%u:%u\n", + icd->user_width, icd->user_height, cam->vin_left, cam->vin_top); + + left_offset = cam->vin_left; + top_offset = cam->vin_top; + + if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_RGB32 && + priv->chip == RCAR_E1) + dsize = 1; + + dev_dbg(icd->parent, "Cam %ux%u@%u:%u\n", + cam->width, cam->height, cam->vin_left, cam->vin_top); + dev_dbg(icd->parent, "Cam subrect %ux%u@%u:%u\n", + cam_subrect->width, cam_subrect->height, + cam_subrect->left, cam_subrect->top); + + /* Set Start/End Pixel/Line Pre-Clip */ + iowrite32(left_offset << dsize, priv->base + VNSPPRC_REG); + iowrite32((left_offset + cam->width - 1) << dsize, + priv->base + VNEPPRC_REG); + switch (priv->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + iowrite32(top_offset / 2, priv->base + VNSLPRC_REG); + iowrite32((top_offset + cam->height) / 2 - 1, + priv->base + VNELPRC_REG); + break; + default: + iowrite32(top_offset, priv->base + VNSLPRC_REG); + iowrite32(top_offset + cam->height - 1, + priv->base + VNELPRC_REG); + break; + } + + /* Set Start/End Pixel/Line Post-Clip */ + iowrite32(0, priv->base + VNSPPOC_REG); + iowrite32(0, priv->base + VNSLPOC_REG); + iowrite32((cam_subrect->width - 1) << dsize, priv->base + VNEPPOC_REG); + switch (priv->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + iowrite32(cam_subrect->height / 2 - 1, + priv->base + VNELPOC_REG); + break; + default: + iowrite32(cam_subrect->height - 1, priv->base + VNELPOC_REG); + break; + } + + iowrite32(ALIGN(cam->width, 0x10), priv->base + VNIS_REG); + + return 0; +} + +static void capture_stop_preserve(struct rcar_vin_priv *priv, u32 *vnmc) +{ + *vnmc = ioread32(priv->base + VNMC_REG); + /* module disable */ + iowrite32(*vnmc & ~VNMC_ME, priv->base + VNMC_REG); +} + +static void capture_restore(struct rcar_vin_priv *priv, u32 vnmc) +{ + unsigned long timeout = jiffies + 10 * HZ; + + /* + * Wait until the end of the current frame. It can take a long time, + * but if it has been aborted by a MRST1 reset, it should exit sooner. + */ + while ((ioread32(priv->base + VNMS_REG) & VNMS_AV) && + time_before(jiffies, timeout)) + msleep(1); + + if (time_after(jiffies, timeout)) { + dev_err(priv->ici.v4l2_dev.dev, + "Timeout waiting for frame end! Interface problem?\n"); + return; + } + + iowrite32(vnmc, priv->base + VNMC_REG); +} + +#define VIN_MBUS_FLAGS (V4L2_MBUS_MASTER | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | \ + V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_HSYNC_ACTIVE_LOW | \ + V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_VSYNC_ACTIVE_LOW | \ + V4L2_MBUS_DATA_ACTIVE_HIGH) + +static int rcar_vin_set_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_mbus_config cfg; + unsigned long common_flags; + u32 vnmc; + u32 val; + int ret; + + capture_stop_preserve(priv, &vnmc); + + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS); + if (!common_flags) { + dev_warn(icd->parent, + "MBUS flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, VIN_MBUS_FLAGS); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } else { + common_flags = VIN_MBUS_FLAGS; + } + + /* 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) + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; + else + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; + } + + if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { + if (priv->pdata->flags & RCAR_VIN_VSYNC_ACTIVE_LOW) + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; + else + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; + } + + cfg.flags = common_flags; + ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + val = priv->field == V4L2_FIELD_NONE ? VNDMR2_FTEV : 0; + if (!(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + val |= VNDMR2_VPS; + if (!(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + val |= VNDMR2_HPS; + iowrite32(val, priv->base + VNDMR2_REG); + + ret = rcar_vin_set_rect(icd); + if (ret < 0) + return ret; + + capture_restore(priv, vnmc); + + return 0; +} + +static int rcar_vin_try_bus_param(struct soc_camera_device *icd, + unsigned char buswidth) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_mbus_config cfg; + int ret; + + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (ret == -ENOIOCTLCMD) + return 0; + else if (ret) + return ret; + + if (buswidth > 24) + return -EINVAL; + + /* check is there common mbus flags */ + ret = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS); + if (ret) + return 0; + + dev_warn(icd->parent, + "MBUS flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, VIN_MBUS_FLAGS); + + return -EINVAL; +} + +static bool rcar_vin_packing_supported(const struct soc_mbus_pixelfmt *fmt) +{ + return fmt->packing == SOC_MBUS_PACKING_NONE || + (fmt->bits_per_sample > 8 && + fmt->packing == SOC_MBUS_PACKING_EXTEND16); +} + +static const struct soc_mbus_pixelfmt rcar_vin_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .name = "NV16", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .name = "RGB565", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, + { + .fourcc = V4L2_PIX_FMT_RGB555X, + .name = "ARGB1555", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, + { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888", + .bits_per_sample = 32, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}; + +static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, + struct soc_camera_format_xlate *xlate) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->parent; + int ret, k, n; + int formats = 0; + struct rcar_vin_cam *cam; + enum v4l2_mbus_pixelcode code; + const struct soc_mbus_pixelfmt *fmt; + + ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); + if (ret < 0) + return 0; + + fmt = soc_mbus_get_fmtdesc(code); + if (!fmt) { + dev_warn(dev, "unsupported format code #%u: %d\n", idx, code); + return 0; + } + + ret = rcar_vin_try_bus_param(icd, fmt->bits_per_sample); + if (ret < 0) + return 0; + + if (!icd->host_priv) { + struct v4l2_mbus_framefmt mf; + struct v4l2_rect rect; + struct device *dev = icd->parent; + int shift; + + ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + if (ret < 0) + return ret; + + /* Cache current client geometry */ + ret = soc_camera_client_g_rect(sd, &rect); + if (ret == -ENOIOCTLCMD) { + /* Sensor driver doesn't support cropping */ + rect.left = 0; + rect.top = 0; + rect.width = mf.width; + rect.height = mf.height; + } else if (ret < 0) { + return ret; + } + + /* + * If sensor proposes too large format then try smaller ones: + * 1280x960, 640x480, 320x240 + */ + for (shift = 0; shift < 3; shift++) { + if (mf.width <= VIN_MAX_WIDTH && + mf.height <= VIN_MAX_HEIGHT) + break; + + mf.width = 1280 >> shift; + mf.height = 960 >> shift; + ret = v4l2_device_call_until_err(sd->v4l2_dev, + soc_camera_grp_id(icd), + video, s_mbus_fmt, + &mf); + if (ret < 0) + return ret; + } + + if (shift == 3) { + dev_err(dev, + "Failed to configure the client below %ux%x\n", + mf.width, mf.height); + return -EIO; + } + + dev_dbg(dev, "camera fmt %ux%u\n", mf.width, mf.height); + + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) + return -ENOMEM; + /* + * We are called with current camera crop, + * initialise subrect with it + */ + cam->rect = rect; + cam->subrect = rect; + cam->width = mf.width; + cam->height = mf.height; + + icd->host_priv = cam; + } else { + cam = icd->host_priv; + } + + /* Beginning of a pass */ + if (!idx) + cam->extra_fmt = NULL; + + switch (code) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV8_2X8: + if (cam->extra_fmt) + break; + + /* Add all our formats that can be generated by VIN */ + cam->extra_fmt = rcar_vin_formats; + + n = ARRAY_SIZE(rcar_vin_formats); + formats += n; + for (k = 0; xlate && k < n; k++, xlate++) { + xlate->host_fmt = &rcar_vin_formats[k]; + xlate->code = code; + dev_dbg(dev, "Providing format %s using code %d\n", + rcar_vin_formats[k].name, code); + } + break; + default: + if (!rcar_vin_packing_supported(fmt)) + return 0; + + dev_dbg(dev, "Providing format %s in pass-through mode\n", + fmt->name); + break; + } + + /* Generic pass-through */ + formats++; + if (xlate) { + xlate->host_fmt = fmt; + xlate->code = code; + xlate++; + } + + return formats; +} + +static void rcar_vin_put_formats(struct soc_camera_device *icd) +{ + kfree(icd->host_priv); + icd->host_priv = NULL; +} + +static int rcar_vin_set_crop(struct soc_camera_device *icd, + const struct v4l2_crop *a) +{ + struct v4l2_crop a_writable = *a; + const struct v4l2_rect *rect = &a_writable.c; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct v4l2_crop cam_crop; + struct rcar_vin_cam *cam = icd->host_priv; + struct v4l2_rect *cam_rect = &cam_crop.c; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->parent; + struct v4l2_mbus_framefmt mf; + u32 vnmc; + int ret, i; + + dev_dbg(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, + rect->left, rect->top); + + /* During camera cropping its output window can change too, stop VIN */ + capture_stop_preserve(priv, &vnmc); + dev_dbg(dev, "VNMC_REG 0x%x\n", vnmc); + + /* Apply iterative camera S_CROP for new input window. */ + ret = soc_camera_client_s_crop(sd, &a_writable, &cam_crop, + &cam->rect, &cam->subrect); + if (ret < 0) + return ret; + + dev_dbg(dev, "camera cropped to %ux%u@%u:%u\n", + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top); + + /* On success cam_crop contains current camera crop */ + + /* Retrieve camera output window */ + ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + if (ret < 0) + return ret; + + if (mf.width > VIN_MAX_WIDTH || mf.height > VIN_MAX_HEIGHT) + return -EINVAL; + + /* Cache camera output window */ + cam->width = mf.width; + cam->height = mf.height; + + icd->user_width = cam->width; + icd->user_height = cam->height; + + cam->vin_left = rect->left & ~1; + cam->vin_top = rect->top & ~1; + + /* Use VIN cropping to crop to the new window. */ + ret = rcar_vin_set_rect(icd); + if (ret < 0) + return ret; + + cam->subrect = *rect; + + dev_dbg(dev, "VIN cropped to %ux%u@%u:%u\n", + icd->user_width, icd->user_height, + cam->vin_left, cam->vin_top); + + /* Restore capture */ + for (i = 0; i < MAX_BUFFER_NUM; i++) { + if (priv->queue_buf[i] && priv->state == STOPPED) { + vnmc |= VNMC_ME; + break; + } + } + capture_restore(priv, vnmc); + + /* Even if only camera cropping succeeded */ + return ret; +} + +static int rcar_vin_get_crop(struct soc_camera_device *icd, + struct v4l2_crop *a) +{ + struct rcar_vin_cam *cam = icd->host_priv; + + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->c = cam->subrect; + + return 0; +} + +/* Similar to set_crop multistage iterative algorithm */ +static int rcar_vin_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct rcar_vin_cam *cam = icd->host_priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_mbus_framefmt mf; + struct device *dev = icd->parent; + __u32 pixfmt = pix->pixelformat; + const struct soc_camera_format_xlate *xlate; + unsigned int vin_sub_width = 0, vin_sub_height = 0; + int ret; + bool can_scale; + enum v4l2_field field; + v4l2_std_id std; + + dev_dbg(dev, "S_FMT(pix=0x%x, %ux%u)\n", + pixfmt, pix->width, pix->height); + + switch (pix->field) { + default: + pix->field = V4L2_FIELD_NONE; + /* fall-through */ + case V4L2_FIELD_NONE: + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + field = pix->field; + break; + case V4L2_FIELD_INTERLACED: + /* Query for standard if not explicitly mentioned _TB/_BT */ + ret = v4l2_subdev_call(sd, video, querystd, &std); + if (ret < 0) + std = V4L2_STD_625_50; + + field = std & V4L2_STD_625_50 ? V4L2_FIELD_INTERLACED_TB : + V4L2_FIELD_INTERLACED_BT; + break; + } + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + /* Calculate client output geometry */ + soc_camera_calc_client_output(icd, &cam->rect, &cam->subrect, pix, &mf, + 12); + mf.field = pix->field; + mf.colorspace = pix->colorspace; + mf.code = xlate->code; + + switch (pixfmt) { + case V4L2_PIX_FMT_RGB32: + can_scale = priv->chip != RCAR_E1; + break; + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB555X: + can_scale = true; + break; + default: + can_scale = false; + break; + } + + dev_dbg(dev, "request camera output %ux%u\n", mf.width, mf.height); + + ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect, + &mf, &vin_sub_width, &vin_sub_height, + can_scale, 12); + + /* Done with the camera. Now see if we can improve the result */ + dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n", + ret, mf.width, mf.height, pix->width, pix->height); + + if (ret == -ENOIOCTLCMD) + dev_dbg(dev, "Sensor doesn't support scaling\n"); + else if (ret < 0) + return ret; + + if (mf.code != xlate->code) + return -EINVAL; + + /* Prepare VIN crop */ + cam->width = mf.width; + cam->height = mf.height; + + /* Use VIN scaling to scale to the requested user window. */ + + /* We cannot scale up */ + if (pix->width > vin_sub_width) + vin_sub_width = pix->width; + + if (pix->height > vin_sub_height) + vin_sub_height = pix->height; + + pix->colorspace = mf.colorspace; + + if (!can_scale) { + pix->width = vin_sub_width; + pix->height = vin_sub_height; + } + + /* + * We have calculated CFLCR, the actual configuration will be performed + * in rcar_vin_set_bus_param() + */ + + dev_dbg(dev, "W: %u : %u, H: %u : %u\n", + vin_sub_width, pix->width, vin_sub_height, pix->height); + + icd->current_fmt = xlate; + + priv->field = field; + + return 0; +} + +static int rcar_vin_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_mbus_framefmt mf; + __u32 pixfmt = pix->pixelformat; + int width, height; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + xlate = icd->current_fmt; + dev_dbg(icd->parent, "Format %x not found, keeping %x\n", + pixfmt, xlate->host_fmt->fourcc); + pixfmt = xlate->host_fmt->fourcc; + pix->pixelformat = pixfmt; + pix->colorspace = icd->colorspace; + } + + /* FIXME: calculate using depth and bus width */ + v4l_bound_align_image(&pix->width, 2, VIN_MAX_WIDTH, 1, + &pix->height, 4, VIN_MAX_HEIGHT, 2, 0); + + width = pix->width; + height = pix->height; + + /* let soc-camera calculate these values */ + pix->bytesperline = 0; + pix->sizeimage = 0; + + /* limit to sensor capabilities */ + mf.width = pix->width; + mf.height = pix->height; + mf.field = pix->field; + mf.code = xlate->code; + mf.colorspace = pix->colorspace; + + ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), + 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; + + if (pixfmt == V4L2_PIX_FMT_NV16) { + /* FIXME: check against rect_max after converting soc-camera */ + /* We can scale precisely, need a bigger image from camera */ + if (pix->width < width || pix->height < height) { + /* + * We presume, the sensor behaves sanely, i.e. if + * requested a bigger rectangle, it will not return a + * smaller one. + */ + mf.width = VIN_MAX_WIDTH; + mf.height = VIN_MAX_HEIGHT; + ret = v4l2_device_call_until_err(sd->v4l2_dev, + soc_camera_grp_id(icd), + video, try_mbus_fmt, + &mf); + if (ret < 0) { + dev_err(icd->parent, + "client try_fmt() = %d\n", ret); + return ret; + } + } + /* We will scale exactly */ + if (mf.width > width) + pix->width = width; + if (mf.height > height) + pix->height = height; + } + + return ret; +} + +static unsigned int rcar_vin_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_device *icd = file->private_data; + + return vb2_poll(&icd->vb2_vidq, file, pt); +} + +static int rcar_vin_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + return 0; +} + +static int rcar_vin_init_videobuf2(struct vb2_queue *vq, + struct soc_camera_device *icd) +{ + vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vq->io_modes = VB2_MMAP | VB2_USERPTR; + vq->drv_priv = icd; + vq->ops = &rcar_vin_vb2_ops; + vq->mem_ops = &vb2_dma_contig_memops; + vq->buf_struct_size = sizeof(struct rcar_vin_buffer); + vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + return vb2_queue_init(vq); +} + +static struct soc_camera_host_ops rcar_vin_host_ops = { + .owner = THIS_MODULE, + .add = rcar_vin_add_device, + .remove = rcar_vin_remove_device, + .clock_start = rcar_vin_clock_start, + .clock_stop = rcar_vin_clock_stop, + .get_formats = rcar_vin_get_formats, + .put_formats = rcar_vin_put_formats, + .get_crop = rcar_vin_get_crop, + .set_crop = rcar_vin_set_crop, + .try_fmt = rcar_vin_try_fmt, + .set_fmt = rcar_vin_set_fmt, + .poll = rcar_vin_poll, + .querycap = rcar_vin_querycap, + .set_bus_param = rcar_vin_set_bus_param, + .init_videobuf2 = rcar_vin_init_videobuf2, +}; + +static struct platform_device_id rcar_vin_id_table[] = { + { "r8a7779-vin", RCAR_H1 }, + { "r8a7778-vin", RCAR_M1 }, + { "uPD35004-vin", RCAR_E1 }, + {}, +}; +MODULE_DEVICE_TABLE(platform, rcar_vin_id_table); + +static int rcar_vin_probe(struct platform_device *pdev) +{ + struct rcar_vin_priv *priv; + struct resource *mem; + struct rcar_vin_platform_data *pdata; + int irq, ret; + + pdata = pdev->dev.platform_data; + if (!pdata || !pdata->flags) { + dev_err(&pdev->dev, "platform data not set\n"); + return -EINVAL; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mem == NULL) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct rcar_vin_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + ret = devm_request_irq(&pdev->dev, irq, rcar_vin_irq, IRQF_SHARED, + dev_name(&pdev->dev), priv); + if (ret) + return ret; + + priv->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(priv->alloc_ctx)) + return PTR_ERR(priv->alloc_ctx); + + 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 = pdata; + priv->chip = pdev->id_entry->driver_data; + spin_lock_init(&priv->lock); + INIT_LIST_HEAD(&priv->capture); + + priv->state = STOPPED; + + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); + + ret = soc_camera_host_register(&priv->ici); + if (ret) + goto cleanup; + + return 0; + +cleanup: + pm_runtime_disable(&pdev->dev); + vb2_dma_contig_cleanup_ctx(priv->alloc_ctx); + + return ret; +} + +static int rcar_vin_remove(struct platform_device *pdev) +{ + struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); + struct rcar_vin_priv *priv = container_of(soc_host, + struct rcar_vin_priv, ici); + + soc_camera_host_unregister(soc_host); + pm_runtime_disable(&pdev->dev); + vb2_dma_contig_cleanup_ctx(priv->alloc_ctx); + + return 0; +} + +static struct platform_driver rcar_vin_driver = { + .probe = rcar_vin_probe, + .remove = rcar_vin_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .id_table = rcar_vin_id_table, +}; + +module_platform_driver(rcar_vin_driver); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rcar_vin"); +MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver"); diff --git a/include/linux/platform_data/camera-rcar.h b/include/linux/platform_data/camera-rcar.h new file mode 100644 index 0000000..dfc83c5 --- /dev/null +++ b/include/linux/platform_data/camera-rcar.h @@ -0,0 +1,25 @@ +/* + * Platform data for Renesas R-Car VIN soc-camera driver + * + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., + * + * 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 __CAMERA_RCAR_H_ +#define __CAMERA_RCAR_H_ + +#define RCAR_VIN_HSYNC_ACTIVE_LOW (1 << 0) +#define RCAR_VIN_VSYNC_ACTIVE_LOW (1 << 1) +#define RCAR_VIN_BT601 (1 << 2) +#define RCAR_VIN_BT656 (1 << 3) + +struct rcar_vin_platform_data { + unsigned int flags; +}; + +#endif /* __CAMERA_RCAR_H_ */ -- cgit v0.10.2 From 85e86c6e8d400c6b794e971ee14251e7ad3467aa Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 25 Jul 2013 09:40:34 -0300 Subject: [media] soc_camera: fix compiler warning media_build/v4l/soc_camera.c: In function 'soc_camera_host_register': media_build/v4l/soc_camera.c:1513:10: warning: 'sasd' may be used uninitialized in this function [-Wmaybe-uninitialized] snprintf(clk_name, sizeof(clk_name), "%d-%04x", ^ media_build/v4l/soc_camera.c:1464:34: note: 'sasd' was declared here struct soc_camera_async_subdev *sasd; ^ By changing the type of 'i' to unsigned and changing a condition we finally convince the compiler that sasd is really initialized. Signed-off-by: Hans Verkuil 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 4b42572..dc1b26c 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1466,7 +1466,8 @@ static int scan_async_group(struct soc_camera_host *ici, struct soc_camera_device *icd; struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; char clk_name[V4L2_SUBDEV_NAME_SIZE]; - int ret, i; + unsigned int i; + int ret; /* First look for a sensor */ for (i = 0; i < size; i++) { @@ -1475,7 +1476,7 @@ static int scan_async_group(struct soc_camera_host *ici, break; } - if (i == size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) { + if (i >= size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) { /* All useless */ dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n"); return -ENODEV; -- cgit v0.10.2 From 23272427ccf70d40b3abb81ea92f505adc1bdcd1 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 30 Jul 2013 08:01:55 -0300 Subject: [media] V4L2: soc-camera: fix requesting regulators in synchronous case With synchronous subdevice probing regulators should be requested by the soc-camera core in soc_camera_pdrv_probe(). Subdevice drivers, supporting asynchronous probing, call soc_camera_power_init() to request regulators. Erroneously, the same regulator array is used in the latter case as in the former, which leads to a failure. This patch fixes it by preventing the second regulator request from being executed. 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 dc1b26c..387a232 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -136,7 +136,7 @@ EXPORT_SYMBOL(soc_camera_power_off); int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd) { - + /* Should not have any effect in synchronous case */ return devm_regulator_bulk_get(dev, ssdd->num_regulators, ssdd->regulators); } @@ -1311,6 +1311,7 @@ eusrfmt: static int soc_camera_i2c_init(struct soc_camera_device *icd, struct soc_camera_desc *sdesc) { + struct soc_camera_subdev_desc *ssdd; struct i2c_client *client; struct soc_camera_host *ici; struct soc_camera_host_desc *shd = &sdesc->host_desc; @@ -1333,7 +1334,21 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd, return -ENODEV; } - shd->board_info->platform_data = &sdesc->subdev_desc; + ssdd = kzalloc(sizeof(*ssdd), GFP_KERNEL); + if (!ssdd) { + ret = -ENOMEM; + goto ealloc; + } + + memcpy(ssdd, &sdesc->subdev_desc, sizeof(*ssdd)); + /* + * In synchronous case we request regulators ourselves in + * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try + * to allocate them again. + */ + ssdd->num_regulators = 0; + ssdd->regulators = NULL; + shd->board_info->platform_data = ssdd; snprintf(clk_name, sizeof(clk_name), "%d-%04x", shd->i2c_adapter_id, shd->board_info->addr); @@ -1359,8 +1374,10 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd, return 0; ei2cnd: v4l2_clk_unregister(icd->clk); -eclkreg: icd->clk = NULL; +eclkreg: + kfree(ssdd); +ealloc: i2c_put_adapter(adap); return ret; } @@ -1370,15 +1387,18 @@ static void soc_camera_i2c_free(struct soc_camera_device *icd) struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); struct i2c_adapter *adap; + struct soc_camera_subdev_desc *ssdd; icd->control = NULL; if (icd->sasc) return; adap = client->adapter; + ssdd = client->dev.platform_data; v4l2_device_unregister_subdev(i2c_get_clientdata(client)); i2c_unregister_device(client); i2c_put_adapter(adap); + kfree(ssdd); v4l2_clk_unregister(icd->clk); icd->clk = NULL; } @@ -1995,9 +2015,10 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev) /* * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below - * regulator allocation is a dummy. They will be really requested later - * in soc_camera_async_bind(). Also note, that in that case regulators - * are attached to the I2C device and not to the camera platform device. + * regulator allocation is a dummy. They are actually requested by the + * subdevice driver, using soc_camera_power_init(). Also note, that in + * that case regulators are attached to the I2C device and not to the + * camera platform device. */ ret = devm_regulator_bulk_get(&pdev->dev, ssdd->num_regulators, ssdd->regulators); -- cgit v0.10.2 From 72f2874411dff8d8ec424ecf7e2d1a3ebe0302f0 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 29 Jul 2013 12:18:04 -0300 Subject: [media] V4L2: mx3_camera: convert to managed resource allocation Use devm_* resource allocators to simplify the driver's probe and clean up paths. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 1047e3e..e526096 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -1151,23 +1151,19 @@ static int mx3_camera_probe(struct platform_device *pdev) struct soc_camera_host *soc_host; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - err = -ENODEV; - goto egetres; - } + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); - mx3_cam = vzalloc(sizeof(*mx3_cam)); + mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL); if (!mx3_cam) { dev_err(&pdev->dev, "Could not allocate mx3 camera object\n"); - err = -ENOMEM; - goto ealloc; + return -ENOMEM; } - mx3_cam->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(mx3_cam->clk)) { - err = PTR_ERR(mx3_cam->clk); - goto eclkget; - } + mx3_cam->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mx3_cam->clk)) + return PTR_ERR(mx3_cam->clk); mx3_cam->pdata = pdev->dev.platform_data; mx3_cam->platform_flags = mx3_cam->pdata->flags; @@ -1201,13 +1197,6 @@ static int mx3_camera_probe(struct platform_device *pdev) INIT_LIST_HEAD(&mx3_cam->capture); spin_lock_init(&mx3_cam->lock); - base = ioremap(res->start, resource_size(res)); - if (!base) { - pr_err("Couldn't map %x@%x\n", resource_size(res), res->start); - err = -ENOMEM; - goto eioremap; - } - mx3_cam->base = base; soc_host = &mx3_cam->soc_host; @@ -1218,10 +1207,8 @@ static int mx3_camera_probe(struct platform_device *pdev) soc_host->nr = pdev->id; mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(mx3_cam->alloc_ctx)) { - err = PTR_ERR(mx3_cam->alloc_ctx); - goto eallocctx; - } + if (IS_ERR(mx3_cam->alloc_ctx)) + return PTR_ERR(mx3_cam->alloc_ctx); err = soc_camera_host_register(soc_host); if (err) @@ -1234,14 +1221,6 @@ static int mx3_camera_probe(struct platform_device *pdev) ecamhostreg: vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx); -eallocctx: - iounmap(base); -eioremap: - clk_put(mx3_cam->clk); -eclkget: - vfree(mx3_cam); -ealloc: -egetres: return err; } @@ -1251,12 +1230,8 @@ static int mx3_camera_remove(struct platform_device *pdev) struct mx3_camera_dev *mx3_cam = container_of(soc_host, struct mx3_camera_dev, soc_host); - clk_put(mx3_cam->clk); - soc_camera_host_unregister(soc_host); - iounmap(mx3_cam->base); - /* * The channel has either not been allocated, * or should have been released @@ -1266,8 +1241,6 @@ static int mx3_camera_remove(struct platform_device *pdev) vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx); - vfree(mx3_cam); - dmaengine_put(); return 0; -- cgit v0.10.2 From 2bcccaec9937a49a65c258789d33ecc5ee1f8fff Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 30 Jul 2013 02:53:15 -0300 Subject: [media] V4L2: mx3_camera: print V4L2_MBUS_FMT_* codes in hexadecimal format V4L2_MBUS_FMT_* codes are defined in v4l2-mediabus.h as hexadecimal constants. Print them in the same form for easier recognition. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index e526096..83592e4 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -672,7 +672,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id fmt = soc_mbus_get_fmtdesc(code); if (!fmt) { dev_warn(icd->parent, - "Unsupported format code #%u: %d\n", idx, code); + "Unsupported format code #%u: 0x%x\n", idx, code); return 0; } @@ -688,7 +688,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id xlate->host_fmt = &mx3_camera_formats[0]; xlate->code = code; xlate++; - dev_dbg(dev, "Providing format %s using code %d\n", + dev_dbg(dev, "Providing format %s using code 0x%x\n", mx3_camera_formats[0].name, code); } break; @@ -698,7 +698,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id xlate->host_fmt = &mx3_camera_formats[1]; xlate->code = code; xlate++; - dev_dbg(dev, "Providing format %s using code %d\n", + dev_dbg(dev, "Providing format %s using code 0x%x\n", mx3_camera_formats[1].name, code); } break; -- cgit v0.10.2 From 4dbfd040757b8bf22f4ac17e80b39c068061a16c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 30 Jul 2013 02:59:49 -0300 Subject: [media] V4L2: mx3_camera: add support for asynchronous subdevice registration The soc-camera core does all the work on supporting asynchronous subdevice probing, host drivers only have to pass a subdevice list to soc-camera. Typically this list is provided by the platform. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 83592e4..8f9f621 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -1144,6 +1144,7 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = { static int mx3_camera_probe(struct platform_device *pdev) { + struct mx3_camera_pdata *pdata = pdev->dev.platform_data; struct mx3_camera_dev *mx3_cam; struct resource *res; void __iomem *base; @@ -1155,6 +1156,9 @@ static int mx3_camera_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); + if (!pdata) + return -EINVAL; + mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL); if (!mx3_cam) { dev_err(&pdev->dev, "Could not allocate mx3 camera object\n"); @@ -1165,8 +1169,8 @@ static int mx3_camera_probe(struct platform_device *pdev) if (IS_ERR(mx3_cam->clk)) return PTR_ERR(mx3_cam->clk); - mx3_cam->pdata = pdev->dev.platform_data; - mx3_cam->platform_flags = mx3_cam->pdata->flags; + mx3_cam->pdata = pdata; + mx3_cam->platform_flags = pdata->flags; if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) { /* * Platform hasn't set available data widths. This is bad. @@ -1185,7 +1189,7 @@ static int mx3_camera_probe(struct platform_device *pdev) if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) mx3_cam->width_flags |= 1 << 14; - mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000; + mx3_cam->mclk = pdata->mclk_10khz * 10000; if (!mx3_cam->mclk) { dev_warn(&pdev->dev, "mclk_10khz == 0! Please, fix your platform data. " @@ -1210,6 +1214,11 @@ static int mx3_camera_probe(struct platform_device *pdev) if (IS_ERR(mx3_cam->alloc_ctx)) return PTR_ERR(mx3_cam->alloc_ctx); + if (pdata->asd_sizes) { + soc_host->asd = pdata->asd; + soc_host->asd_sizes = pdata->asd_sizes; + } + err = soc_camera_host_register(soc_host); if (err) goto ecamhostreg; @@ -1249,6 +1258,7 @@ static int mx3_camera_remove(struct platform_device *pdev) static struct platform_driver mx3_camera_driver = { .driver = { .name = MX3_CAM_DRV_NAME, + .owner = THIS_MODULE, }, .probe = mx3_camera_probe, .remove = mx3_camera_remove, diff --git a/include/linux/platform_data/camera-mx3.h b/include/linux/platform_data/camera-mx3.h index f226ee3..a910dad 100644 --- a/include/linux/platform_data/camera-mx3.h +++ b/include/linux/platform_data/camera-mx3.h @@ -33,6 +33,8 @@ #define MX3_CAMERA_DATAWIDTH_MASK (MX3_CAMERA_DATAWIDTH_4 | MX3_CAMERA_DATAWIDTH_8 | \ MX3_CAMERA_DATAWIDTH_10 | MX3_CAMERA_DATAWIDTH_15) +struct v4l2_async_subdev; + /** * struct mx3_camera_pdata - i.MX3x camera platform data * @flags: MX3_CAMERA_* flags @@ -43,6 +45,8 @@ struct mx3_camera_pdata { unsigned long flags; unsigned long mclk_10khz; struct device *dma_dev; + struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ + int *asd_sizes; /* 0-terminated array of asd group sizes */ }; #endif -- cgit v0.10.2 From 3d238885dbfd5359cd0c12fad858e8638f17bfc6 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 30 Jul 2013 03:34:06 -0300 Subject: [media] V4L2: mt9t031: don't Oops if asynchronous probing is attempted The mt9t031 driver hasn't yet been updated to support asynchronous subdevice probing. If such a probing is attempted, the driver is allowed to fail, but it shouldn't Oops. This patch fixes such a potential NULL pointer dereference. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index 47d18d0..ee7bb0f 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -594,9 +594,12 @@ static int mt9t031_s_power(struct v4l2_subdev *sd, int on) ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk); if (ret < 0) return ret; - vdev->dev.type = &mt9t031_dev_type; + if (vdev) + /* Not needed during probing, when vdev isn't available yet */ + vdev->dev.type = &mt9t031_dev_type; } else { - vdev->dev.type = NULL; + if (vdev) + vdev->dev.type = NULL; soc_camera_power_off(&client->dev, ssdd, mt9t031->clk); } -- cgit v0.10.2 From ef6672ea35b5bb64ab42e18c1a1ffc717c31588a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 30 Jul 2013 04:35:18 -0300 Subject: [media] V4L2: mt9m111: switch to asynchronous subdevice probing Convert the mt9m111 driver to asynchronous subdevice probing. Synchronous probing is also still possible. 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 de3605d..6f40566 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -946,6 +946,10 @@ static int mt9m111_probe(struct i2c_client *client, if (!mt9m111) return -ENOMEM; + mt9m111->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(mt9m111->clk)) + return -EPROBE_DEFER; + /* Default HIGHPOWER context */ mt9m111->ctx = &context_b; @@ -963,8 +967,10 @@ static int mt9m111_probe(struct i2c_client *client, &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); mt9m111->subdev.ctrl_handler = &mt9m111->hdl; - if (mt9m111->hdl.error) - return mt9m111->hdl.error; + if (mt9m111->hdl.error) { + ret = mt9m111->hdl.error; + goto out_clkput; + } /* Second stage probe - when a capture adapter is there */ mt9m111->rect.left = MT9M111_MIN_DARK_COLS; @@ -975,18 +981,25 @@ static int mt9m111_probe(struct i2c_client *client, mt9m111->lastpage = -1; mutex_init(&mt9m111->power_lock); - mt9m111->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9m111->clk)) { - ret = PTR_ERR(mt9m111->clk); - goto eclkget; - } + ret = soc_camera_power_init(&client->dev, ssdd); + if (ret < 0) + goto out_hdlfree; ret = mt9m111_video_probe(client); - if (ret) { - v4l2_clk_put(mt9m111->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9m111->hdl); - } + if (ret < 0) + goto out_hdlfree; + + mt9m111->subdev.dev = &client->dev; + ret = v4l2_async_register_subdev(&mt9m111->subdev); + if (ret < 0) + goto out_hdlfree; + + return 0; + +out_hdlfree: + v4l2_ctrl_handler_free(&mt9m111->hdl); +out_clkput: + v4l2_clk_put(mt9m111->clk); return ret; } @@ -995,6 +1008,7 @@ static int mt9m111_remove(struct i2c_client *client) { struct mt9m111 *mt9m111 = to_mt9m111(client); + v4l2_async_unregister_subdev(&mt9m111->subdev); v4l2_clk_put(mt9m111->clk); v4l2_device_unregister_subdev(&mt9m111->subdev); v4l2_ctrl_handler_free(&mt9m111->hdl); -- cgit v0.10.2 From ceedcc4ede04e069c5201748a34b2bdc044d3824 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 31 Jul 2013 13:10:18 -0300 Subject: [media] v4l2-async: Use proper list head for iteration over registered subdevs This fixes regression introduced in commit b426b3a660c85faf6e1ca1c92c6d [media] V4L: Merge struct v4l2_async_subdev_list with struct v4l2_subdev Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 10bb62c..c85d69d 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -199,7 +199,7 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) list_del(¬ifier->list); - list_for_each_entry_safe(sd, tmp, ¬ifier->done, list) { + list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) { dev[i] = get_device(sd->dev); v4l2_async_cleanup(sd); -- cgit v0.10.2 From 3dbc9964b926c4ab4340df902efdb40bfe769811 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 24 Jul 2013 10:57:32 -0300 Subject: [media] V4L: s5c73m3: Add format propagation for TRY formats Resolution set on ISP pad of S5C73M3-OIF subdev should be propagated to source pad for TRY and ACTIVE formats. The patch adds missing propagation for TRY format. Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 825ea86..b76ec0e 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1111,6 +1111,11 @@ static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { mf = v4l2_subdev_get_try_format(fh, fmt->pad); *mf = fmt->format; + if (fmt->pad == OIF_ISP_PAD) { + mf = v4l2_subdev_get_try_format(fh, OIF_SOURCE_PAD); + mf->width = fmt->format.width; + mf->height = fmt->format.height; + } } else { switch (fmt->pad) { case OIF_ISP_PAD: -- cgit v0.10.2 From 6a40cbbe471bc7b821458973a5c18ba3d97d72f4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 2 Aug 2013 04:58:25 -0300 Subject: [media] exynos4-is: Fix potential NULL pointer dereference dev->of_node could be NULL. Hence check for the same and return before dereferencing it in the subsequent error message. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 08fbfed..318d4c3 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -1504,16 +1504,17 @@ static int fimc_lite_probe(struct platform_device *pdev) struct resource *res; int ret; + if (!dev->of_node) + return -ENODEV; + fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL); if (!fimc) return -ENOMEM; - if (dev->of_node) { - of_id = of_match_node(flite_of_match, dev->of_node); - if (of_id) - drv_data = (struct flite_drvdata *)of_id->data; - fimc->index = of_alias_get_id(dev->of_node, "fimc-lite"); - } + of_id = of_match_node(flite_of_match, dev->of_node); + if (of_id) + drv_data = (struct flite_drvdata *)of_id->data; + fimc->index = of_alias_get_id(dev->of_node, "fimc-lite"); if (!drv_data || fimc->index >= drv_data->num_instances || fimc->index < 0) { -- cgit v0.10.2 From ae796c78684c1679b82eaafcd236d6fc1de099a2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 2 Aug 2013 02:32:12 -0300 Subject: [media] exynos4-is: Staticize local symbol __fimc_is_hw_update_param is used only in this file. Make it static. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c index c7e7f69..a353be0 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-param.c +++ b/drivers/media/platform/exynos4-is/fimc-is-param.c @@ -56,7 +56,7 @@ static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is) __hw_param_copy(dst, src); } -int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset) +static int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset) { struct is_param_region *par = &is->is_p_region->parameter; struct chain_config *cfg = &is->config[is->config_index]; -- cgit v0.10.2 From f9fa906f00388bf326d010f36bc905cbd0b6554f Mon Sep 17 00:00:00 2001 From: Prathyush K Date: Wed, 7 Aug 2013 08:53:04 -0300 Subject: [media] exynos-gsc: fix s2r functionality When gsc is in runtime suspended state, there is no need to call m2m_suspend during suspend and similarly, there is no need to call m2m_resume during resume if already in runtime suspended state. This patch adds the necessary conditions to achieve this. Signed-off-by: Prathyush K Signed-off-by: Arun Mankuzhi Signed-off-by: Arun Kumar K Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 559fab2..fe69eae 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1210,12 +1210,12 @@ static int gsc_resume(struct device *dev) spin_unlock_irqrestore(&gsc->slock, flags); return 0; } - gsc_hw_set_sw_reset(gsc); - gsc_wait_reset(gsc); - spin_unlock_irqrestore(&gsc->slock, flags); - return gsc_m2m_resume(gsc); + if (!pm_runtime_suspended(dev)) + return gsc_runtime_resume(dev); + + return 0; } static int gsc_suspend(struct device *dev) @@ -1227,7 +1227,10 @@ static int gsc_suspend(struct device *dev) if (test_and_set_bit(ST_SUSPEND, &gsc->state)) return 0; - return gsc_m2m_suspend(gsc); + if (!pm_runtime_suspended(dev)) + return gsc_runtime_suspend(dev); + + return 0; } static const struct dev_pm_ops gsc_pm_ops = { -- cgit v0.10.2 From e5b6a69790c7112b90570db0202c6de621485fb9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 18 Aug 2013 09:36:03 -0300 Subject: sh_mobile_ceu_camera: Fix a compilation warning drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c: In function 'sh_mobile_ceu_clock_start': drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c:613:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] Cc: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index dae9716..8df22f7 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -610,13 +610,12 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) static int sh_mobile_ceu_clock_start(struct soc_camera_host *ici) { struct sh_mobile_ceu_dev *pcdev = ici->priv; - int ret; pm_runtime_get_sync(ici->v4l2_dev.dev); pcdev->buf_total = 0; - ret = sh_mobile_ceu_soft_reset(pcdev); + sh_mobile_ceu_soft_reset(pcdev); return 0; } -- cgit v0.10.2 From bfd22c490bc74f9603ea90c37823036660a313e2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 18 Aug 2013 10:17:38 -0300 Subject: v4l2-common: warning fix (W=1): add a missed function prototype changeset d1e9b7c12 added a new ancilliary function to API, but forgot to add it to the corresponding header file. drivers/media/v4l2-core/v4l2-ctrls.c:568:18: warning: no previous prototype for 'v4l2_ctrl_get_int_menu' [-Wmissing-prototypes] const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len) Cc: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 0e1d010..16550c4 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -86,6 +86,7 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, const char * const *menu_items); const char *v4l2_ctrl_get_name(u32 id); const char * const *v4l2_ctrl_get_menu(u32 id); +const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len); int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def); int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl, const char * const *menu_items); -- cgit v0.10.2 From 977e444f59ad16eedf66acae4f357011c74da291 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 5 Jul 2013 00:24:08 -0300 Subject: [media] Mirics MSi3101 SDR Dongle driver It is first Kernel software defined radio receiver driver. That device is sold as a digital television solution. MSi3101 is reference design which consists of two chips: 1) MSi2500 USB ADC 2) MSi001 RF-tuner Driver is put to staging as API is not ready. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index ae0abc3..46f1e61 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -29,6 +29,8 @@ source "drivers/staging/media/dt3155v4l/Kconfig" source "drivers/staging/media/go7007/Kconfig" +source "drivers/staging/media/msi3101/Kconfig" + source "drivers/staging/media/solo6x10/Kconfig" # Keep LIRC at the end, as it has sub-menus diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 2b97cae..eb7f30b 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -4,4 +4,5 @@ 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/ diff --git a/drivers/staging/media/msi3101/Kconfig b/drivers/staging/media/msi3101/Kconfig new file mode 100644 index 0000000..b94a95a --- /dev/null +++ b/drivers/staging/media/msi3101/Kconfig @@ -0,0 +1,3 @@ +config USB_MSI3101 + tristate "Mirics MSi3101 SDR Dongle" + depends on USB && VIDEO_DEV && VIDEO_V4L2 diff --git a/drivers/staging/media/msi3101/Makefile b/drivers/staging/media/msi3101/Makefile new file mode 100644 index 0000000..3730654 --- /dev/null +++ b/drivers/staging/media/msi3101/Makefile @@ -0,0 +1 @@ +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 new file mode 100644 index 0000000..46fdb6c --- /dev/null +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -0,0 +1,1618 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct msi3101_gain { + u8 tot:7; + u8 baseband:6; + bool lna:1; + bool mixer:1; +}; + +/* 60 – 120 MHz band, lna 24dB, mixer 19dB */ +static const struct msi3101_gain msi3101_gain_lut_120[] = { + { 0, 0, 0, 0}, + { 1, 1, 0, 0}, + { 2, 2, 0, 0}, + { 3, 3, 0, 0}, + { 4, 4, 0, 0}, + { 5, 5, 0, 0}, + { 6, 6, 0, 0}, + { 7, 7, 0, 0}, + { 8, 8, 0, 0}, + { 9, 9, 0, 0}, + { 10, 10, 0, 0}, + { 11, 11, 0, 0}, + { 12, 12, 0, 0}, + { 13, 13, 0, 0}, + { 14, 14, 0, 0}, + { 15, 15, 0, 0}, + { 16, 16, 0, 0}, + { 17, 17, 0, 0}, + { 18, 18, 0, 0}, + { 19, 19, 0, 0}, + { 20, 20, 0, 0}, + { 21, 21, 0, 0}, + { 22, 22, 0, 0}, + { 23, 23, 0, 0}, + { 24, 24, 0, 0}, + { 25, 25, 0, 0}, + { 26, 26, 0, 0}, + { 27, 27, 0, 0}, + { 28, 28, 0, 0}, + { 29, 5, 1, 0}, + { 30, 6, 1, 0}, + { 31, 7, 1, 0}, + { 32, 8, 1, 0}, + { 33, 9, 1, 0}, + { 34, 10, 1, 0}, + { 35, 11, 1, 0}, + { 36, 12, 1, 0}, + { 37, 13, 1, 0}, + { 38, 14, 1, 0}, + { 39, 15, 1, 0}, + { 40, 16, 1, 0}, + { 41, 17, 1, 0}, + { 42, 18, 1, 0}, + { 43, 19, 1, 0}, + { 44, 20, 1, 0}, + { 45, 21, 1, 0}, + { 46, 22, 1, 0}, + { 47, 23, 1, 0}, + { 48, 24, 1, 0}, + { 49, 25, 1, 0}, + { 50, 26, 1, 0}, + { 51, 27, 1, 0}, + { 52, 28, 1, 0}, + { 53, 29, 1, 0}, + { 54, 30, 1, 0}, + { 55, 31, 1, 0}, + { 56, 32, 1, 0}, + { 57, 33, 1, 0}, + { 58, 34, 1, 0}, + { 59, 35, 1, 0}, + { 60, 36, 1, 0}, + { 61, 37, 1, 0}, + { 62, 38, 1, 0}, + { 63, 39, 1, 0}, + { 64, 40, 1, 0}, + { 65, 41, 1, 0}, + { 66, 42, 1, 0}, + { 67, 43, 1, 0}, + { 68, 44, 1, 0}, + { 69, 45, 1, 0}, + { 70, 46, 1, 0}, + { 71, 47, 1, 0}, + { 72, 48, 1, 0}, + { 73, 49, 1, 0}, + { 74, 50, 1, 0}, + { 75, 51, 1, 0}, + { 76, 52, 1, 0}, + { 77, 53, 1, 0}, + { 78, 54, 1, 0}, + { 79, 55, 1, 0}, + { 80, 56, 1, 0}, + { 81, 57, 1, 0}, + { 82, 58, 1, 0}, + { 83, 40, 1, 1}, + { 84, 41, 1, 1}, + { 85, 42, 1, 1}, + { 86, 43, 1, 1}, + { 87, 44, 1, 1}, + { 88, 45, 1, 1}, + { 89, 46, 1, 1}, + { 90, 47, 1, 1}, + { 91, 48, 1, 1}, + { 92, 49, 1, 1}, + { 93, 50, 1, 1}, + { 94, 51, 1, 1}, + { 95, 52, 1, 1}, + { 96, 53, 1, 1}, + { 97, 54, 1, 1}, + { 98, 55, 1, 1}, + { 99, 56, 1, 1}, + {100, 57, 1, 1}, + {101, 58, 1, 1}, + {102, 59, 1, 1}, +}; + +/* 120 – 245 MHz band, lna 24dB, mixer 19dB */ +static const struct msi3101_gain msi3101_gain_lut_245[] = { + { 0, 0, 0, 0}, + { 1, 1, 0, 0}, + { 2, 2, 0, 0}, + { 3, 3, 0, 0}, + { 4, 4, 0, 0}, + { 5, 5, 0, 0}, + { 6, 6, 0, 0}, + { 7, 7, 0, 0}, + { 8, 8, 0, 0}, + { 9, 9, 0, 0}, + { 10, 10, 0, 0}, + { 11, 11, 0, 0}, + { 12, 12, 0, 0}, + { 13, 13, 0, 0}, + { 14, 14, 0, 0}, + { 15, 15, 0, 0}, + { 16, 16, 0, 0}, + { 17, 17, 0, 0}, + { 18, 18, 0, 0}, + { 19, 19, 0, 0}, + { 20, 20, 0, 0}, + { 21, 21, 0, 0}, + { 22, 22, 0, 0}, + { 23, 23, 0, 0}, + { 24, 24, 0, 0}, + { 25, 25, 0, 0}, + { 26, 26, 0, 0}, + { 27, 27, 0, 0}, + { 28, 28, 0, 0}, + { 29, 5, 1, 0}, + { 30, 6, 1, 0}, + { 31, 7, 1, 0}, + { 32, 8, 1, 0}, + { 33, 9, 1, 0}, + { 34, 10, 1, 0}, + { 35, 11, 1, 0}, + { 36, 12, 1, 0}, + { 37, 13, 1, 0}, + { 38, 14, 1, 0}, + { 39, 15, 1, 0}, + { 40, 16, 1, 0}, + { 41, 17, 1, 0}, + { 42, 18, 1, 0}, + { 43, 19, 1, 0}, + { 44, 20, 1, 0}, + { 45, 21, 1, 0}, + { 46, 22, 1, 0}, + { 47, 23, 1, 0}, + { 48, 24, 1, 0}, + { 49, 25, 1, 0}, + { 50, 26, 1, 0}, + { 51, 27, 1, 0}, + { 52, 28, 1, 0}, + { 53, 29, 1, 0}, + { 54, 30, 1, 0}, + { 55, 31, 1, 0}, + { 56, 32, 1, 0}, + { 57, 33, 1, 0}, + { 58, 34, 1, 0}, + { 59, 35, 1, 0}, + { 60, 36, 1, 0}, + { 61, 37, 1, 0}, + { 62, 38, 1, 0}, + { 63, 39, 1, 0}, + { 64, 40, 1, 0}, + { 65, 41, 1, 0}, + { 66, 42, 1, 0}, + { 67, 43, 1, 0}, + { 68, 44, 1, 0}, + { 69, 45, 1, 0}, + { 70, 46, 1, 0}, + { 71, 47, 1, 0}, + { 72, 48, 1, 0}, + { 73, 49, 1, 0}, + { 74, 50, 1, 0}, + { 75, 51, 1, 0}, + { 76, 52, 1, 0}, + { 77, 53, 1, 0}, + { 78, 54, 1, 0}, + { 79, 55, 1, 0}, + { 80, 56, 1, 0}, + { 81, 57, 1, 0}, + { 82, 58, 1, 0}, + { 83, 40, 1, 1}, + { 84, 41, 1, 1}, + { 85, 42, 1, 1}, + { 86, 43, 1, 1}, + { 87, 44, 1, 1}, + { 88, 45, 1, 1}, + { 89, 46, 1, 1}, + { 90, 47, 1, 1}, + { 91, 48, 1, 1}, + { 92, 49, 1, 1}, + { 93, 50, 1, 1}, + { 94, 51, 1, 1}, + { 95, 52, 1, 1}, + { 96, 53, 1, 1}, + { 97, 54, 1, 1}, + { 98, 55, 1, 1}, + { 99, 56, 1, 1}, + {100, 57, 1, 1}, + {101, 58, 1, 1}, + {102, 59, 1, 1}, +}; + +/* 420 – 1000 MHz band, lna 7dB, mixer 19dB */ +static const struct msi3101_gain msi3101_gain_lut_1000[] = { + { 0, 0, 0, 0}, + { 1, 1, 0, 0}, + { 2, 2, 0, 0}, + { 3, 3, 0, 0}, + { 4, 4, 0, 0}, + { 5, 5, 0, 0}, + { 6, 6, 0, 0}, + { 7, 7, 0, 0}, + { 8, 8, 0, 0}, + { 9, 9, 0, 0}, + { 10, 10, 0, 0}, + { 11, 11, 0, 0}, + { 12, 5, 1, 0}, + { 13, 6, 1, 0}, + { 14, 7, 1, 0}, + { 15, 8, 1, 0}, + { 16, 9, 1, 0}, + { 17, 10, 1, 0}, + { 18, 11, 1, 0}, + { 19, 12, 1, 0}, + { 20, 13, 1, 0}, + { 21, 14, 1, 0}, + { 22, 15, 1, 0}, + { 23, 16, 1, 0}, + { 24, 17, 1, 0}, + { 25, 18, 1, 0}, + { 26, 19, 1, 0}, + { 27, 20, 1, 0}, + { 28, 21, 1, 0}, + { 29, 22, 1, 0}, + { 30, 23, 1, 0}, + { 31, 24, 1, 0}, + { 32, 25, 1, 0}, + { 33, 26, 1, 0}, + { 34, 27, 1, 0}, + { 35, 28, 1, 0}, + { 36, 29, 1, 0}, + { 37, 30, 1, 0}, + { 38, 31, 1, 0}, + { 39, 32, 1, 0}, + { 40, 33, 1, 0}, + { 41, 34, 1, 0}, + { 42, 35, 1, 0}, + { 43, 36, 1, 0}, + { 44, 37, 1, 0}, + { 45, 38, 1, 0}, + { 46, 39, 1, 0}, + { 47, 40, 1, 0}, + { 48, 41, 1, 0}, + { 49, 42, 1, 0}, + { 50, 43, 1, 0}, + { 51, 44, 1, 0}, + { 52, 45, 1, 0}, + { 53, 46, 1, 0}, + { 54, 47, 1, 0}, + { 55, 48, 1, 0}, + { 56, 49, 1, 0}, + { 57, 50, 1, 0}, + { 58, 51, 1, 0}, + { 59, 52, 1, 0}, + { 60, 53, 1, 0}, + { 61, 54, 1, 0}, + { 62, 55, 1, 0}, + { 63, 56, 1, 0}, + { 64, 57, 1, 0}, + { 65, 58, 1, 0}, + { 66, 40, 1, 1}, + { 67, 41, 1, 1}, + { 68, 42, 1, 1}, + { 69, 43, 1, 1}, + { 70, 44, 1, 1}, + { 71, 45, 1, 1}, + { 72, 46, 1, 1}, + { 73, 47, 1, 1}, + { 74, 48, 1, 1}, + { 75, 49, 1, 1}, + { 76, 50, 1, 1}, + { 77, 51, 1, 1}, + { 78, 52, 1, 1}, + { 79, 53, 1, 1}, + { 80, 54, 1, 1}, + { 81, 55, 1, 1}, + { 82, 56, 1, 1}, + { 83, 57, 1, 1}, + { 84, 58, 1, 1}, + { 85, 59, 1, 1}, +}; + +/* + * 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 + +#define MSI3101_CID_SAMPLING_RATE ((V4L2_CID_USER_BASE | 0xf000) + 0) +#define MSI3101_CID_SAMPLING_RESOLUTION ((V4L2_CID_USER_BASE | 0xf000) + 1) +#define MSI3101_CID_TUNER_RF ((V4L2_CID_USER_BASE | 0xf000) + 10) +#define MSI3101_CID_TUNER_BW ((V4L2_CID_USER_BASE | 0xf000) + 11) +#define MSI3101_CID_TUNER_IF ((V4L2_CID_USER_BASE | 0xf000) + 12) +#define MSI3101_CID_TUNER_GAIN ((V4L2_CID_USER_BASE | 0xf000) + 13) + +/* 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; + void *data; /* raw data from USB device */ + int filled; /* number of bytes filled to *data */ +}; + +struct msi3101_state { + 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 */ + + /* 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 isoc_errors; /* number of contiguous ISOC errors */ + unsigned int vb_full; /* vb is full and packets dropped */ + + struct urb *urbs[MAX_ISO_BUFS]; + + /* Controls */ + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *ctrl_sampling_rate; + struct v4l2_ctrl *ctrl_tuner_rf; + struct v4l2_ctrl *ctrl_tuner_bw; + struct v4l2_ctrl *ctrl_tuner_if; + struct v4l2_ctrl *ctrl_tuner_gain; + + u32 symbol_received; /* for track lost packets */ + u32 symbol; /* for symbol rate calc */ + unsigned long jiffies; +}; + +/* 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; +} + +/* + * Converts signed 10-bit integer into 32-bit IEEE floating point + * representation. + * Will be exact from 0 to 2^24. Above that, we round towards zero + * as the fractional bits will not fit in a float. (It would be better to + * round towards even as the fpu does, but that is slower.) + */ +#define I2F_FRAC_BITS 23 +#define I2F_MASK ((1 << I2F_FRAC_BITS) - 1) +static uint32_t msi3101_int2float(uint32_t x) +{ + uint32_t msb, exponent, fraction, sign; + + /* Zero is special */ + if (!x) + return 0; + + /* Negative / positive value */ + if (x & 0x200) { + x = -x; + x &= 0x3ff; + sign = 1 << 31; + } else { + sign = 0 << 31; + } + + /* Get location of the most significant bit */ + msb = __fls(x); + + /* + * Use a rotate instead of a shift because that works both leftwards + * and rightwards due to the mod(32) behaviour. This means we don't + * need to check to see if we are above 2^24 or not. + */ + fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK; + exponent = (127 + msb) << I2F_FRAC_BITS; + + return (fraction + exponent) | sign; +} + +#define MSI3101_CONVERT_IN_URB_HANDLER +#define MSI3101_EXTENSIVE_DEBUG +static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, + const u8 *src, unsigned int src_len) +{ + int i, j, k, l, i_max, dst_len = 0; + u16 sample[4]; +#ifdef MSI3101_EXTENSIVE_DEBUG + u32 symbol[3]; +#endif + /* There could be 1-3 1024 bytes URB frames */ + i_max = src_len / 1024; + for (i = 0; i < i_max; i++) { +#ifdef MSI3101_EXTENSIVE_DEBUG + symbol[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; + if (i == 0 && s->symbol_received != symbol[0]) { + dev_dbg(&s->udev->dev, + "%d symbols lost, %d %08x:%08x\n", + symbol[0] - s->symbol_received, + src_len, s->symbol_received, symbol[0]); + } +#endif + src += 16; + for (j = 0; j < 6; j++) { + for (k = 0; k < 16; k++) { + for (l = 0; l < 10; l += 5) { + sample[0] = (src[l + 0] & 0xff) >> 0 | (src[l + 1] & 0x03) << 8; + sample[1] = (src[l + 1] & 0xfc) >> 2 | (src[l + 2] & 0x0f) << 6; + sample[2] = (src[l + 2] & 0xf0) >> 4 | (src[l + 3] & 0x3f) << 4; + sample[3] = (src[l + 3] & 0xc0) >> 6 | (src[l + 4] & 0xff) << 2; + + *dst++ = msi3101_int2float(sample[0]); + *dst++ = msi3101_int2float(sample[1]); + *dst++ = msi3101_int2float(sample[2]); + *dst++ = msi3101_int2float(sample[3]); + + /* 4 x 32bit float samples */ + dst_len += 4 * 4; + } + src += 10; + } +#ifdef MSI3101_EXTENSIVE_DEBUG + if (memcmp(src, "\xff\xff\xff\xff", 4) && memcmp(src, "\x00\x00\x00\x00", 4)) + dev_dbg_ratelimited(&s->udev->dev, + "padding %*ph\n", 4, src); +#endif + src += 4; + } + src += 24; + } + +#ifdef MSI3101_EXTENSIVE_DEBUG + /* calculate symbol rate and output it in 10 seconds intervals */ + if ((s->jiffies + msecs_to_jiffies(10000)) <= jiffies) { + unsigned long jiffies_now = jiffies; + unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies); + unsigned int symbols = symbol[i_max - 1] - s->symbol; + s->jiffies = jiffies_now; + s->symbol = symbol[i_max - 1]; + dev_dbg(&s->udev->dev, + "slen=%d symbols=%u msecs=%lu symbolrate=%lu\n", + src_len, symbols, msecs, + symbols * 1000UL / msecs); + } + + /* last received symbol (symbol = symbol + i * 384) */ + s->symbol_received = symbol[i_max - 1] + 384; +#endif + 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 (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 (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++) { + /* Check frame error */ + fstatus = urb->iso_frame_desc[i].status; + if (fstatus) { + dev_dbg(&s->udev->dev, + "frame=%d/%d has error %d skipping\n", + i, urb->number_of_packets, fstatus); + goto skip; + } + + /* Check if that frame contains data */ + flen = urb->iso_frame_desc[i].actual_length; + if (flen == 0) + goto skip; + + iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* Get free framebuffer */ + fbuf = msi3101_get_next_fill_buf(s); + if (fbuf == NULL) { + s->vb_full++; + dev_dbg_ratelimited(&s->udev->dev, + "videobuf is full, %d packets dropped\n", + s->vb_full); + goto skip; + } + + /* fill framebuffer */ +#ifdef MSI3101_CONVERT_IN_URB_HANDLER + vb2_set_plane_payload(&fbuf->vb, 0, + msi3101_convert_stream(s, + vb2_plane_vaddr(&fbuf->vb, 0), iso_buf, flen)); +#else + memcpy(fbuf->data, iso_buf, flen); + fbuf->filled = flen; +#endif + vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); +skip: + ; + } + +handler_end: + i = usb_submit_urb(urb, GFP_ATOMIC); + if (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 < 0) + 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); + 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_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + cap->device_caps = 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 = 32; + *nplanes = 1; + sizes[0] = PAGE_ALIGN(3 * 3072); /* 3 * 768 * 4 */ + dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n", + __func__, *nbuffers, sizes[0]); + return 0; +} + +static int msi3101_buf_init(struct vb2_buffer *vb) +{ + struct msi3101_state *s = vb2_get_drv_priv(vb->vb2_queue); + struct msi3101_frame_buf *fbuf = + container_of(vb, struct msi3101_frame_buf, vb); + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + fbuf->data = vzalloc(ISO_MAX_FRAME_SIZE); + if (fbuf->data == NULL) + return -ENOMEM; + + return 0; +} + +static int msi3101_buf_prepare(struct vb2_buffer *vb) +{ + struct msi3101_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; +} + +/* + * +=========================================================================== + * | 00-1024 | USB packet + * +=========================================================================== + * | 00- 03 | packet address + * +--------------------------------------------------------------------------- + * | 04- 15 | garbage + * +--------------------------------------------------------------------------- + * | 16- 175 | samples + * +--------------------------------------------------------------------------- + * | 176- 179 | padding + * +--------------------------------------------------------------------------- + * | 180- 339 | samples + * +--------------------------------------------------------------------------- + * | 340- 343 | padding + * +--------------------------------------------------------------------------- + * | 344- 503 | samples + * +--------------------------------------------------------------------------- + * | 504- 507 | padding + * +--------------------------------------------------------------------------- + * | 508- 667 | samples + * +--------------------------------------------------------------------------- + * | 668- 671 | padding + * +--------------------------------------------------------------------------- + * | 672- 831 | samples + * +--------------------------------------------------------------------------- + * | 832- 835 | padding + * +--------------------------------------------------------------------------- + * | 836- 995 | samples + * +--------------------------------------------------------------------------- + * | 996- 999 | padding + * +--------------------------------------------------------------------------- + * | 1000-1024 | garbage + * +--------------------------------------------------------------------------- + * + * bytes 4 - 7 could have some meaning? + * padding is "00 00 00 00" or "ff ff ff ff" + * 6 * 16 * 2 * 4 = 768 samples. 768 * 4 = 3072 bytes + */ +#ifdef MSI3101_CONVERT_IN_URB_HANDLER +static int msi3101_buf_finish(struct vb2_buffer *vb) +{ + return 0; +} +#else +static int msi3101_buf_finish(struct vb2_buffer *vb) +{ + struct msi3101_state *s = vb2_get_drv_priv(vb->vb2_queue); + struct msi3101_frame_buf *fbuf = + container_of(vb, struct msi3101_frame_buf, vb); + int ret; + u32 *dst = vb2_plane_vaddr(&fbuf->vb, 0); + ret = msi3101_convert_stream(s, dst, fbuf->data, fbuf->filled); + vb2_set_plane_payload(&fbuf->vb, 0, ret); + return 0; +} +#endif + +static void msi3101_buf_cleanup(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); + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + vfree(buf->data); +} +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 (!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_rcvctrlpipe(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; +}; + +static int msi3101_tuner_write(struct msi3101_state *s, u32 data) +{ + return msi3101_ctrl_msg(s, CMD_WREG, data << 8 | 0x09); +}; + +#define F_REF 24000000 +static int msi3101_set_usb_adc(struct msi3101_state *s) +{ + int ret, div_n, div_m, div_r_out, f_sr; + u32 reg4, reg3; + /* + * FIXME: Synthesizer config is just a educated guess... + * It seems to give reasonable values when N is 5-12 and output + * divider R is 2, which means sampling rates 5-12 Msps in practise. + * + * reg 3 ADC synthesizer config + * [7:0] 0x03, register address + * [8] 1, always + * [9] ? + * [12:10] output divider + * [13] 0 ? + * [14] 0 ? + * [15] increase sr by max fract + * [16:19] N + * [23:20] ? + * [24:31] 0x01 + * + * output divider + * val div + * 0 - (invalid) + * 1 2 + * 2 3 + * 3 4 + * 4 5 + * 5 6 + * 6 7 + * 7 8 + */ + + f_sr = s->ctrl_sampling_rate->val64; + reg3 = 0x01c00303; + + for (div_n = 12; div_n > 5; div_n--) { + if (f_sr >= div_n * 1000000) + break; + } + + reg3 |= div_n << 16; + + for (div_r_out = 2; div_r_out < 8; div_r_out++) { + if (f_sr >= div_n * F_REF / div_r_out / 12) + break; + } + + reg3 |= (div_r_out - 1) << 10; + div_m = f_sr % (div_n * F_REF / div_r_out / 12); + + if (div_m >= 500000) { + reg3 |= 1 << 15; + div_m -= 500000; + } + + reg4 = ((div_m * 0x0ffffful / 500000) << 8) | 0x04; + + dev_dbg(&s->udev->dev, "%s: sr=%d n=%d m=%d r_out=%d reg4=%08x\n", + __func__, f_sr, div_n, div_m, div_r_out, 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, 0x0000a507); + 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_set_tuner(struct msi3101_state *s) +{ + int i, ret, len; + u32 reg, synthstep, thresh, n, frac; + u64 fsynth; + u8 mode, lo_div; + const struct msi3101_gain *gain_lut; + static const struct { + u32 rf; + u8 mode; + u8 lo_div; + } band_lut[] = { + { 30000000, 0x01, 16}, /* AM_MODE1 */ + {108000000, 0x02, 32}, /* VHF_MODE */ + {240000000, 0x04, 16}, /* B3_MODE */ + {960000000, 0x08, 4}, /* B45_MODE */ + {167500000, 0x10, 2}, /* BL_MODE */ + }; + static const struct { + u32 freq; + u8 val; + } 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 rf_freq = s->ctrl_tuner_rf->val64; + + /* + * bandwidth (Hz) + * 200000, 300000, 600000, 1536000, 5000000, 6000000, 7000000, 8000000 + */ + int bandwidth = s->ctrl_tuner_bw->val; + + /* + * intermediate frequency (Hz) + * 0, 450000, 1620000, 2048000 + */ + int if_freq = s->ctrl_tuner_if->val; + + /* + * gain reduction (dB) + * 0 - 102 below 420 MHz + * 0 - 85 above 420 MHz + */ + int gain = s->ctrl_tuner_gain->val; + + dev_dbg(&s->udev->dev, + "%s: rf_freq=%d bandwidth=%d if_freq=%d gain=%d\n", + __func__, rf_freq, bandwidth, if_freq, gain); + + ret = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(band_lut); i++) { + if (rf_freq <= band_lut[i].rf) { + mode = band_lut[i].mode; + lo_div = band_lut[i].lo_div; + break; + } + } + + if (i == ARRAY_SIZE(band_lut)) + goto err; + + for (i = 0; i < ARRAY_SIZE(if_freq_lut); i++) { + if (if_freq == if_freq_lut[i].freq) { + if_freq = if_freq_lut[i].val; + break; + } + } + + if (i == ARRAY_SIZE(if_freq_lut)) + goto err; + + 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)) + goto err; + + #define FSTEP 10000 + #define FREF1 24000000 + fsynth = (rf_freq + 0) * lo_div; + synthstep = FSTEP * lo_div; + thresh = (FREF1 * 4) / synthstep; + n = fsynth / (FREF1 * 4); + frac = thresh * (fsynth % (FREF1 * 4)) / (FREF1 * 4); + + if (thresh > 4095 || n > 63 || frac > 4095) { + dev_dbg(&s->udev->dev, + "%s: synth setup failed rf=%d thresh=%d n=%d frac=%d\n", + __func__, rf_freq, thresh, n, frac); + ret = -EINVAL; + goto err; + } + + ret = msi3101_tuner_write(s, 0x00000e); + ret = msi3101_tuner_write(s, 0x000003); + + reg = 0 << 0; + reg |= mode << 4; + reg |= 1 << 10; + reg |= if_freq << 12; + reg |= bandwidth << 14; + reg |= 0x02 << 17; + reg |= 0x00 << 20; + ret = msi3101_tuner_write(s, reg); + if (ret) + goto err; + + reg = 5 << 0; + reg |= thresh << 4; + reg |= 1 << 19; + reg |= 1 << 21; + ret = msi3101_tuner_write(s, reg); + if (ret) + goto err; + + reg = 2 << 0; + reg |= frac << 4; + reg |= n << 16; + ret = msi3101_tuner_write(s, reg); + if (ret) + goto err; + + if (rf_freq < 120000000) { + gain_lut = msi3101_gain_lut_120; + len = ARRAY_SIZE(msi3101_gain_lut_120); + } else if (rf_freq < 245000000) { + gain_lut = msi3101_gain_lut_245; + len = ARRAY_SIZE(msi3101_gain_lut_120); + } else { + gain_lut = msi3101_gain_lut_1000; + len = ARRAY_SIZE(msi3101_gain_lut_1000); + } + + for (i = 0; i < len; i++) { + if (gain_lut[i].tot >= gain) + break; + } + + if (i == len) + goto err; + + dev_dbg(&s->udev->dev, + "%s: gain tot=%d baseband=%d lna=%d mixer=%d\n", + __func__, gain_lut[i].tot, gain_lut[i].baseband, + gain_lut[i].lna, gain_lut[i].mixer); + + reg = 1 << 0; + reg |= gain_lut[i].baseband << 4; + reg |= 0 << 10; + reg |= gain_lut[i].mixer << 12; + reg |= gain_lut[i].lna << 13; + reg |= 4 << 14; + reg |= 0 << 17; + ret = msi3101_tuner_write(s, reg); + if (ret) + goto err; + + reg = 6 << 0; + reg |= 63 << 4; + reg |= 4095 << 10; + ret = msi3101_tuner_write(s, reg); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&s->udev->dev, "%s: failed %d\n", __func__, ret); + 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; + + 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 int msi3101_stop_streaming(struct vb2_queue *vq) +{ + struct msi3101_state *s = vb2_get_drv_priv(vq); + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + if (mutex_lock_interruptible(&s->v4l2_lock)) + return -ERESTARTSYS; + + msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0); + + if (s->udev) + msi3101_isoc_cleanup(s); + + msi3101_cleanup_queued_bufs(s); + mutex_unlock(&s->v4l2_lock); + + return 0; +} + +static struct vb2_ops msi3101_vb2_ops = { + .queue_setup = msi3101_queue_setup, + .buf_init = msi3101_buf_init, + .buf_prepare = msi3101_buf_prepare, + .buf_finish = msi3101_buf_finish, + .buf_cleanup = msi3101_buf_cleanup, + .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_input(struct file *file, void *fh, struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + + strlcpy(i->name, "SDR data", sizeof(i->name)); + i->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int msi3101_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int msi3101_s_input(struct file *file, void *fh, unsigned int i) +{ + return i ? -EINVAL : 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *v) +{ + struct msi3101_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) +{ + struct msi3101_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s:\n", __func__); + + strcpy(v->name, "SDR RX"); + v->capability = V4L2_TUNER_CAP_LOW; + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct msi3101_state *s = video_drvdata(file); + dev_dbg(&s->udev->dev, "%s: frequency=%u Hz (%d)\n", + __func__, f->frequency * 625U / 10U, f->frequency); + + return v4l2_ctrl_s_ctrl_int64(s->ctrl_tuner_rf, + f->frequency * 625U / 10U); +} + +const struct v4l2_ioctl_ops msi3101_ioctl_ops = { + .vidioc_querycap = msi3101_querycap, + + .vidioc_enum_input = msi3101_enum_input, + .vidioc_g_input = msi3101_g_input, + .vidioc_s_input = msi3101_s_input, + + .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 = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_s_frequency = vidioc_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 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 int msi3101_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct msi3101_state *s = + container_of(ctrl->handler, struct msi3101_state, + ctrl_handler); + 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, + ctrl->minimum, ctrl->maximum, ctrl->step); + + switch (ctrl->id) { + case MSI3101_CID_SAMPLING_RATE: + case MSI3101_CID_SAMPLING_RESOLUTION: + ret = 0; + break; + case MSI3101_CID_TUNER_RF: + case MSI3101_CID_TUNER_BW: + case MSI3101_CID_TUNER_IF: + case MSI3101_CID_TUNER_GAIN: + ret = msi3101_set_tuner(s); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ctrl_ops msi3101_ctrl_ops = { + .s_ctrl = msi3101_s_ctrl, +}; + +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->ctrl_handler); + v4l2_device_unregister(&s->v4l2_dev); + kfree(s); +} + +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; + int ret; + static const struct v4l2_ctrl_config ctrl_sampling_rate = { + .ops = &msi3101_ctrl_ops, + .id = MSI3101_CID_SAMPLING_RATE, + .type = V4L2_CTRL_TYPE_INTEGER64, + .name = "Sampling Rate", + .min = 500000, + .max = 12000000, + .def = 2048000, + .step = 1, + }; + static const struct v4l2_ctrl_config ctrl_sampling_resolution = { + .ops = &msi3101_ctrl_ops, + .id = MSI3101_CID_SAMPLING_RESOLUTION, + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = V4L2_CTRL_FLAG_INACTIVE, + .name = "Sampling Resolution", + .min = 10, + .max = 10, + .def = 10, + .step = 1, + }; + static const struct v4l2_ctrl_config ctrl_tuner_rf = { + .ops = &msi3101_ctrl_ops, + .id = MSI3101_CID_TUNER_RF, + .type = V4L2_CTRL_TYPE_INTEGER64, + .name = "Tuner RF", + .min = 40000000, + .max = 2000000000, + .def = 100000000, + .step = 1, + }; + static const struct v4l2_ctrl_config ctrl_tuner_bw = { + .ops = &msi3101_ctrl_ops, + .id = MSI3101_CID_TUNER_BW, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Tuner BW", + .min = 200000, + .max = 8000000, + .def = 600000, + .step = 1, + }; + static const struct v4l2_ctrl_config ctrl_tuner_if = { + .ops = &msi3101_ctrl_ops, + .id = MSI3101_CID_TUNER_IF, + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = V4L2_CTRL_FLAG_INACTIVE, + .name = "Tuner IF", + .min = 0, + .max = 2048000, + .def = 0, + .step = 1, + }; + static const struct v4l2_ctrl_config ctrl_tuner_gain = { + .ops = &msi3101_ctrl_ops, + .id = MSI3101_CID_TUNER_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Tuner Gain", + .min = 0, + .max = 102, + .def = 0, + .step = 1, + }; + + 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; + + /* Init videobuf2 queue structure */ + s->vb_queue.type = V4L2_BUF_TYPE_VIDEO_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_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&s->vb_queue); + if (ret < 0) { + 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; + set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev.flags); + video_set_drvdata(&s->vdev, s); + + /* Register controls */ + v4l2_ctrl_handler_init(&s->ctrl_handler, 6); + s->ctrl_sampling_rate = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_sampling_rate, NULL); + v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_sampling_resolution, NULL); + s->ctrl_tuner_rf = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_tuner_rf, NULL); + s->ctrl_tuner_bw = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_tuner_bw, NULL); + s->ctrl_tuner_if = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_tuner_if, NULL); + s->ctrl_tuner_gain = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_tuner_gain, NULL); + if (s->ctrl_handler.error) { + ret = s->ctrl_handler.error; + dev_err(&s->udev->dev, "Could not initialize controls\n"); + goto err_free_controls; + } + + /* 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_controls; + } + + s->v4l2_dev.ctrl_handler = &s->ctrl_handler; + s->vdev.v4l2_dev = &s->v4l2_dev; + s->vdev.lock = &s->v4l2_lock; + + ret = video_register_device(&s->vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + 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_unregister_v4l2_dev: + v4l2_device_unregister(&s->v4l2_dev); +err_free_controls: + v4l2_ctrl_handler_free(&s->ctrl_handler); +err_free_mem: + kfree(s); + return ret; +} + +/* USB device ID list */ +static struct usb_device_id msi3101_id_table[] = { + { USB_DEVICE(0x1df7, 0x2500) }, + { } +}; +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 34599b9b5305eb70644d54fe22e6df4aa5fef65c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 8 Jul 2013 22:31:47 -0300 Subject: [media] msi3101: sample is correct term for sample Just fix wrong term name. No functional changes. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 46fdb6c..87896ee 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -405,8 +405,8 @@ struct msi3101_state { struct v4l2_ctrl *ctrl_tuner_if; struct v4l2_ctrl *ctrl_tuner_gain; - u32 symbol_received; /* for track lost packets */ - u32 symbol; /* for symbol rate calc */ + u32 next_sample; /* for track lost packets */ + u32 sample; /* for sample rate calc */ unsigned long jiffies; }; @@ -476,18 +476,18 @@ static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, int i, j, k, l, i_max, dst_len = 0; u16 sample[4]; #ifdef MSI3101_EXTENSIVE_DEBUG - u32 symbol[3]; + u32 sample_num[3]; #endif /* There could be 1-3 1024 bytes URB frames */ i_max = src_len / 1024; for (i = 0; i < i_max; i++) { #ifdef MSI3101_EXTENSIVE_DEBUG - symbol[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->symbol_received != symbol[0]) { + 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(&s->udev->dev, - "%d symbols lost, %d %08x:%08x\n", - symbol[0] - s->symbol_received, - src_len, s->symbol_received, symbol[0]); + "%d samples lost, %d %08x:%08x\n", + sample_num[0] - s->next_sample, + src_len, s->next_sample, sample_num[0]); } #endif src += 16; @@ -520,21 +520,21 @@ static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, } #ifdef MSI3101_EXTENSIVE_DEBUG - /* calculate symbol rate and output it in 10 seconds intervals */ + /* calculate samping rate and output it in 10 seconds intervals */ if ((s->jiffies + msecs_to_jiffies(10000)) <= jiffies) { unsigned long jiffies_now = jiffies; unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies); - unsigned int symbols = symbol[i_max - 1] - s->symbol; + unsigned int samples = sample_num[i_max - 1] - s->sample; s->jiffies = jiffies_now; - s->symbol = symbol[i_max - 1]; + s->sample = sample_num[i_max - 1]; dev_dbg(&s->udev->dev, - "slen=%d symbols=%u msecs=%lu symbolrate=%lu\n", - src_len, symbols, msecs, - symbols * 1000UL / msecs); + "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", + src_len, samples, msecs, + samples * 1000UL / msecs); } - /* last received symbol (symbol = symbol + i * 384) */ - s->symbol_received = symbol[i_max - 1] + 384; + /* next sample (sample = sample + i * 384) */ + s->next_sample = sample_num[i_max - 1] + 384; #endif return dst_len; } -- cgit v0.10.2 From b15208573d926e2fdf46a753ae167235d73e1bf6 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 9 Jul 2013 02:44:56 -0300 Subject: [media] msi3101: fix sampling rate calculation These calculations seem to give 100% correct results. Calculation formulas could be still a little bit wrong as I have no knowledge what kind of dividers, multipliers and VCO limits there really is. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 87896ee..4de4f50 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -960,16 +960,14 @@ static int msi3101_tuner_write(struct msi3101_state *s, u32 data) }; #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; + int ret, div_n, div_m, div_r_out, f_sr, f_vco; u32 reg4, reg3; /* - * FIXME: Synthesizer config is just a educated guess... - * It seems to give reasonable values when N is 5-12 and output - * divider R is 2, which means sampling rates 5-12 Msps in practise. + * Synthesizer config is just a educated guess... * - * reg 3 ADC synthesizer config * [7:0] 0x03, register address * [8] 1, always * [9] ? @@ -984,42 +982,48 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) * output divider * val div * 0 - (invalid) - * 1 2 - * 2 3 - * 3 4 - * 4 5 - * 5 6 - * 6 7 - * 7 8 + * 1 4 + * 2 6 + * 3 8 + * 4 10 + * 5 12 + * 6 14 + * 7 16 + * + * VCO 202000000 - 720000000++ */ f_sr = s->ctrl_sampling_rate->val64; reg3 = 0x01c00303; - for (div_n = 12; div_n > 5; div_n--) { - if (f_sr >= div_n * 1000000) + 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; } - reg3 |= div_n << 16; - - for (div_r_out = 2; div_r_out < 8; div_r_out++) { - if (f_sr >= div_n * F_REF / div_r_out / 12) - break; - } + div_n = f_vco / (F_REF * DIV_R_IN); + div_m = f_vco % (F_REF * DIV_R_IN); - reg3 |= (div_r_out - 1) << 10; - div_m = f_sr % (div_n * F_REF / div_r_out / 12); + reg3 |= div_n << 16; + reg3 |= (div_r_out / 2 - 1) << 10; + reg4 = 0x0ffffful * div_m / F_REF; - if (div_m >= 500000) { + if (reg4 >= 0x0ffffful) { + dev_dbg(&s->udev->dev, + "%s: extending fractional part value %08x\n", + __func__, reg4); + reg4 -= 0x0ffffful; reg3 |= 1 << 15; - div_m -= 500000; } - reg4 = ((div_m * 0x0ffffful / 500000) << 8) | 0x04; + reg4 = (reg4 << 8) | 0x04; - dev_dbg(&s->udev->dev, "%s: sr=%d n=%d m=%d r_out=%d reg4=%08x\n", - __func__, f_sr, div_n, div_m, div_r_out, reg4); + dev_dbg(&s->udev->dev, + "%s: f_sr=%d f_vco=%d div_n=%d div_m=%d div_r_out=%d reg4=%08x\n", + __func__, f_sr, f_vco, div_n, div_m, div_r_out, reg4); ret = msi3101_ctrl_msg(s, CMD_WREG, 0x00608008); if (ret) -- cgit v0.10.2 From 3673b35cfaed18a4f6ef2f302333a5e12dbe3d25 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 9 Jul 2013 16:19:15 -0300 Subject: [media] msi3101: add sampling mode control Whilst Quadrature Sampling is most common sampling technique used in radio technology there is others too. Add control user could select sampling mode. We currently support only quadrature sampling. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 4de4f50..b6a8939 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -361,8 +361,9 @@ static const struct msi3101_gain msi3101_gain_lut_1000[] = { #define MAX_ISOC_ERRORS 20 -#define MSI3101_CID_SAMPLING_RATE ((V4L2_CID_USER_BASE | 0xf000) + 0) -#define MSI3101_CID_SAMPLING_RESOLUTION ((V4L2_CID_USER_BASE | 0xf000) + 1) +#define MSI3101_CID_SAMPLING_MODE ((V4L2_CID_USER_BASE | 0xf000) + 0) +#define MSI3101_CID_SAMPLING_RATE ((V4L2_CID_USER_BASE | 0xf000) + 1) +#define MSI3101_CID_SAMPLING_RESOLUTION ((V4L2_CID_USER_BASE | 0xf000) + 2) #define MSI3101_CID_TUNER_RF ((V4L2_CID_USER_BASE | 0xf000) + 10) #define MSI3101_CID_TUNER_BW ((V4L2_CID_USER_BASE | 0xf000) + 11) #define MSI3101_CID_TUNER_IF ((V4L2_CID_USER_BASE | 0xf000) + 12) @@ -1418,6 +1419,7 @@ static int msi3101_s_ctrl(struct v4l2_ctrl *ctrl) ctrl->minimum, ctrl->maximum, ctrl->step); switch (ctrl->id) { + case MSI3101_CID_SAMPLING_MODE: case MSI3101_CID_SAMPLING_RATE: case MSI3101_CID_SAMPLING_RESOLUTION: ret = 0; @@ -1455,6 +1457,18 @@ static int msi3101_probe(struct usb_interface *intf, struct usb_device *udev = interface_to_usbdev(intf); struct msi3101_state *s = NULL; int ret; + static const char * const ctrl_sampling_mode_qmenu_strings[] = { + "Quadrature Sampling", + NULL, + }; + static const struct v4l2_ctrl_config ctrl_sampling_mode = { + .ops = &msi3101_ctrl_ops, + .id = MSI3101_CID_SAMPLING_MODE, + .type = V4L2_CTRL_TYPE_MENU, + .flags = V4L2_CTRL_FLAG_INACTIVE, + .name = "Sampling Mode", + .qmenu = ctrl_sampling_mode_qmenu_strings, + }; static const struct v4l2_ctrl_config ctrl_sampling_rate = { .ops = &msi3101_ctrl_ops, .id = MSI3101_CID_SAMPLING_RATE, @@ -1553,7 +1567,8 @@ static int msi3101_probe(struct usb_interface *intf, video_set_drvdata(&s->vdev, s); /* Register controls */ - v4l2_ctrl_handler_init(&s->ctrl_handler, 6); + v4l2_ctrl_handler_init(&s->ctrl_handler, 7); + v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_sampling_mode, NULL); s->ctrl_sampling_rate = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_sampling_rate, NULL); v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_sampling_resolution, NULL); s->ctrl_tuner_rf = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_tuner_rf, NULL); -- cgit v0.10.2 From 020046812f2f53fc952f6ea56accf1599d4b3ba0 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 10 Jul 2013 21:59:58 -0300 Subject: [media] msi3101: enhance sampling results It looks like there is some extra data carried to enhance sampling results. When you tune to some some weak/empty channel those bits are always zeroes. When you tune to some channel where is very strong signals those bits are all zeroes. Examining those 32-bits reveals shortly there is 16 pieces of 2-bit numbers. Number seen are 0, 1 and 3 - for some reason 2 is not used. I used that number to shift bits given amount to left, "increasing" sampling resolution by 3-bits. It may be wrong, but after some testing it still provides better signal. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index b6a8939..c73f1d9 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -409,6 +409,7 @@ struct msi3101_state { u32 next_sample; /* for track lost packets */ u32 sample; /* for sample rate calc */ unsigned long jiffies; + unsigned int sample_ctrl_bit[4]; }; /* Private functions */ @@ -430,6 +431,51 @@ leave: } /* + * +=========================================================================== + * | 00-1024 | USB packet + * +=========================================================================== + * | 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-1024 | 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 + */ + +/* * Converts signed 10-bit integer into 32-bit IEEE floating point * representation. * Will be exact from 0 to 2^24. Above that, we round towards zero @@ -438,20 +484,24 @@ leave: */ #define I2F_FRAC_BITS 23 #define I2F_MASK ((1 << I2F_FRAC_BITS) - 1) -static uint32_t msi3101_int2float(uint32_t x) +static u32 msi3101_convert_sample(struct msi3101_state *s, u16 x, int shift) { - uint32_t msb, exponent, fraction, sign; + u32 msb, exponent, fraction, sign; + s->sample_ctrl_bit[shift]++; /* Zero is special */ if (!x) return 0; - /* Negative / positive value */ - if (x & 0x200) { + /* Convert 10-bit two's complement to 13-bit */ + if (x & (1 << 9)) { + x |= ~0U << 10; /* set all the rest bits to one */ + x <<= shift; x = -x; - x &= 0x3ff; + x &= 0xfff; /* result is 12 bit ... + sign */ sign = 1 << 31; } else { + x <<= shift; sign = 0 << 31; } @@ -476,6 +526,7 @@ static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, { int i, j, k, l, i_max, dst_len = 0; u16 sample[4]; + u32 bits; #ifdef MSI3101_EXTENSIVE_DEBUG u32 sample_num[3]; #endif @@ -493,6 +544,7 @@ static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, #endif src += 16; for (j = 0; j < 6; j++) { + bits = src[160 + 3] << 24 | src[160 + 2] << 16 | src[160 + 1] << 8 | src[160 + 0] << 0; for (k = 0; k < 16; k++) { for (l = 0; l < 10; l += 5) { sample[0] = (src[l + 0] & 0xff) >> 0 | (src[l + 1] & 0x03) << 8; @@ -500,10 +552,10 @@ static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, sample[2] = (src[l + 2] & 0xf0) >> 4 | (src[l + 3] & 0x3f) << 4; sample[3] = (src[l + 3] & 0xc0) >> 6 | (src[l + 4] & 0xff) << 2; - *dst++ = msi3101_int2float(sample[0]); - *dst++ = msi3101_int2float(sample[1]); - *dst++ = msi3101_int2float(sample[2]); - *dst++ = msi3101_int2float(sample[3]); + *dst++ = msi3101_convert_sample(s, sample[0], (bits >> (2 * k)) & 0x3); + *dst++ = msi3101_convert_sample(s, sample[1], (bits >> (2 * k)) & 0x3); + *dst++ = msi3101_convert_sample(s, sample[2], (bits >> (2 * k)) & 0x3); + *dst++ = msi3101_convert_sample(s, sample[3], (bits >> (2 * k)) & 0x3); /* 4 x 32bit float samples */ dst_len += 4 * 4; @@ -511,9 +563,8 @@ static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, src += 10; } #ifdef MSI3101_EXTENSIVE_DEBUG - if (memcmp(src, "\xff\xff\xff\xff", 4) && memcmp(src, "\x00\x00\x00\x00", 4)) - dev_dbg_ratelimited(&s->udev->dev, - "padding %*ph\n", 4, src); + dev_dbg_ratelimited(&s->udev->dev, + "sample control bits %08x\n", bits); #endif src += 4; } @@ -529,9 +580,11 @@ static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, s->jiffies = jiffies_now; s->sample = sample_num[i_max - 1]; dev_dbg(&s->udev->dev, - "slen=%d samples=%u msecs=%lu sampling rate=%lu\n", + "slen=%d samples=%u msecs=%lu sampling rate=%lu bits=%d.%d.%d.%d\n", src_len, samples, msecs, - samples * 1000UL / 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) */ @@ -833,45 +886,6 @@ static int msi3101_buf_prepare(struct vb2_buffer *vb) return 0; } -/* - * +=========================================================================== - * | 00-1024 | USB packet - * +=========================================================================== - * | 00- 03 | packet address - * +--------------------------------------------------------------------------- - * | 04- 15 | garbage - * +--------------------------------------------------------------------------- - * | 16- 175 | samples - * +--------------------------------------------------------------------------- - * | 176- 179 | padding - * +--------------------------------------------------------------------------- - * | 180- 339 | samples - * +--------------------------------------------------------------------------- - * | 340- 343 | padding - * +--------------------------------------------------------------------------- - * | 344- 503 | samples - * +--------------------------------------------------------------------------- - * | 504- 507 | padding - * +--------------------------------------------------------------------------- - * | 508- 667 | samples - * +--------------------------------------------------------------------------- - * | 668- 671 | padding - * +--------------------------------------------------------------------------- - * | 672- 831 | samples - * +--------------------------------------------------------------------------- - * | 832- 835 | padding - * +--------------------------------------------------------------------------- - * | 836- 995 | samples - * +--------------------------------------------------------------------------- - * | 996- 999 | padding - * +--------------------------------------------------------------------------- - * | 1000-1024 | garbage - * +--------------------------------------------------------------------------- - * - * bytes 4 - 7 could have some meaning? - * padding is "00 00 00 00" or "ff ff ff ff" - * 6 * 16 * 2 * 4 = 768 samples. 768 * 4 = 3072 bytes - */ #ifdef MSI3101_CONVERT_IN_URB_HANDLER static int msi3101_buf_finish(struct vb2_buffer *vb) { -- cgit v0.10.2 From d0fadf40338fc551f4bbb82fc4c223d2a8de2c1d Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 11 Jul 2013 12:19:32 -0300 Subject: [media] msi3101: fix stream re-start halt Restarting stream fails quite often. Small delay is between urb killing and stream stop command - likely to give harware some time to process killed urbs. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index c73f1d9..2180bf8 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -959,7 +959,7 @@ static int msi3101_ctrl_msg(struct msi3101_state *s, u8 cmd, u32 data) msi3101_dbg_usb_control_msg(s->udev, request, requesttype, value, index, NULL, 0); - ret = usb_control_msg(s->udev, usb_rcvctrlpipe(s->udev, 0), + ret = usb_control_msg(s->udev, usb_sndctrlpipe(s->udev, 0), request, requesttype, value, index, NULL, 0, 2000); if (ret) @@ -1300,12 +1300,15 @@ static int msi3101_stop_streaming(struct vb2_queue *vq) if (mutex_lock_interruptible(&s->v4l2_lock)) return -ERESTARTSYS; - msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0); - if (s->udev) msi3101_isoc_cleanup(s); msi3101_cleanup_queued_bufs(s); + + /* according to tests, at least 700us delay is required */ + msleep(20); + msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0); + mutex_unlock(&s->v4l2_lock); return 0; -- cgit v0.10.2 From 42fc5b42300b0795d418ba567ae6a7fb4504d022 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 17 Jul 2013 15:05:27 -0300 Subject: [media] msi3101: add 2040:d300 Hauppauge WinTV 133559 LF It is based Mirics MSi3101 reference design and will just work without any changes. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 2180bf8..152415a 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -1635,6 +1635,7 @@ err_free_mem: /* USB device ID list */ static struct usb_device_id msi3101_id_table[] = { { USB_DEVICE(0x1df7, 0x2500) }, + { USB_DEVICE(0x2040, 0xd300) }, /* Hauppauge WinTV 133559 LF */ { } }; MODULE_DEVICE_TABLE(usb, msi3101_id_table); -- cgit v0.10.2 From 6450e5091c6dfc12afc164e8225b7677b1b1a422 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 31 Jul 2013 19:58:12 -0300 Subject: [media] msi3101: add debug dump for unknown stream data Dump all unknown 'garbage' data - maybe we will discover someday if there is something rational... Also fix comment in USB frame description. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 152415a..2b73fc1 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -432,7 +432,7 @@ leave: /* * +=========================================================================== - * | 00-1024 | USB packet + * | 00-1023 | USB packet * +=========================================================================== * | 00- 03 | sequence number of first sample in that USB packet * +--------------------------------------------------------------------------- @@ -462,7 +462,7 @@ leave: * +--------------------------------------------------------------------------- * | 996- 999 | control bits for previous samples * +--------------------------------------------------------------------------- - * | 1000-1024 | garbage + * | 1000-1023 | garbage * +--------------------------------------------------------------------------- * * Bytes 4 - 7 could have some meaning? @@ -522,7 +522,7 @@ static u32 msi3101_convert_sample(struct msi3101_state *s, u16 x, int shift) #define MSI3101_CONVERT_IN_URB_HANDLER #define MSI3101_EXTENSIVE_DEBUG static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, - const u8 *src, unsigned int src_len) + u8 *src, unsigned int src_len) { int i, j, k, l, i_max, dst_len = 0; u16 sample[4]; @@ -541,6 +541,15 @@ static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, 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]); + memset(&src[4], 0, 12); + memset(&src[1000], 0, 24); #endif src += 16; for (j = 0; j < 6; j++) { -- cgit v0.10.2 From 22ca680e02aceb88d939b1eb1bbe3e94b71bf555 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 31 Jul 2013 21:48:51 -0300 Subject: [media] msi3101: correct ADC sampling rate calc a little bit No need to compare numbers, we could just store that fractional value MSB directly. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 2b73fc1..04bbbdf 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -987,7 +987,7 @@ static int msi3101_tuner_write(struct msi3101_state *s, u32 data) #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; + int ret, div_n, div_m, div_r_out, f_sr, f_vco, fract; u32 reg4, reg3; /* * Synthesizer config is just a educated guess... @@ -998,7 +998,7 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) * [12:10] output divider * [13] 0 ? * [14] 0 ? - * [15] increase sr by max fract + * [15] fractional MSB, bit 20 * [16:19] N * [23:20] ? * [24:31] 0x01 @@ -1019,6 +1019,7 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) f_sr = s->ctrl_sampling_rate->val64; reg3 = 0x01c00303; + reg4 = 0x00000004; for (div_r_out = 4; div_r_out < 16; div_r_out += 2) { f_vco = f_sr * div_r_out * 12; @@ -1030,24 +1031,16 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) 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; - reg4 = 0x0ffffful * div_m / F_REF; - - if (reg4 >= 0x0ffffful) { - dev_dbg(&s->udev->dev, - "%s: extending fractional part value %08x\n", - __func__, reg4); - reg4 -= 0x0ffffful; - reg3 |= 1 << 15; - } - - reg4 = (reg4 << 8) | 0x04; + 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 reg4=%08x\n", - __func__, f_sr, f_vco, div_n, div_m, div_r_out, reg4); + "%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) -- cgit v0.10.2 From 23df427e3333882beea2e65d27fd2a581e37fae4 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 2 Aug 2013 21:49:49 -0300 Subject: [media] msi3101: improve tuner synth calc step size Allow stepless synthesizer configuration. With that change we lose precision a little bit, as it is now between +-500Hz from the target. It could be better but on that case calculation algorithm goes more complex and atm there is more important things to do. Two approach to improve which comes to my mind are: 1) select and use biggest suitable step 2) use greatest common divisor algo to find divisor for thresh & frac when possible to avoid rounding errors, which is root of cause of current +-500Hz inaccuracy. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 04bbbdf..4ff6030 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -1079,9 +1079,10 @@ err: static int msi3101_set_tuner(struct msi3101_state *s) { - int i, ret, len; - u32 reg, synthstep, thresh, n, frac; - u64 fsynth; + int ret, i, len; + unsigned int n, m, thresh, frac, vco_step, tmp; + u32 reg; + u64 f_vco; u8 mode, lo_div; const struct msi3101_gain *gain_lut; static const struct { @@ -1176,21 +1177,30 @@ static int msi3101_set_tuner(struct msi3101_state *s) if (i == ARRAY_SIZE(bandwidth_lut)) goto err; - #define FSTEP 10000 - #define FREF1 24000000 - fsynth = (rf_freq + 0) * lo_div; - synthstep = FSTEP * lo_div; - thresh = (FREF1 * 4) / synthstep; - n = fsynth / (FREF1 * 4); - frac = thresh * (fsynth % (FREF1 * 4)) / (FREF1 * 4); +#define F_OUT_STEP 1 +#define R_REF 4 +#define F_IF 0 + f_vco = (rf_freq + F_IF) * lo_div; + n = f_vco / (F_REF * R_REF); + m = f_vco % (F_REF * R_REF); - if (thresh > 4095 || n > 63 || frac > 4095) { - dev_dbg(&s->udev->dev, - "%s: synth setup failed rf=%d thresh=%d n=%d frac=%d\n", - __func__, rf_freq, thresh, n, frac); - ret = -EINVAL; - goto err; - } + vco_step = F_OUT_STEP * lo_div; + thresh = (F_REF * R_REF) / vco_step; + frac = 1ul * thresh * m / (F_REF * R_REF); + + /* Divide to reg max. After that RF resolution will be +-500Hz. */ + 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->udev->dev, + "%s: rf=%u:%u n=%d thresh=%d frac=%d\n", + __func__, rf_freq, tmp, n, thresh, frac); ret = msi3101_tuner_write(s, 0x00000e); ret = msi3101_tuner_write(s, 0x000003); -- cgit v0.10.2 From 554cbfbe3b99d25caa3ab794a8619ccdea2b2935 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 5 Aug 2013 20:43:36 -0300 Subject: [media] msi3101: add support for stream format "252" I+Q per frame That one seems to have 14-bit ADC resolution, wow! It is now used when sampling rate is below 6 Msps. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 4ff6030..a937d00 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -397,6 +397,8 @@ 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, u32 *dst, u8 *src, + unsigned int src_len); /* Controls */ struct v4l2_ctrl_handler ctrl_handler; @@ -484,7 +486,7 @@ leave: */ #define I2F_FRAC_BITS 23 #define I2F_MASK ((1 << I2F_FRAC_BITS) - 1) -static u32 msi3101_convert_sample(struct msi3101_state *s, u16 x, int shift) +static u32 msi3101_convert_sample_384(struct msi3101_state *s, u16 x, int shift) { u32 msb, exponent, fraction, sign; s->sample_ctrl_bit[shift]++; @@ -521,7 +523,7 @@ static u32 msi3101_convert_sample(struct msi3101_state *s, u16 x, int shift) #define MSI3101_CONVERT_IN_URB_HANDLER #define MSI3101_EXTENSIVE_DEBUG -static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, +static int msi3101_convert_stream_384(struct msi3101_state *s, u32 *dst, u8 *src, unsigned int src_len) { int i, j, k, l, i_max, dst_len = 0; @@ -561,10 +563,10 @@ static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, sample[2] = (src[l + 2] & 0xf0) >> 4 | (src[l + 3] & 0x3f) << 4; sample[3] = (src[l + 3] & 0xc0) >> 6 | (src[l + 4] & 0xff) << 2; - *dst++ = msi3101_convert_sample(s, sample[0], (bits >> (2 * k)) & 0x3); - *dst++ = msi3101_convert_sample(s, sample[1], (bits >> (2 * k)) & 0x3); - *dst++ = msi3101_convert_sample(s, sample[2], (bits >> (2 * k)) & 0x3); - *dst++ = msi3101_convert_sample(s, sample[3], (bits >> (2 * k)) & 0x3); + *dst++ = msi3101_convert_sample_384(s, sample[0], (bits >> (2 * k)) & 0x3); + *dst++ = msi3101_convert_sample_384(s, sample[1], (bits >> (2 * k)) & 0x3); + *dst++ = msi3101_convert_sample_384(s, sample[2], (bits >> (2 * k)) & 0x3); + *dst++ = msi3101_convert_sample_384(s, sample[3], (bits >> (2 * k)) & 0x3); /* 4 x 32bit float samples */ dst_len += 4 * 4; @@ -603,6 +605,103 @@ static int msi3101_convert_stream(struct msi3101_state *s, u32 *dst, } /* + * Converts signed 14-bit integer into 32-bit IEEE floating point + * representation. + * Will be exact from 0 to 2^24. Above that, we round towards zero + * as the fractional bits will not fit in a float. (It would be better to + * round towards even as the fpu does, but that is slower.) + */ +#define I2F_FRAC_BITS 23 +#define I2F_MASK ((1 << I2F_FRAC_BITS) - 1) +static u32 msi3101_convert_sample_252(struct msi3101_state *s, u16 x) +{ + u32 msb, exponent, fraction, sign; + + /* Zero is special */ + if (!x) + return 0; + + /* Negative / positive value */ + if (x & (1 << 13)) { + x = -x; + x &= 0x1fff; /* result is 13 bit ... + sign */ + sign = 1 << 31; + } else { + sign = 0 << 31; + } + + /* Get location of the most significant bit */ + msb = __fls(x); + + /* + * Use a rotate instead of a shift because that works both leftwards + * and rightwards due to the mod(32) behaviour. This means we don't + * need to check to see if we are above 2^24 or not. + */ + fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK; + exponent = (127 + msb) << I2F_FRAC_BITS; + + return (fraction + exponent) | sign; +} + +static int msi3101_convert_stream_252(struct msi3101_state *s, u32 *dst, + u8 *src, unsigned int src_len) +{ + int i, j, i_max, dst_len = 0; + u16 sample[2]; + 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]); + + src += 16; + for (j = 0; j < 1008; j += 4) { + sample[0] = src[j + 0] >> 0 | src[j + 1] << 8; + sample[1] = src[j + 2] >> 0 | src[j + 3] << 8; + + *dst++ = msi3101_convert_sample_252(s, sample[0]); + *dst++ = msi3101_convert_sample_252(s, sample[1]); + } + /* 252 x I+Q 32bit float samples */ + dst_len += 252 * 2 * 4; + src += 1008; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if ((s->jiffies + msecs_to_jiffies(10000)) <= jiffies) { + unsigned long jiffies_now = jiffies; + unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies); + unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies = 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; +} + +/* * 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. */ @@ -636,6 +735,8 @@ static void msi3101_isoc_handler(struct urb *urb) /* Compact data */ for (i = 0; i < urb->number_of_packets; i++) { + void *ptr; + /* Check frame error */ fstatus = urb->iso_frame_desc[i].status; if (fstatus) { @@ -664,9 +765,9 @@ static void msi3101_isoc_handler(struct urb *urb) /* fill framebuffer */ #ifdef MSI3101_CONVERT_IN_URB_HANDLER - vb2_set_plane_payload(&fbuf->vb, 0, - msi3101_convert_stream(s, - vb2_plane_vaddr(&fbuf->vb, 0), iso_buf, flen)); + ptr = vb2_plane_vaddr(&fbuf->vb, 0); + flen = s->convert_stream(s, ptr, iso_buf, flen); + vb2_set_plane_payload(&fbuf->vb, 0, flen); #else memcpy(fbuf->data, iso_buf, flen); fbuf->filled = flen; @@ -908,7 +1009,7 @@ static int msi3101_buf_finish(struct vb2_buffer *vb) container_of(vb, struct msi3101_frame_buf, vb); int ret; u32 *dst = vb2_plane_vaddr(&fbuf->vb, 0); - ret = msi3101_convert_stream(s, dst, fbuf->data, fbuf->filled); + ret = msi3101_convert_stream_384(s, dst, fbuf->data, fbuf->filled); vb2_set_plane_payload(&fbuf->vb, 0, ret); return 0; } @@ -988,7 +1089,19 @@ static int msi3101_tuner_write(struct msi3101_state *s, u32 data) 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 reg4, reg3; + u32 reg3, reg4, reg7; + + f_sr = s->ctrl_sampling_rate->val64; + + /* select stream format */ + if (f_sr < 6000000) { + s->convert_stream = msi3101_convert_stream_252; + reg7 = 0x00009407; + } else { + s->convert_stream = msi3101_convert_stream_384; + reg7 = 0x0000a507; + } + /* * Synthesizer config is just a educated guess... * @@ -1016,8 +1129,6 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) * * VCO 202000000 - 720000000++ */ - - f_sr = s->ctrl_sampling_rate->val64; reg3 = 0x01c00303; reg4 = 0x00000004; @@ -1062,7 +1173,7 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) if (ret) goto err; - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x0000a507); + ret = msi3101_ctrl_msg(s, CMD_WREG, reg7); if (ret) goto err; -- cgit v0.10.2 From c5a431d02cdcafaf12edff770c294c5fbadcdf54 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 5 Aug 2013 21:33:31 -0300 Subject: [media] msi3101: init bits 23:20 on PLL register It is not sure what is meaning of that number, but it is better to try do as Windows driver. It seems to have small effect for signal when looking FM FFT. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index a937d00..93168db 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -1129,9 +1129,19 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) * * VCO 202000000 - 720000000++ */ - reg3 = 0x01c00303; + 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", -- cgit v0.10.2 From 35111f5bc94e790a2ac236dcaf3009c14c5e9f5d Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 7 Aug 2013 00:02:40 -0300 Subject: [media] msi3101: fix overflow in freq setting Higher frequencies were not possible to set correctly as that value overflows. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 93168db..56f9757 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -1507,11 +1507,11 @@ static int vidioc_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { struct msi3101_state *s = video_drvdata(file); - dev_dbg(&s->udev->dev, "%s: frequency=%u Hz (%d)\n", - __func__, f->frequency * 625U / 10U, f->frequency); + dev_dbg(&s->udev->dev, "%s: frequency=%lu Hz (%u)\n", + __func__, f->frequency * 625UL / 10UL, f->frequency); return v4l2_ctrl_s_ctrl_int64(s->ctrl_tuner_rf, - f->frequency * 625U / 10U); + f->frequency * 625UL / 10UL); } const struct v4l2_ioctl_ops msi3101_ioctl_ops = { -- cgit v0.10.2 From 8e2efac7d2873ed81b536b8b1462b5e1eeefc2ab Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 7 Aug 2013 11:27:21 -0300 Subject: [media] msi3101: add stream format 336 I+Q pairs per frame That one seem to have 12-bit resolution. Use it for streams that has sampling rate 6 <= rate (Msps) < 8, between 6 and 8Msps. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 56f9757..c4bd963 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -605,6 +605,98 @@ static int msi3101_convert_stream_384(struct msi3101_state *s, u32 *dst, } /* + * Converts signed 12-bit integer into 32-bit IEEE floating point + * representation. + */ +static u32 msi3101_convert_sample_336(struct msi3101_state *s, u16 x) +{ + u32 msb, exponent, fraction, sign; + + /* Zero is special */ + if (!x) + return 0; + + /* Negative / positive value */ + if (x & (1 << 11)) { + x = -x; + x &= 0x7ff; /* result is 11 bit ... + sign */ + sign = 1 << 31; + } else { + sign = 0 << 31; + } + + /* Get location of the most significant bit */ + msb = __fls(x); + + /* + * Use a rotate instead of a shift because that works both leftwards + * and rightwards due to the mod(32) behaviour. This means we don't + * need to check to see if we are above 2^24 or not. + */ + fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK; + exponent = (127 + msb) << I2F_FRAC_BITS; + + return (fraction + exponent) | sign; +} + +static int msi3101_convert_stream_336(struct msi3101_state *s, u32 *dst, + u8 *src, unsigned int src_len) +{ + int i, j, i_max, dst_len = 0; + u16 sample[2]; + 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]); + + src += 16; + for (j = 0; j < 1008; j += 3) { + sample[0] = (src[j + 0] & 0xff) >> 0 | (src[j + 1] & 0x0f) << 8; + sample[1] = (src[j + 1] & 0xf0) >> 4 | (src[j + 2] & 0xff) << 4; + + *dst++ = msi3101_convert_sample_336(s, sample[0]); + *dst++ = msi3101_convert_sample_336(s, sample[1]); + } + /* 336 x I+Q 32bit float samples */ + dst_len += 336 * 2 * 4; + src += 1008; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if ((s->jiffies + msecs_to_jiffies(10000)) <= jiffies) { + unsigned long jiffies_now = jiffies; + unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies); + unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies = 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; +} + +/* * Converts signed 14-bit integer into 32-bit IEEE floating point * representation. * Will be exact from 0 to 2^24. Above that, we round towards zero @@ -1097,6 +1189,9 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) if (f_sr < 6000000) { s->convert_stream = msi3101_convert_stream_252; reg7 = 0x00009407; + } else if (f_sr < 8000000) { + s->convert_stream = msi3101_convert_stream_336; + reg7 = 0x00008507; } else { s->convert_stream = msi3101_convert_stream_384; reg7 = 0x0000a507; -- cgit v0.10.2 From 093cdc79a96d598ab31a829e12b4547d8ca15d4a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 7 Aug 2013 13:20:29 -0300 Subject: [media] msi3101: changes for tuner PLL freq limits I made some tuner freq limit tests against RF signal generator. Adjust some PLL limits according to these test results. Here are the results, taken from two different devices. Ranges are measured RF limits + calculated VCO limits. Mirics MSi3101 SDR Dongle: VHF_MODE 52 - 132 1664 - 4224 B3_MODE 103 - 263 1648 - 4208 B45_MODE 413 - 960 1652 - 3840 Hauppauge WinTV 133559 LF: VHF_MODE 49 - 130 1568 - 4160 B3_MODE 98 - 259 1568 - 4144 B45_MODE 391 - 960 1564 - 3840 Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index c4bd963..e7a21a2 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -1306,11 +1306,11 @@ static int msi3101_set_tuner(struct msi3101_state *s) u8 mode; u8 lo_div; } band_lut[] = { - { 30000000, 0x01, 16}, /* AM_MODE1 */ + { 47000000, 0x01, 16}, /* AM_MODE1 */ {108000000, 0x02, 32}, /* VHF_MODE */ - {240000000, 0x04, 16}, /* B3_MODE */ + {330000000, 0x04, 16}, /* B3_MODE */ {960000000, 0x08, 4}, /* B45_MODE */ - {167500000, 0x10, 2}, /* BL_MODE */ + { ~0U, 0x10, 2}, /* BL_MODE */ }; static const struct { u32 freq; -- cgit v0.10.2 From 00e049b05a4fe4c7a1503f2a46aced2548867631 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 7 Aug 2013 15:06:29 -0300 Subject: [media] msi3101: a lot of small cleanups Add comments, remove useless code and so. House cleaning party. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index e7a21a2..a3cc4c6 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -16,20 +16,36 @@ * 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 + * + * Development tree of that driver will be on: + * http://git.linuxtv.org/anttip/media_tree.git/shortlog/refs/heads/mirics + * + * GNU Radio plugin "gr-kernel" for device usage will be on: + * http://git.linuxtv.org/anttip/gr-kernel.git + * + * TODO: + * I will look these: + * - split RF tuner and USB ADC interface to own drivers (msi2500 and msi001) + * - move controls to V4L2 API + * + * Help is very highly welcome for these + all the others you could imagine: + * - use libv4l2 for stream format conversions + * - gr-kernel: switch to v4l2_mmap (current read eats a lot of cpu) + * - SDRSharp support */ -#include #include -#include #include -#include -#include #include #include #include #include #include -#include #include struct msi3101_gain { @@ -358,9 +374,9 @@ static const struct msi3101_gain msi3101_gain_lut_1000[] = { #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 MSI3101_CID_SAMPLING_MODE ((V4L2_CID_USER_BASE | 0xf000) + 0) #define MSI3101_CID_SAMPLING_RATE ((V4L2_CID_USER_BASE | 0xf000) + 1) #define MSI3101_CID_SAMPLING_RESOLUTION ((V4L2_CID_USER_BASE | 0xf000) + 2) @@ -373,8 +389,6 @@ static const struct msi3101_gain msi3101_gain_lut_1000[] = { struct msi3101_frame_buf { struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ struct list_head list; - void *data; /* raw data from USB device */ - int filled; /* number of bytes filled to *data */ }; struct msi3101_state { @@ -478,14 +492,19 @@ leave: */ /* - * Converts signed 10-bit integer into 32-bit IEEE floating point - * representation. - * Will be exact from 0 to 2^24. Above that, we round towards zero - * as the fractional bits will not fit in a float. (It would be better to - * round towards even as the fpu does, but that is slower.) + * Integer to 32-bit IEEE floating point representation routine is taken + * from Radeon R600 driver (drivers/gpu/drm/radeon/r600_blit_kms.c). + * + * TODO: Currently we do conversion here in Kernel, but in future that will + * be moved to the libv4l2 library as video format conversions are. */ #define I2F_FRAC_BITS 23 #define I2F_MASK ((1 << I2F_FRAC_BITS) - 1) + +/* + * Converts signed ~10+3-bit integer into 32-bit IEEE floating point + * representation. + */ static u32 msi3101_convert_sample_384(struct msi3101_state *s, u16 x, int shift) { u32 msb, exponent, fraction, sign; @@ -510,35 +529,26 @@ static u32 msi3101_convert_sample_384(struct msi3101_state *s, u16 x, int shift) /* Get location of the most significant bit */ msb = __fls(x); - /* - * Use a rotate instead of a shift because that works both leftwards - * and rightwards due to the mod(32) behaviour. This means we don't - * need to check to see if we are above 2^24 or not. - */ fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK; exponent = (127 + msb) << I2F_FRAC_BITS; return (fraction + exponent) | sign; } -#define MSI3101_CONVERT_IN_URB_HANDLER -#define MSI3101_EXTENSIVE_DEBUG static int msi3101_convert_stream_384(struct msi3101_state *s, u32 *dst, u8 *src, unsigned int src_len) { int i, j, k, l, i_max, dst_len = 0; u16 sample[4]; u32 bits; -#ifdef MSI3101_EXTENSIVE_DEBUG u32 sample_num[3]; -#endif + /* There could be 1-3 1024 bytes URB frames */ i_max = src_len / 1024; for (i = 0; i < i_max; i++) { -#ifdef MSI3101_EXTENSIVE_DEBUG 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(&s->udev->dev, + 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]); @@ -550,9 +560,7 @@ static int msi3101_convert_stream_384(struct msi3101_state *s, u32 *dst, */ dev_dbg_ratelimited(&s->udev->dev, "%*ph %*ph\n", 12, &src[4], 24, &src[1000]); - memset(&src[4], 0, 12); - memset(&src[1000], 0, 24); -#endif + src += 16; for (j = 0; j < 6; j++) { bits = src[160 + 3] << 24 | src[160 + 2] << 16 | src[160 + 1] << 8 | src[160 + 0] << 0; @@ -567,22 +575,18 @@ static int msi3101_convert_stream_384(struct msi3101_state *s, u32 *dst, *dst++ = msi3101_convert_sample_384(s, sample[1], (bits >> (2 * k)) & 0x3); *dst++ = msi3101_convert_sample_384(s, sample[2], (bits >> (2 * k)) & 0x3); *dst++ = msi3101_convert_sample_384(s, sample[3], (bits >> (2 * k)) & 0x3); - - /* 4 x 32bit float samples */ - dst_len += 4 * 4; } src += 10; } -#ifdef MSI3101_EXTENSIVE_DEBUG dev_dbg_ratelimited(&s->udev->dev, "sample control bits %08x\n", bits); -#endif src += 4; } + /* 384 x I+Q 32bit float samples */ + dst_len += 384 * 2 * 4; src += 24; } -#ifdef MSI3101_EXTENSIVE_DEBUG /* calculate samping rate and output it in 10 seconds intervals */ if ((s->jiffies + msecs_to_jiffies(10000)) <= jiffies) { unsigned long jiffies_now = jiffies; @@ -600,7 +604,7 @@ static int msi3101_convert_stream_384(struct msi3101_state *s, u32 *dst, /* next sample (sample = sample + i * 384) */ s->next_sample = sample_num[i_max - 1] + 384; -#endif + return dst_len; } @@ -628,11 +632,6 @@ static u32 msi3101_convert_sample_336(struct msi3101_state *s, u16 x) /* Get location of the most significant bit */ msb = __fls(x); - /* - * Use a rotate instead of a shift because that works both leftwards - * and rightwards due to the mod(32) behaviour. This means we don't - * need to check to see if we are above 2^24 or not. - */ fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK; exponent = (127 + msb) << I2F_FRAC_BITS; @@ -699,12 +698,7 @@ static int msi3101_convert_stream_336(struct msi3101_state *s, u32 *dst, /* * Converts signed 14-bit integer into 32-bit IEEE floating point * representation. - * Will be exact from 0 to 2^24. Above that, we round towards zero - * as the fractional bits will not fit in a float. (It would be better to - * round towards even as the fpu does, but that is slower.) */ -#define I2F_FRAC_BITS 23 -#define I2F_MASK ((1 << I2F_FRAC_BITS) - 1) static u32 msi3101_convert_sample_252(struct msi3101_state *s, u16 x) { u32 msb, exponent, fraction, sign; @@ -725,11 +719,6 @@ static u32 msi3101_convert_sample_252(struct msi3101_state *s, u16 x) /* Get location of the most significant bit */ msb = __fls(x); - /* - * Use a rotate instead of a shift because that works both leftwards - * and rightwards due to the mod(32) behaviour. This means we don't - * need to check to see if we are above 2^24 or not. - */ fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK; exponent = (127 + msb) << I2F_FRAC_BITS; @@ -832,7 +821,7 @@ static void msi3101_isoc_handler(struct urb *urb) /* Check frame error */ fstatus = urb->iso_frame_desc[i].status; if (fstatus) { - dev_dbg(&s->udev->dev, + dev_dbg_ratelimited(&s->udev->dev, "frame=%d/%d has error %d skipping\n", i, urb->number_of_packets, fstatus); goto skip; @@ -856,14 +845,9 @@ static void msi3101_isoc_handler(struct urb *urb) } /* fill framebuffer */ -#ifdef MSI3101_CONVERT_IN_URB_HANDLER ptr = vb2_plane_vaddr(&fbuf->vb, 0); flen = s->convert_stream(s, ptr, iso_buf, flen); vb2_set_plane_payload(&fbuf->vb, 0, flen); -#else - memcpy(fbuf->data, iso_buf, flen); - fbuf->filled = flen; -#endif vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); skip: ; @@ -1063,20 +1047,6 @@ static int msi3101_queue_setup(struct vb2_queue *vq, return 0; } -static int msi3101_buf_init(struct vb2_buffer *vb) -{ - struct msi3101_state *s = vb2_get_drv_priv(vb->vb2_queue); - struct msi3101_frame_buf *fbuf = - container_of(vb, struct msi3101_frame_buf, vb); - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - fbuf->data = vzalloc(ISO_MAX_FRAME_SIZE); - if (fbuf->data == NULL) - return -ENOMEM; - - return 0; -} - static int msi3101_buf_prepare(struct vb2_buffer *vb) { struct msi3101_state *s = vb2_get_drv_priv(vb->vb2_queue); @@ -1088,34 +1058,6 @@ static int msi3101_buf_prepare(struct vb2_buffer *vb) return 0; } -#ifdef MSI3101_CONVERT_IN_URB_HANDLER -static int msi3101_buf_finish(struct vb2_buffer *vb) -{ - return 0; -} -#else -static int msi3101_buf_finish(struct vb2_buffer *vb) -{ - struct msi3101_state *s = vb2_get_drv_priv(vb->vb2_queue); - struct msi3101_frame_buf *fbuf = - container_of(vb, struct msi3101_frame_buf, vb); - int ret; - u32 *dst = vb2_plane_vaddr(&fbuf->vb, 0); - ret = msi3101_convert_stream_384(s, dst, fbuf->data, fbuf->filled); - vb2_set_plane_payload(&fbuf->vb, 0, ret); - return 0; -} -#endif - -static void msi3101_buf_cleanup(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); - dev_dbg(&s->udev->dev, "%s:\n", __func__); - - vfree(buf->data); -} static void msi3101_buf_queue(struct vb2_buffer *vb) { struct msi3101_state *s = vb2_get_drv_priv(vb->vb2_queue); @@ -1544,10 +1486,7 @@ static int msi3101_stop_streaming(struct vb2_queue *vq) static struct vb2_ops msi3101_vb2_ops = { .queue_setup = msi3101_queue_setup, - .buf_init = msi3101_buf_init, .buf_prepare = msi3101_buf_prepare, - .buf_finish = msi3101_buf_finish, - .buf_cleanup = msi3101_buf_cleanup, .buf_queue = msi3101_buf_queue, .start_streaming = msi3101_start_streaming, .stop_streaming = msi3101_stop_streaming, @@ -1862,7 +1801,7 @@ err_free_mem: /* USB device ID list */ static struct usb_device_id msi3101_id_table[] = { - { USB_DEVICE(0x1df7, 0x2500) }, + { USB_DEVICE(0x1df7, 0x2500) }, /* Mirics MSi3101 SDR Dongle */ { USB_DEVICE(0x2040, 0xd300) }, /* Hauppauge WinTV 133559 LF */ { } }; -- cgit v0.10.2 From 61198dfd08969a662767072aafeebcb493909338 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 8 Aug 2013 15:39:33 -0300 Subject: [media] msi3101: implement stream format 504 That stream format carries 504 x I+Q samples per 1024 USB frame. Sample resolution is 8-bit signed. Default it when sampling rate is 9Msps or over. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index a3cc4c6..bf735f9 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -448,7 +448,7 @@ leave: /* * +=========================================================================== - * | 00-1023 | USB packet + * | 00-1023 | USB packet type '384' * +=========================================================================== * | 00- 03 | sequence number of first sample in that USB packet * +--------------------------------------------------------------------------- @@ -502,6 +502,93 @@ leave: #define I2F_MASK ((1 << I2F_FRAC_BITS) - 1) /* + * Converts signed 8-bit integer into 32-bit IEEE floating point + * representation. + */ +static u32 msi3101_convert_sample_504(struct msi3101_state *s, u16 x) +{ + u32 msb, exponent, fraction, sign; + + /* Zero is special */ + if (!x) + return 0; + + /* Negative / positive value */ + if (x & (1 << 7)) { + x = -x; + x &= 0x7f; /* result is 7 bit ... + sign */ + sign = 1 << 31; + } else { + sign = 0 << 31; + } + + /* Get location of the most significant bit */ + msb = __fls(x); + + fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK; + exponent = (127 + msb) << I2F_FRAC_BITS; + + return (fraction + exponent) | sign; +} + +static int msi3101_convert_stream_504(struct msi3101_state *s, u32 *dst, + u8 *src, unsigned int src_len) +{ + int i, j, i_max, dst_len = 0; + u16 sample[2]; + 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]); + + src += 16; + for (j = 0; j < 1008; j += 2) { + sample[0] = src[j + 0]; + sample[1] = src[j + 1]; + + *dst++ = msi3101_convert_sample_504(s, sample[0]); + *dst++ = msi3101_convert_sample_504(s, sample[1]); + } + /* 504 x I+Q 32bit float samples */ + dst_len += 504 * 2 * 4; + src += 1008; + } + + /* calculate samping rate and output it in 10 seconds intervals */ + if ((s->jiffies + msecs_to_jiffies(10000)) <= jiffies) { + unsigned long jiffies_now = jiffies; + unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies); + unsigned int samples = sample_num[i_max - 1] - s->sample; + s->jiffies = 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; +} + +/* * Converts signed ~10+3-bit integer into 32-bit IEEE floating point * representation. */ @@ -1134,9 +1221,12 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) } else if (f_sr < 8000000) { s->convert_stream = msi3101_convert_stream_336; reg7 = 0x00008507; - } else { + } else if (f_sr < 9000000) { s->convert_stream = msi3101_convert_stream_384; reg7 = 0x0000a507; + } else { + s->convert_stream = msi3101_convert_stream_504; + reg7 = 0x000c9407; } /* -- cgit v0.10.2 From 0046079812cfcf6c725e067edb683e472d388552 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 11 Aug 2013 19:07:02 -0300 Subject: [media] msi3101: change stream format 384 After feeding different signal levels using RF generator and looking GNU Radio FFT sink I made decision to change bit shift 3 to bit shift 2 as there was very (too) huge visible leap in FFT sink GUI. Now it looks more natural. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index bf735f9..839e601 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -589,7 +589,7 @@ static int msi3101_convert_stream_504(struct msi3101_state *s, u32 *dst, } /* - * Converts signed ~10+3-bit integer into 32-bit IEEE floating point + * Converts signed ~10+2-bit integer into 32-bit IEEE floating point * representation. */ static u32 msi3101_convert_sample_384(struct msi3101_state *s, u16 x, int shift) @@ -601,12 +601,15 @@ static u32 msi3101_convert_sample_384(struct msi3101_state *s, u16 x, int shift) if (!x) return 0; - /* Convert 10-bit two's complement to 13-bit */ + if (shift == 3) + shift = 2; + + /* Convert 10-bit two's complement to 12-bit */ if (x & (1 << 9)) { x |= ~0U << 10; /* set all the rest bits to one */ x <<= shift; x = -x; - x &= 0xfff; /* result is 12 bit ... + sign */ + x &= 0x7ff; /* result is 11 bit ... + sign */ sign = 1 << 31; } else { x <<= shift; -- cgit v0.10.2 From c59e6d5650e3c0690b6f4f62ebd965e1aa466ea2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 17 Aug 2013 19:30:18 -0300 Subject: [media] msi3101: few improvements for RF tuner * Fix AM_MODE. Now it could work at least in theory, cannot test. * Use greatest common divisor algo to divide PLL fractional parts. * Fix IF frequency mode. * + some very minor "style" issues Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 839e601..eebe1d0 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -29,11 +29,9 @@ * http://git.linuxtv.org/anttip/gr-kernel.git * * TODO: - * I will look these: - * - split RF tuner and USB ADC interface to own drivers (msi2500 and msi001) - * - move controls to V4L2 API - * * Help is very highly welcome for these + all the others you could imagine: + * - split USB ADC interface and RF tuner to own drivers (msi2500 and msi001) + * - 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 @@ -41,6 +39,7 @@ #include #include +#include #include #include #include @@ -1331,25 +1330,25 @@ err: static int msi3101_set_tuner(struct msi3101_state *s) { int ret, i, len; - unsigned int n, m, thresh, frac, vco_step, tmp; + unsigned int n, m, thresh, frac, vco_step, tmp, f_if1; u32 reg; u64 f_vco; - u8 mode, lo_div; + u8 mode, filter_mode, lo_div; const struct msi3101_gain *gain_lut; static const struct { u32 rf; u8 mode; u8 lo_div; } band_lut[] = { - { 47000000, 0x01, 16}, /* AM_MODE1 */ - {108000000, 0x02, 32}, /* VHF_MODE */ - {330000000, 0x04, 16}, /* B3_MODE */ - {960000000, 0x08, 4}, /* B45_MODE */ - { ~0U, 0x10, 2}, /* BL_MODE */ + { 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 val; + u8 filter_mode; } if_freq_lut[] = { { 0, 0x03}, /* Zero IF */ { 450000, 0x02}, /* 450 kHz IF */ @@ -1370,19 +1369,19 @@ static int msi3101_set_tuner(struct msi3101_state *s) {8000000, 0x07}, /* 8 MHz */ }; - unsigned int rf_freq = s->ctrl_tuner_rf->val64; + unsigned int f_rf = s->ctrl_tuner_rf->val64; /* * bandwidth (Hz) * 200000, 300000, 600000, 1536000, 5000000, 6000000, 7000000, 8000000 */ - int bandwidth = s->ctrl_tuner_bw->val; + unsigned int bandwidth = s->ctrl_tuner_bw->val; /* * intermediate frequency (Hz) * 0, 450000, 1620000, 2048000 */ - int if_freq = s->ctrl_tuner_if->val; + unsigned int f_if = s->ctrl_tuner_if->val; /* * gain reduction (dB) @@ -1392,13 +1391,13 @@ static int msi3101_set_tuner(struct msi3101_state *s) int gain = s->ctrl_tuner_gain->val; dev_dbg(&s->udev->dev, - "%s: rf_freq=%d bandwidth=%d if_freq=%d gain=%d\n", - __func__, rf_freq, bandwidth, if_freq, gain); + "%s: f_rf=%d bandwidth=%d f_if=%d gain=%d\n", + __func__, f_rf, bandwidth, f_if, gain); ret = -EINVAL; for (i = 0; i < ARRAY_SIZE(band_lut); i++) { - if (rf_freq <= band_lut[i].rf) { + if (f_rf <= band_lut[i].rf) { mode = band_lut[i].mode; lo_div = band_lut[i].lo_div; break; @@ -1408,9 +1407,15 @@ static int msi3101_set_tuner(struct msi3101_state *s) if (i == ARRAY_SIZE(band_lut)) 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 (if_freq == if_freq_lut[i].freq) { - if_freq = if_freq_lut[i].val; + if (f_if == if_freq_lut[i].freq) { + filter_mode = if_freq_lut[i].filter_mode; break; } } @@ -1430,8 +1435,7 @@ static int msi3101_set_tuner(struct msi3101_state *s) #define F_OUT_STEP 1 #define R_REF 4 -#define F_IF 0 - f_vco = (rf_freq + F_IF) * lo_div; + f_vco = (f_rf + f_if + f_if1) * lo_div; n = f_vco / (F_REF * R_REF); m = f_vco % (F_REF * R_REF); @@ -1439,7 +1443,12 @@ static int msi3101_set_tuner(struct msi3101_state *s) thresh = (F_REF * R_REF) / vco_step; frac = 1ul * thresh * m / (F_REF * R_REF); - /* Divide to reg max. After that RF resolution will be +-500Hz. */ + /* 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); @@ -1451,15 +1460,19 @@ static int msi3101_set_tuner(struct msi3101_state *s) dev_dbg(&s->udev->dev, "%s: rf=%u:%u n=%d thresh=%d frac=%d\n", - __func__, rf_freq, tmp, n, thresh, frac); + __func__, f_rf, tmp, n, thresh, frac); ret = msi3101_tuner_write(s, 0x00000e); + if (ret) + goto err; + ret = msi3101_tuner_write(s, 0x000003); + if (ret) + goto err; reg = 0 << 0; reg |= mode << 4; - reg |= 1 << 10; - reg |= if_freq << 12; + reg |= filter_mode << 12; reg |= bandwidth << 14; reg |= 0x02 << 17; reg |= 0x00 << 20; @@ -1482,10 +1495,10 @@ static int msi3101_set_tuner(struct msi3101_state *s) if (ret) goto err; - if (rf_freq < 120000000) { + if (f_rf < 120000000) { gain_lut = msi3101_gain_lut_120; len = ARRAY_SIZE(msi3101_gain_lut_120); - } else if (rf_freq < 245000000) { + } else if (f_rf < 245000000) { gain_lut = msi3101_gain_lut_245; len = ARRAY_SIZE(msi3101_gain_lut_120); } else { -- cgit v0.10.2 From 6c94e14e7f802011afc1018fc8529e66f40866b9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 20 Aug 2013 09:18:24 -0300 Subject: [media] radio-si470x-usb: Remove software version check We've an user reporting a device with a software version of 0, which works fine. Once we lower the version check to accept version 0 it becomes a nop, so remove it completely. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index 62f3ede..d6d4d60 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -142,8 +142,6 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); /************************************************************************** * Software/Hardware Versions from Scratch Page **************************************************************************/ -#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6 -#define RADIO_SW_VERSION 1 #define RADIO_HW_VERSION 1 @@ -682,15 +680,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, } dev_info(&intf->dev, "software version %d, hardware version %d\n", radio->software_version, radio->hardware_version); - if (radio->software_version < RADIO_SW_VERSION) { - dev_warn(&intf->dev, - "This driver is known to work with " - "software version %hu,\n", RADIO_SW_VERSION); - dev_warn(&intf->dev, - "but the device has software version %hu.\n", - radio->software_version); - version_warning = 1; - } if (radio->hardware_version < RADIO_HW_VERSION) { dev_warn(&intf->dev, "This driver is known to work with " -- cgit v0.10.2 From b563a0d049143a98411b6a45745186daf443d587 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Mon, 5 Aug 2013 17:16:37 -0300 Subject: [media] gspca: fix dev_open() error path If v4l2_fh_open() fails in dev_open(), gspca_dev->module left locked. The patch adds module_put(gspca_dev->module) on this path. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov 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 b7ae872..048507b 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1266,6 +1266,7 @@ static void gspca_release(struct v4l2_device *v4l2_device) static int dev_open(struct file *file) { struct gspca_dev *gspca_dev = video_drvdata(file); + int ret; PDEBUG(D_STREAM, "[%s] open", current->comm); @@ -1273,7 +1274,10 @@ static int dev_open(struct file *file) if (!try_module_get(gspca_dev->module)) return -ENODEV; - return v4l2_fh_open(file); + ret = v4l2_fh_open(file); + if (ret) + module_put(gspca_dev->module); + return ret; } static int dev_close(struct file *file) -- cgit v0.10.2 From d48de1c73b41d27e3cc6e500eb9588449edb2f14 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Thu, 15 Aug 2013 07:29:32 -0300 Subject: [media] gspca-ov534: don't call sd_start() from sd_init() sd_start() operates on device controls but after the conversion to the v4l2 control framework in commits 62bba5d and 1bd7d6a controls are initialized in sd_init_controls() which is called _after_ sd_init(): The change fixes a NULL pointer dereference for Hercules Blog Webcam; the problem is observable since 3.6: gspca_main: v2.14.0 registered gspca_main: ov534-2.14.0 probing 06f8:3002 BUG: unable to handle kernel NULL pointer dereference at 0000000000000050 IP: [] v4l2_ctrl_g_ctrl+0x11/0x60 [videodev] PGD 0 Oops: 0000 [#1] SMP Modules linked in: gspca_ov534(+) gspca_main videodev rfcomm bnep ppdev bluetooth binfmt_misc snd_hda_codec_hdmi snd_hda_codec_realtek stir4200 irda crc_ccitt usblp snd_hda_intel snd_hda_codec snd_hwdep snd_pcm hid_generic snd_page_alloc snd_seq_midi snd_seq_midi_event usbhid snd_rawmidi snd_seq snd_seq_device snd_timer hid i915 snd psmouse drm_kms_helper serio_raw mei_me drm mei soundcore video i2c_algo_bit lpc_ich mac_hid coretemp lp parport firewire_ohci firewire_core crc_itu_t ahci libahci alx mdio r8169 mii [last unloaded: parport_pc] CPU: 3 PID: 4352 Comm: modprobe Not tainted 3.11.0-031100rc2-generic #201307211535 Hardware name: Gigabyte Technology Co., Ltd. To be filled by O.E.M./Z77-DS3H, BIOS F9 09/19/2012 task: ffff8801c20f9770 ti: ffff8801ceaa0000 task.ti: ffff8801ceaa0000 RIP: 0010:[] [] v4l2_ctrl_g_ctrl+0x11/0x60 [videodev] RSP: 0018:ffff8801ceaa1af8 EFLAGS: 00010292 RAX: 0000000000000001 RBX: 0000000000000000 RCX: 000000000001988b RDX: 000000000001988a RSI: ffffffffa032745a RDI: 0000000000000000 RBP: ffff8801ceaa1b28 R08: 0000000000017380 R09: ffffea0008419d80 R10: ffffffff81538f5a R11: 0000000000000002 R12: ffffffffa03273dc R13: ffffffffa03273dc R14: 0000000000000000 R15: ffffffffa03270a0 FS: 00007f72d564a740(0000) GS:ffff88021f380000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000050 CR3: 00000001bd1f0000 CR4: 00000000001407e0 Stack: ffff8801ceaa1b28 ffffffffa0325cff ffff8801000001f4 ffff8801ceb44000 ffffffffa03273dc ffff8801ceb44000 ffff8801ceaa1b58 ffffffffa032688e ffff8801ceb44000 ffffffffa03274f0 ffffffffa03274f0 ffff8801ceb44380 Call Trace: [] ? sccb_w_array+0x3f/0x80 [gspca_ov534] [] sd_start+0xce/0x2b0 [gspca_ov534] [] sd_init+0x189/0x1e8 [gspca_ov534] [] gspca_dev_probe2+0x285/0x410 [gspca_main] [] gspca_dev_probe+0x38/0x60 [gspca_main] [] sd_probe+0x21/0x30 [gspca_ov534] [] usb_probe_interface+0x1c0/0x2f0 [] really_probe+0x6c/0x330 [] driver_probe_device+0x47/0xa0 [] __driver_attach+0xab/0xb0 [] ? driver_probe_device+0xa0/0xa0 [] bus_for_each_dev+0x5e/0x90 [] driver_attach+0x1e/0x20 [] bus_add_driver+0x10c/0x290 [] driver_register+0x7d/0x160 [] usb_register_driver+0xa0/0x160 [] ? 0xffffffffa0066fff [] sd_driver_init+0x1e/0x1000 [gspca_ov534] [] do_one_initcall+0xfa/0x1b0 [] ? set_memory_nx+0x43/0x50 [] do_init_module+0x80/0x1d1 [] load_module+0x4c9/0x5f0 [] ? add_kallsyms+0x210/0x210 [] SyS_init_module+0xb4/0x100 [] tracesys+0xe1/0xe6 Code: a0 09 00 00 48 c7 c7 30 c3 3c a0 e8 7a 38 ca e0 eb cf 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 55 48 89 e5 53 48 89 fb 48 83 ec 28 <8b> 47 50 83 e8 05 83 f8 02 77 09 80 b8 20 8c 3c a0 00 74 1d 48 RIP [] v4l2_ctrl_g_ctrl+0x11/0x60 [videodev] RSP CR2: 0000000000000050 ---[ end trace 6786f15abfd2ac90 ]--- Original bug report from: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1173723/ Signed-off-by: Antonio Ospite Tested-by: Yaroslav Zakharuk Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 2e28c81..03a33c4 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -1305,8 +1305,7 @@ static int sd_init(struct gspca_dev *gspca_dev) ov534_set_led(gspca_dev, 1); sccb_w_array(gspca_dev, sensor_init[sd->sensor].val, sensor_init[sd->sensor].len); - if (sd->sensor == SENSOR_OV767x) - sd_start(gspca_dev); + sd_stopN(gspca_dev); /* set_frame_rate(gspca_dev); */ -- cgit v0.10.2 From 4ab0620bdc6fe3ca2365c014552dee64402670a4 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Tue, 20 Aug 2013 10:03:59 -0300 Subject: [media] introduce gspca-stk1135: Syntek STK1135 driver Hello, this is a new gspca driver for Syntek STK1135 webcams. The code is completely new, but register values are based on Syntekdriver (stk11xx) by Nicolas VIVIEN (http://syntekdriver.sourceforge.net). Only one webcam type is supported now - vendor 0x174f, device 0x6a31. It's Asus F5RL laptop flippable webcam with MT9M112. The camera works better than in Windows - initializes much faster and provides more resolutions (the sensor can do almost any resolution - just add it to the stk1135_modes[] - could this feature be somehow used by applications to avoid SW scaling?). Autoflip works too - when the camera is flipped around, the image is flipped automatically. Signed-off-by: Ondrej Zary Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig index 6345f93..4f0c6d5 100644 --- a/drivers/media/usb/gspca/Kconfig +++ b/drivers/media/usb/gspca/Kconfig @@ -338,6 +338,15 @@ config USB_GSPCA_STK014 To compile this driver as a module, choose M here: the module will be called gspca_stk014. +config USB_GSPCA_STK1135 + tristate "Syntek STK1135 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the STK1135 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_stk1135. + config USB_GSPCA_STV0680 tristate "STV0680 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/usb/gspca/Makefile b/drivers/media/usb/gspca/Makefile index c901da0..5855131 100644 --- a/drivers/media/usb/gspca/Makefile +++ b/drivers/media/usb/gspca/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o obj-$(CONFIG_USB_GSPCA_SQ930X) += gspca_sq930x.o obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o +obj-$(CONFIG_USB_GSPCA_STK1135) += gspca_stk1135.o obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o @@ -78,6 +79,7 @@ gspca_sq905-objs := sq905.o gspca_sq905c-objs := sq905c.o gspca_sq930x-objs := sq930x.o gspca_stk014-objs := stk014.o +gspca_stk1135-objs := stk1135.o gspca_stv0680-objs := stv0680.o gspca_sunplus-objs := sunplus.o gspca_t613-objs := t613.o diff --git a/drivers/media/usb/gspca/stk1135.c b/drivers/media/usb/gspca/stk1135.c new file mode 100644 index 0000000..5858688 --- /dev/null +++ b/drivers/media/usb/gspca/stk1135.c @@ -0,0 +1,685 @@ +/* + * Syntek STK1135 subdriver + * + * Copyright (c) 2013 Ondrej Zary + * + * Based on Syntekdriver (stk11xx) by Nicolas VIVIEN: + * http://syntekdriver.sourceforge.net + * + * 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 + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define MODULE_NAME "stk1135" + +#include "gspca.h" +#include "stk1135.h" + +MODULE_AUTHOR("Ondrej Zary"); +MODULE_DESCRIPTION("Syntek STK1135 USB Camera Driver"); +MODULE_LICENSE("GPL"); + + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + u8 pkt_seq; + u8 sensor_page; + + bool flip_status; + u8 flip_debounce; + + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; +}; + +static const struct v4l2_pix_format stk1135_modes[] = { + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB}, + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB}, + {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB}, + {720, 576, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 720, + .sizeimage = 720 * 576, + .colorspace = V4L2_COLORSPACE_SRGB}, + {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB}, + {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1024, + .sizeimage = 1024 * 768, + .colorspace = V4L2_COLORSPACE_SRGB}, + {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024, + .colorspace = V4L2_COLORSPACE_SRGB}, +}; + +/* -- read a register -- */ +static u8 reg_r(struct gspca_dev *gspca_dev, u16 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return 0; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, + index, + gspca_dev->usb_buf, 1, + 500); + + PDEBUG(D_USBI, "reg_r 0x%x=0x%02x", index, gspca_dev->usb_buf[0]); + if (ret < 0) { + pr_err("reg_r 0x%x err %d\n", index, ret); + gspca_dev->usb_err = ret; + return 0; + } + + return gspca_dev->usb_buf[0]; +} + +/* -- write a register -- */ +static void reg_w(struct gspca_dev *gspca_dev, u16 index, u8 val) +{ + int ret; + struct usb_device *dev = gspca_dev->dev; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, + index, + NULL, + 0, + 500); + PDEBUG(D_USBO, "reg_w 0x%x:=0x%02x", index, val); + if (ret < 0) { + pr_err("reg_w 0x%x err %d\n", index, ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w_mask(struct gspca_dev *gspca_dev, u16 index, u8 val, u8 mask) +{ + val = (reg_r(gspca_dev, index) & ~mask) | (val & mask); + reg_w(gspca_dev, index, val); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + gspca_dev->cam.cam_mode = stk1135_modes; + gspca_dev->cam.nmodes = ARRAY_SIZE(stk1135_modes); + return 0; +} + +static int stk1135_serial_wait_ready(struct gspca_dev *gspca_dev) +{ + int i = 0; + u8 val; + + do { + val = reg_r(gspca_dev, STK1135_REG_SICTL + 1); + if (i++ > 500) { /* maximum retry count */ + pr_err("serial bus timeout: status=0x%02x\n", val); + return -1; + } + /* repeat if BUSY or WRITE/READ not finished */ + } while ((val & 0x10) || !(val & 0x05)); + + return 0; +} + +static u8 sensor_read_8(struct gspca_dev *gspca_dev, u8 addr) +{ + reg_w(gspca_dev, STK1135_REG_SBUSR, addr); + /* begin read */ + reg_w(gspca_dev, STK1135_REG_SICTL, 0x20); + /* wait until finished */ + if (stk1135_serial_wait_ready(gspca_dev)) { + pr_err("Sensor read failed\n"); + return 0; + } + + return reg_r(gspca_dev, STK1135_REG_SBUSR + 1); +} + +static u16 sensor_read_16(struct gspca_dev *gspca_dev, u8 addr) +{ + return (sensor_read_8(gspca_dev, addr) << 8) | + sensor_read_8(gspca_dev, 0xf1); +} + +static void sensor_write_8(struct gspca_dev *gspca_dev, u8 addr, u8 data) +{ + /* load address and data registers */ + reg_w(gspca_dev, STK1135_REG_SBUSW, addr); + reg_w(gspca_dev, STK1135_REG_SBUSW + 1, data); + /* begin write */ + reg_w(gspca_dev, STK1135_REG_SICTL, 0x01); + /* wait until finished */ + if (stk1135_serial_wait_ready(gspca_dev)) { + pr_err("Sensor write failed\n"); + return; + } +} + +static void sensor_write_16(struct gspca_dev *gspca_dev, u8 addr, u16 data) +{ + sensor_write_8(gspca_dev, addr, data >> 8); + sensor_write_8(gspca_dev, 0xf1, data & 0xff); +} + +static void sensor_set_page(struct gspca_dev *gspca_dev, u8 page) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (page != sd->sensor_page) { + sensor_write_16(gspca_dev, 0xf0, page); + sd->sensor_page = page; + } +} + +static u16 sensor_read(struct gspca_dev *gspca_dev, u16 reg) +{ + sensor_set_page(gspca_dev, reg >> 8); + return sensor_read_16(gspca_dev, reg & 0xff); +} + +static void sensor_write(struct gspca_dev *gspca_dev, u16 reg, u16 val) +{ + sensor_set_page(gspca_dev, reg >> 8); + sensor_write_16(gspca_dev, reg & 0xff, val); +} + +static void sensor_write_mask(struct gspca_dev *gspca_dev, + u16 reg, u16 val, u16 mask) +{ + val = (sensor_read(gspca_dev, reg) & ~mask) | (val & mask); + sensor_write(gspca_dev, reg, val); +} + +struct sensor_val { + u16 reg; + u16 val; +}; + +/* configure MT9M112 sensor */ +static void stk1135_configure_mt9m112(struct gspca_dev *gspca_dev) +{ + static const struct sensor_val cfg[] = { + /* restart&reset, chip enable, reserved */ + { 0x00d, 0x000b }, { 0x00d, 0x0008 }, { 0x035, 0x0022 }, + /* mode ctl: AWB on, AE both, clip aper corr, defect corr, AE */ + { 0x106, 0x700e }, + + { 0x2dd, 0x18e0 }, /* B-R thresholds, */ + + /* AWB */ + { 0x21f, 0x0180 }, /* Cb and Cr limits */ + { 0x220, 0xc814 }, { 0x221, 0x8080 }, /* lum limits, RGB gain */ + { 0x222, 0xa078 }, { 0x223, 0xa078 }, /* R, B limit */ + { 0x224, 0x5f20 }, { 0x228, 0xea02 }, /* mtx adj lim, adv ctl */ + { 0x229, 0x867a }, /* wide gates */ + + /* Color correction */ + /* imager gains base, delta, delta signs */ + { 0x25e, 0x594c }, { 0x25f, 0x4d51 }, { 0x260, 0x0002 }, + /* AWB adv ctl 2, gain offs */ + { 0x2ef, 0x0008 }, { 0x2f2, 0x0000 }, + /* base matrix signs, scale K1-5, K6-9 */ + { 0x202, 0x00ee }, { 0x203, 0x3923 }, { 0x204, 0x0724 }, + /* base matrix coef */ + { 0x209, 0x00cd }, { 0x20a, 0x0093 }, { 0x20b, 0x0004 },/*K1-3*/ + { 0x20c, 0x005c }, { 0x20d, 0x00d9 }, { 0x20e, 0x0053 },/*K4-6*/ + { 0x20f, 0x0008 }, { 0x210, 0x0091 }, { 0x211, 0x00cf },/*K7-9*/ + { 0x215, 0x0000 }, /* delta mtx signs */ + /* delta matrix coef */ + { 0x216, 0x0000 }, { 0x217, 0x0000 }, { 0x218, 0x0000 },/*D1-3*/ + { 0x219, 0x0000 }, { 0x21a, 0x0000 }, { 0x21b, 0x0000 },/*D4-6*/ + { 0x21c, 0x0000 }, { 0x21d, 0x0000 }, { 0x21e, 0x0000 },/*D7-9*/ + /* enable & disable manual WB to apply color corr. settings */ + { 0x106, 0xf00e }, { 0x106, 0x700e }, + + /* Lens shading correction */ + { 0x180, 0x0007 }, /* control */ + /* vertical knee 0, 2+1, 4+3 */ + { 0x181, 0xde13 }, { 0x182, 0xebe2 }, { 0x183, 0x00f6 }, /* R */ + { 0x184, 0xe114 }, { 0x185, 0xeadd }, { 0x186, 0xfdf6 }, /* G */ + { 0x187, 0xe511 }, { 0x188, 0xede6 }, { 0x189, 0xfbf7 }, /* B */ + /* horizontal knee 0, 2+1, 4+3, 5 */ + { 0x18a, 0xd613 }, { 0x18b, 0xedec }, /* R .. */ + { 0x18c, 0xf9f2 }, { 0x18d, 0x0000 }, /* .. R */ + { 0x18e, 0xd815 }, { 0x18f, 0xe9ea }, /* G .. */ + { 0x190, 0xf9f1 }, { 0x191, 0x0002 }, /* .. G */ + { 0x192, 0xde10 }, { 0x193, 0xefef }, /* B .. */ + { 0x194, 0xfbf4 }, { 0x195, 0x0002 }, /* .. B */ + /* vertical knee 6+5, 8+7 */ + { 0x1b6, 0x0e06 }, { 0x1b7, 0x2713 }, /* R */ + { 0x1b8, 0x1106 }, { 0x1b9, 0x2713 }, /* G */ + { 0x1ba, 0x0c03 }, { 0x1bb, 0x2a0f }, /* B */ + /* horizontal knee 7+6, 9+8, 10 */ + { 0x1bc, 0x1208 }, { 0x1bd, 0x1a16 }, { 0x1be, 0x0022 }, /* R */ + { 0x1bf, 0x150a }, { 0x1c0, 0x1c1a }, { 0x1c1, 0x002d }, /* G */ + { 0x1c2, 0x1109 }, { 0x1c3, 0x1414 }, { 0x1c4, 0x002a }, /* B */ + { 0x106, 0x740e }, /* enable lens shading correction */ + + /* Gamma correction - context A */ + { 0x153, 0x0b03 }, { 0x154, 0x4722 }, { 0x155, 0xac82 }, + { 0x156, 0xdac7 }, { 0x157, 0xf5e9 }, { 0x158, 0xff00 }, + /* Gamma correction - context B */ + { 0x1dc, 0x0b03 }, { 0x1dd, 0x4722 }, { 0x1de, 0xac82 }, + { 0x1df, 0xdac7 }, { 0x1e0, 0xf5e9 }, { 0x1e1, 0xff00 }, + + /* output format: RGB, invert output pixclock, output bayer */ + { 0x13a, 0x4300 }, { 0x19b, 0x4300 }, /* for context A, B */ + { 0x108, 0x0180 }, /* format control - enable bayer row flip */ + + { 0x22f, 0xd100 }, { 0x29c, 0xd100 }, /* AE A, B */ + + /* default prg conf, prg ctl - by 0x2d2, prg advance - PA1 */ + { 0x2d2, 0x0000 }, { 0x2cc, 0x0004 }, { 0x2cb, 0x0001 }, + + { 0x22e, 0x0c3c }, { 0x267, 0x1010 }, /* AE tgt ctl, gain lim */ + + /* PLL */ + { 0x065, 0xa000 }, /* clk ctl - enable PLL (clear bit 14) */ + { 0x066, 0x2003 }, { 0x067, 0x0501 }, /* PLL M=128, N=3, P=1 */ + { 0x065, 0x2000 }, /* disable PLL bypass (clear bit 15) */ + + { 0x005, 0x01b8 }, { 0x007, 0x00d8 }, /* horiz blanking B, A */ + + /* AE line size, shutter delay limit */ + { 0x239, 0x06c0 }, { 0x23b, 0x040e }, /* for context A */ + { 0x23a, 0x06c0 }, { 0x23c, 0x0564 }, /* for context B */ + /* shutter width basis 60Hz, 50Hz */ + { 0x257, 0x0208 }, { 0x258, 0x0271 }, /* for context A */ + { 0x259, 0x0209 }, { 0x25a, 0x0271 }, /* for context B */ + + { 0x25c, 0x120d }, { 0x25d, 0x1712 }, /* flicker 60Hz, 50Hz */ + { 0x264, 0x5e1c }, /* reserved */ + /* flicker, AE gain limits, gain zone limits */ + { 0x25b, 0x0003 }, { 0x236, 0x7810 }, { 0x237, 0x8304 }, + + { 0x008, 0x0021 }, /* vert blanking A */ + }; + int i; + u16 width, height; + + for (i = 0; i < ARRAY_SIZE(cfg); i++) + sensor_write(gspca_dev, cfg[i].reg, cfg[i].val); + + /* set output size */ + width = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].width; + height = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].height; + if (width <= 640) { /* use context A (half readout speed by default) */ + sensor_write(gspca_dev, 0x1a7, width); + sensor_write(gspca_dev, 0x1aa, height); + /* set read mode context A */ + sensor_write(gspca_dev, 0x0c8, 0x0000); + /* set resize, read mode, vblank, hblank context A */ + sensor_write(gspca_dev, 0x2c8, 0x0000); + } else { /* use context B (full readout speed by default) */ + sensor_write(gspca_dev, 0x1a1, width); + sensor_write(gspca_dev, 0x1a4, height); + /* set read mode context B */ + sensor_write(gspca_dev, 0x0c8, 0x0008); + /* set resize, read mode, vblank, hblank context B */ + sensor_write(gspca_dev, 0x2c8, 0x040b); + } +} + +static void stk1135_configure_clock(struct gspca_dev *gspca_dev) +{ + /* configure SCLKOUT */ + reg_w(gspca_dev, STK1135_REG_TMGEN, 0x12); + /* set 1 clock per pixel */ + /* and positive edge clocked pulse high when pixel counter = 0 */ + reg_w(gspca_dev, STK1135_REG_TCP1 + 0, 0x41); + reg_w(gspca_dev, STK1135_REG_TCP1 + 1, 0x00); + reg_w(gspca_dev, STK1135_REG_TCP1 + 2, 0x00); + reg_w(gspca_dev, STK1135_REG_TCP1 + 3, 0x00); + + /* enable CLKOUT for sensor */ + reg_w(gspca_dev, STK1135_REG_SENSO + 0, 0x10); + /* disable STOP clock */ + reg_w(gspca_dev, STK1135_REG_SENSO + 1, 0x00); + /* set lower 8 bits of PLL feedback divider */ + reg_w(gspca_dev, STK1135_REG_SENSO + 3, 0x07); + /* set other PLL parameters */ + reg_w(gspca_dev, STK1135_REG_PLLFD, 0x06); + /* enable timing generator */ + reg_w(gspca_dev, STK1135_REG_TMGEN, 0x80); + /* enable PLL */ + reg_w(gspca_dev, STK1135_REG_SENSO + 2, 0x04); + + /* set serial interface clock divider (30MHz/0x1f*16+2) = 60240 kHz) */ + reg_w(gspca_dev, STK1135_REG_SICTL + 2, 0x1f); +} + +static void stk1135_camera_disable(struct gspca_dev *gspca_dev) +{ + /* set capture end Y position to 0 */ + reg_w(gspca_dev, STK1135_REG_CIEPO + 2, 0x00); + reg_w(gspca_dev, STK1135_REG_CIEPO + 3, 0x00); + /* disable capture */ + reg_w_mask(gspca_dev, STK1135_REG_SCTRL, 0x00, 0x80); + + /* enable sensor standby and diasble chip enable */ + sensor_write_mask(gspca_dev, 0x00d, 0x0004, 0x000c); + + /* disable PLL */ + reg_w_mask(gspca_dev, STK1135_REG_SENSO + 2, 0x00, 0x01); + /* disable timing generator */ + reg_w(gspca_dev, STK1135_REG_TMGEN, 0x00); + /* enable STOP clock */ + reg_w(gspca_dev, STK1135_REG_SENSO + 1, 0x20); + /* disable CLKOUT for sensor */ + reg_w(gspca_dev, STK1135_REG_SENSO, 0x00); + + /* disable sensor (GPIO5) and enable GPIO0,3,6 (?) - sensor standby? */ + reg_w(gspca_dev, STK1135_REG_GCTRL, 0x49); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + u16 sensor_id; + char *sensor_name; + struct sd *sd = (struct sd *) gspca_dev; + + /* set GPIO3,4,5,6 direction to output */ + reg_w(gspca_dev, STK1135_REG_GCTRL + 2, 0x78); + /* enable sensor (GPIO5) */ + reg_w(gspca_dev, STK1135_REG_GCTRL, (1 << 5)); + /* disable ROM interface */ + reg_w(gspca_dev, STK1135_REG_GCTRL + 3, 0x80); + /* enable interrupts from GPIO8 (flip sensor) and GPIO9 (???) */ + reg_w(gspca_dev, STK1135_REG_ICTRL + 1, 0x00); + reg_w(gspca_dev, STK1135_REG_ICTRL + 3, 0x03); + /* enable remote wakeup from GPIO9 (???) */ + reg_w(gspca_dev, STK1135_REG_RMCTL + 1, 0x00); + reg_w(gspca_dev, STK1135_REG_RMCTL + 3, 0x02); + + /* reset serial interface */ + reg_w(gspca_dev, STK1135_REG_SICTL, 0x80); + reg_w(gspca_dev, STK1135_REG_SICTL, 0x00); + /* set sensor address */ + reg_w(gspca_dev, STK1135_REG_SICTL + 3, 0xba); + /* disable alt 2-wire serial interface */ + reg_w(gspca_dev, STK1135_REG_ASIC + 3, 0x00); + + stk1135_configure_clock(gspca_dev); + + /* read sensor ID */ + sd->sensor_page = 0xff; + sensor_id = sensor_read(gspca_dev, 0x000); + + switch (sensor_id) { + case 0x148c: + sensor_name = "MT9M112"; + break; + default: + sensor_name = "unknown"; + } + pr_info("Detected sensor type %s (0x%x)\n", sensor_name, sensor_id); + + stk1135_camera_disable(gspca_dev); + + return gspca_dev->usb_err; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 width, height; + + /* enable sensor (GPIO5) */ + reg_w(gspca_dev, STK1135_REG_GCTRL, (1 << 5)); + + stk1135_configure_clock(gspca_dev); + + /* set capture start position X = 0, Y = 0 */ + reg_w(gspca_dev, STK1135_REG_CISPO + 0, 0x00); + reg_w(gspca_dev, STK1135_REG_CISPO + 1, 0x00); + reg_w(gspca_dev, STK1135_REG_CISPO + 2, 0x00); + reg_w(gspca_dev, STK1135_REG_CISPO + 3, 0x00); + + /* set capture end position */ + width = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].width; + height = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].height; + reg_w(gspca_dev, STK1135_REG_CIEPO + 0, width & 0xff); + reg_w(gspca_dev, STK1135_REG_CIEPO + 1, width >> 8); + reg_w(gspca_dev, STK1135_REG_CIEPO + 2, height & 0xff); + reg_w(gspca_dev, STK1135_REG_CIEPO + 3, height >> 8); + + /* set 8-bit mode */ + reg_w(gspca_dev, STK1135_REG_SCTRL, 0x20); + + stk1135_configure_mt9m112(gspca_dev); + + /* enable capture */ + reg_w_mask(gspca_dev, STK1135_REG_SCTRL, 0x80, 0x80); + + if (gspca_dev->usb_err >= 0) + PDEBUG(D_STREAM, "camera started alt: 0x%02x", + gspca_dev->alt); + + sd->pkt_seq = 0; + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + usb_set_interface(dev, gspca_dev->iface, 0); + + stk1135_camera_disable(gspca_dev); + + PDEBUG(D_STREAM, "camera stopped"); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int skip = sizeof(struct stk1135_pkt_header); + bool flip; + enum gspca_packet_type pkt_type = INTER_PACKET; + struct stk1135_pkt_header *hdr = (void *)data; + u8 seq; + + if (len < 4) { + PDEBUG(D_PACK, "received short packet (less than 4 bytes)"); + return; + } + + /* GPIO 8 is flip sensor (1 = normal position, 0 = flipped to back) */ + flip = !(le16_to_cpu(hdr->gpio) & (1 << 8)); + /* it's a switch, needs software debounce */ + if (sd->flip_status != flip) + sd->flip_debounce++; + else + sd->flip_debounce = 0; + + /* check sequence number (not present in new frame packets) */ + if (!(hdr->flags & STK1135_HDR_FRAME_START)) { + seq = hdr->seq & STK1135_HDR_SEQ_MASK; + if (seq != sd->pkt_seq) { + PDEBUG(D_PACK, "received out-of-sequence packet"); + /* resync sequence and discard packet */ + sd->pkt_seq = seq; + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + } + sd->pkt_seq++; + if (sd->pkt_seq > STK1135_HDR_SEQ_MASK) + sd->pkt_seq = 0; + + if (len == sizeof(struct stk1135_pkt_header)) + return; + + if (hdr->flags & STK1135_HDR_FRAME_START) { /* new frame */ + skip = 8; /* the header is longer */ + gspca_frame_add(gspca_dev, LAST_PACKET, data, 0); + pkt_type = FIRST_PACKET; + } + gspca_frame_add(gspca_dev, pkt_type, data + skip, len - skip); +} + +static void sethflip(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->flip_status) + val = !val; + sensor_write_mask(gspca_dev, 0x020, val ? 0x0002 : 0x0000 , 0x0002); +} + +static void setvflip(struct gspca_dev *gspca_dev, s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->flip_status) + val = !val; + sensor_write_mask(gspca_dev, 0x020, val ? 0x0001 : 0x0000 , 0x0001); +} + +static void stk1135_dq_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->flip_debounce > 100) { + sd->flip_status = !sd->flip_status; + sethflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip)); + setvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->vflip)); + } +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + sethflip(gspca_dev, ctrl->val); + break; + case V4L2_CID_VFLIP: + setvflip(gspca_dev, ctrl->val); + break; + } + + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 2); + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .dq_callback = stk1135_dq_callback, +}; + +/* -- module initialisation -- */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x174f, 0x6a31)}, /* ASUS laptop, MT9M112 sensor */ + {} +}; +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); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +module_usb_driver(sd_driver); diff --git a/drivers/media/usb/gspca/stk1135.h b/drivers/media/usb/gspca/stk1135.h new file mode 100644 index 0000000..e1dd92a --- /dev/null +++ b/drivers/media/usb/gspca/stk1135.h @@ -0,0 +1,57 @@ +/* + * STK1135 registers + * + * Copyright (c) 2013 Ondrej Zary + * + * 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 + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define STK1135_REG_GCTRL 0x000 /* GPIO control */ +#define STK1135_REG_ICTRL 0x004 /* Interrupt control */ +#define STK1135_REG_IDATA 0x008 /* Interrupt data */ +#define STK1135_REG_RMCTL 0x00c /* Remote wakeup control */ +#define STK1135_REG_POSVA 0x010 /* Power-on strapping data */ + +#define STK1135_REG_SENSO 0x018 /* Sensor select options */ +#define STK1135_REG_PLLFD 0x01c /* PLL frequency divider */ + +#define STK1135_REG_SCTRL 0x100 /* Sensor control register */ +#define STK1135_REG_DCTRL 0x104 /* Decimation control register */ +#define STK1135_REG_CISPO 0x110 /* Capture image starting position */ +#define STK1135_REG_CIEPO 0x114 /* Capture image ending position */ +#define STK1135_REG_TCTRL 0x120 /* Test data control */ + +#define STK1135_REG_SICTL 0x200 /* Serial interface control register */ +#define STK1135_REG_SBUSW 0x204 /* Serial bus write */ +#define STK1135_REG_SBUSR 0x208 /* Serial bus read */ +#define STK1135_REG_SCSI 0x20c /* Software control serial interface */ +#define STK1135_REG_GSBWP 0x210 /* General serial bus write port */ +#define STK1135_REG_GSBRP 0x214 /* General serial bus read port */ +#define STK1135_REG_ASIC 0x2fc /* Alternate serial interface control */ + +#define STK1135_REG_TMGEN 0x300 /* Timing generator */ +#define STK1135_REG_TCP1 0x350 /* Timing control parameter 1 */ + +struct stk1135_pkt_header { + u8 flags; + u8 seq; + __le16 gpio; +} __packed; + +#define STK1135_HDR_FRAME_START (1 << 7) +#define STK1135_HDR_ODD (1 << 6) +#define STK1135_HDR_I2C_VBLANK (1 << 5) + +#define STK1135_HDR_SEQ_MASK 0x3f -- cgit v0.10.2 From 3300a8fd48976e7126cf078de95f52d59e413bb0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 5 Jul 2013 07:16:02 -0300 Subject: [media] mt9v032: Use the common clock framework Configure the device external clock using the common clock framework instead of a board code callback function. Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 60c6f67..2c50eff 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -12,6 +12,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -135,6 +136,8 @@ struct mt9v032 { struct mutex power_lock; int power_count; + struct clk *clk; + struct mt9v032_platform_data *pdata; u32 sysclk; @@ -219,10 +222,9 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032) struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); int ret; - if (mt9v032->pdata->set_clock) { - mt9v032->pdata->set_clock(&mt9v032->subdev, mt9v032->sysclk); - udelay(1); - } + clk_set_rate(mt9v032->clk, mt9v032->sysclk); + clk_prepare_enable(mt9v032->clk); + udelay(1); /* Reset the chip and stop data read out */ ret = mt9v032_write(client, MT9V032_RESET, 1); @@ -238,8 +240,7 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032) static void mt9v032_power_off(struct mt9v032 *mt9v032) { - if (mt9v032->pdata->set_clock) - mt9v032->pdata->set_clock(&mt9v032->subdev, 0); + clk_disable_unprepare(mt9v032->clk); } static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) @@ -748,6 +749,10 @@ static int mt9v032_probe(struct i2c_client *client, if (!mt9v032) return -ENOMEM; + mt9v032->clk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(mt9v032->clk)) + return PTR_ERR(mt9v032->clk); + mutex_init(&mt9v032->power_lock); mt9v032->pdata = pdata; diff --git a/include/media/mt9v032.h b/include/media/mt9v032.h index 78fd39e..12175a6 100644 --- a/include/media/mt9v032.h +++ b/include/media/mt9v032.h @@ -1,13 +1,9 @@ #ifndef _MEDIA_MT9V032_H #define _MEDIA_MT9V032_H -struct v4l2_subdev; - struct mt9v032_platform_data { unsigned int clk_pol:1; - void (*set_clock)(struct v4l2_subdev *subdev, unsigned int rate); - const s64 *link_freqs; s64 link_def_freq; }; -- cgit v0.10.2 From 7be5c289818cd5a968b35cdaa1e82985a930b5a8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 10 Aug 2013 14:49:45 -0300 Subject: [media] smiapp: re-use clamp_t instead of min(..., max(...)) clamp_t does the job to put a variable into the given range. clamp_t -> clamp as agreed with Andy. Signed-off-by: Andy Shevchenko Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart 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 7ac7580..4d7ba54 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1835,12 +1835,12 @@ static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]; - a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], - max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); - b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], - max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); - max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], - max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + a = clamp(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN], + sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX]); + b = clamp(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN], + sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX]); + max_m = clamp(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN], + sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX]); dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); -- cgit v0.10.2 From 367da7a31804ee76ebce5bc8706fcda507008ac3 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 10 Aug 2013 14:49:46 -0300 Subject: [media] smiapp-pll: Add a few comments to PLL calculation The PLL calculation heuristics is rather complicated and and is often difficult to understand to its original author. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c index d8d5da7..2335529 100644 --- a/drivers/media/i2c/smiapp-pll.c +++ b/drivers/media/i2c/smiapp-pll.c @@ -87,6 +87,17 @@ static void print_pll(struct device *dev, struct smiapp_pll *pll) dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); } +/* + * Heuristically guess the PLL tree for a given common multiplier and + * divisor. Begin with the operational timing and continue to video + * timing once operational timing has been verified. + * + * @mul is the PLL multiplier and @div is the common divisor + * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL + * multiplier will be a multiple of @mul. + * + * @return Zero on success, error code on error. + */ static int __smiapp_pll_calculate(struct device *dev, const struct smiapp_pll_limits *limits, struct smiapp_pll *pll, uint32_t mul, @@ -95,6 +106,12 @@ static int __smiapp_pll_calculate(struct device *dev, uint32_t sys_div; uint32_t best_pix_div = INT_MAX >> 1; uint32_t vt_op_binning_div; + /* + * Higher multipliers (and divisors) are often required than + * necessitated by the external clock and the output clocks. + * There are limits for all values in the clock tree. These + * are the minimum and maximum multiplier for mul. + */ uint32_t more_mul_min, more_mul_max; uint32_t more_mul_factor; uint32_t min_vt_div, max_vt_div, vt_div; -- cgit v0.10.2 From d0aae0048aef36065de953878422c475f53230d0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 10 Aug 2013 14:49:47 -0300 Subject: [media] smiapp: Prepare and unprepare clocks correctly Prepare clocks before enabling and unprepare after disabling them. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart 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 4d7ba54..7de9892 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1122,9 +1122,9 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) rval = sensor->platform_data->set_xclk( &sensor->src->sd, sensor->platform_data->ext_clk); else - rval = clk_enable(sensor->ext_clk); + rval = clk_prepare_enable(sensor->ext_clk); if (rval < 0) { - dev_dbg(&client->dev, "failed to set xclk\n"); + dev_dbg(&client->dev, "failed to enable xclk\n"); goto out_xclk_fail; } usleep_range(1000, 1000); @@ -1244,7 +1244,7 @@ out_cci_addr_fail: if (sensor->platform_data->set_xclk) sensor->platform_data->set_xclk(&sensor->src->sd, 0); else - clk_disable(sensor->ext_clk); + clk_disable_unprepare(sensor->ext_clk); out_xclk_fail: regulator_disable(sensor->vana); @@ -1270,7 +1270,7 @@ static void smiapp_power_off(struct smiapp_sensor *sensor) if (sensor->platform_data->set_xclk) sensor->platform_data->set_xclk(&sensor->src->sd, 0); else - clk_disable(sensor->ext_clk); + clk_disable_unprepare(sensor->ext_clk); usleep_range(5000, 5000); regulator_disable(sensor->vana); sensor->streaming = 0; @@ -2839,7 +2839,7 @@ static int smiapp_remove(struct i2c_client *client) if (sensor->platform_data->set_xclk) sensor->platform_data->set_xclk(&sensor->src->sd, 0); else - clk_disable(sensor->ext_clk); + clk_disable_unprepare(sensor->ext_clk); sensor->power_count = 0; } -- cgit v0.10.2 From a354177f058541b7212230feb2c0da7c464e9b9d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 10 Aug 2013 14:49:48 -0300 Subject: [media] smiapp: Call the clock "ext_clk" As the clock framework makes it possible to assign a device specific name to the clocks, remove the ability to use a named clock in the driver. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart 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 7de9892..ae66d91 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2363,11 +2363,9 @@ static int smiapp_registered(struct v4l2_subdev *subdev) } if (!sensor->platform_data->set_xclk) { - sensor->ext_clk = devm_clk_get(&client->dev, - sensor->platform_data->ext_clk_name); + sensor->ext_clk = devm_clk_get(&client->dev, "ext_clk"); if (IS_ERR(sensor->ext_clk)) { - dev_err(&client->dev, "could not get clock %s\n", - sensor->platform_data->ext_clk_name); + dev_err(&client->dev, "could not get clock\n"); return -ENODEV; } @@ -2375,8 +2373,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev) sensor->platform_data->ext_clk); if (rval < 0) { dev_err(&client->dev, - "unable to set clock %s freq to %u\n", - sensor->platform_data->ext_clk_name, + "unable to set clock freq to %u\n", sensor->platform_data->ext_clk); return -ENODEV; } diff --git a/include/media/smiapp.h b/include/media/smiapp.h index 07f96a8..0b8f124 100644 --- a/include/media/smiapp.h +++ b/include/media/smiapp.h @@ -77,7 +77,6 @@ struct smiapp_platform_data { struct smiapp_flash_strobe_parms *strobe_setup; int (*set_xclk)(struct v4l2_subdev *sd, int hz); - char *ext_clk_name; int xshutdown; /* gpio or SMIAPP_NO_XSHUTDOWN */ }; -- cgit v0.10.2 From 38e35a85284b9cce271ff43a849d3676a46f8512 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 30 Jul 2013 19:00:00 -0300 Subject: [media] redrat3: ensure whole packet is read The length in the header excludes the header itself, so we're getting spurious readings. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 0042367..ccd267f 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -663,7 +663,8 @@ static int redrat3_get_ir_data(struct redrat3_dev *rr3, unsigned len) goto out; } - if (rr3->bytes_read < be16_to_cpu(rr3->irdata.header.length)) + if (rr3->bytes_read < be16_to_cpu(rr3->irdata.header.length) + + sizeof(struct redrat3_header)) /* we're still accumulating data */ return 0; -- cgit v0.10.2 From 153a60bb0faca59bfde313d4c713d9420e475b75 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 30 Jul 2013 19:00:01 -0300 Subject: [media] rc: add feedback led trigger for rc keypresses Many devices with an ir receiver also have a feedback led. Add the led trigger to support this. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 1dedebd..aa5d8e7 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ /* Used to keep track of known keymaps */ static LIST_HEAD(rc_map_list); static DEFINE_SPINLOCK(rc_map_lock); +static struct led_trigger *led_feedback; static struct rc_map_list *seek_rc_map(const char *name) { @@ -535,6 +537,7 @@ static void ir_do_keyup(struct rc_dev *dev, bool sync) IR_dprintk(1, "keyup key 0x%04x\n", dev->last_keycode); input_report_key(dev->input_dev, dev->last_keycode, 0); + led_trigger_event(led_feedback, LED_OFF); if (sync) input_sync(dev->input_dev); dev->keypressed = false; @@ -648,6 +651,7 @@ static void ir_do_keydown(struct rc_dev *dev, int scancode, input_report_key(dev->input_dev, keycode, 1); } + led_trigger_event(led_feedback, LED_FULL); input_sync(dev->input_dev); } @@ -1222,6 +1226,7 @@ static int __init rc_core_init(void) return rc; } + led_trigger_register_simple("rc-feedback", &led_feedback); rc_map_register(&empty_map); return 0; @@ -1230,6 +1235,7 @@ static int __init rc_core_init(void) static void __exit rc_core_exit(void) { class_unregister(&rc_class); + led_trigger_unregister_simple(led_feedback); rc_map_unregister(&empty_map); } -- cgit v0.10.2 From bf139726892c3e81538dd33d96cdf18eba6c1e53 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 30 Jul 2013 19:00:04 -0300 Subject: [media] redrat3: wire up rc feedback led Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 5a79c33..0bf4ac7 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -223,6 +223,8 @@ config IR_REDRAT3 tristate "RedRat3 IR Transceiver" depends on USB_ARCH_HAS_HCD depends on RC_CORE + select NEW_LEDS + select LEDS_CLASS select USB ---help--- Say Y here if you want to use a RedRat3 Infrared Transceiver. diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index ccd267f..094484f 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -186,6 +187,13 @@ struct redrat3_dev { struct rc_dev *rc; struct device *dev; + /* led control */ + struct led_classdev led; + atomic_t flash; + struct usb_ctrlrequest flash_control; + struct urb *flash_urb; + u8 flash_in_buf; + /* save off the usb device pointer */ struct usb_device *udev; @@ -480,9 +488,9 @@ static inline void redrat3_delete(struct redrat3_dev *rr3, { rr3_ftr(rr3->dev, "%s cleaning up\n", __func__); usb_kill_urb(rr3->read_urb); - + usb_kill_urb(rr3->flash_urb); usb_free_urb(rr3->read_urb); - + usb_free_urb(rr3->flash_urb); usb_free_coherent(udev, le16_to_cpu(rr3->ep_in->wMaxPacketSize), rr3->bulk_in_buf, rr3->dma_in); @@ -850,6 +858,44 @@ out: return ret; } +static void redrat3_brightness_set(struct led_classdev *led_dev, enum + led_brightness brightness) +{ + struct redrat3_dev *rr3 = container_of(led_dev, struct redrat3_dev, + led); + + if (brightness != LED_OFF && atomic_cmpxchg(&rr3->flash, 0, 1) == 0) { + int ret = usb_submit_urb(rr3->flash_urb, GFP_ATOMIC); + if (ret != 0) { + dev_dbg(rr3->dev, "%s: unexpected ret of %d\n", + __func__, ret); + atomic_set(&rr3->flash, 0); + } + } +} + +static void redrat3_led_complete(struct urb *urb) +{ + struct redrat3_dev *rr3 = urb->context; + + switch (urb->status) { + case 0: + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + usb_unlink_urb(urb); + return; + case -EPIPE: + default: + dev_dbg(rr3->dev, "Error: urb status = %d\n", urb->status); + break; + } + + rr3->led.brightness = LED_OFF; + atomic_dec(&rr3->flash); +} + static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3) { struct device *dev = rr3->dev; @@ -993,10 +1039,35 @@ static int redrat3_dev_probe(struct usb_interface *intf, /* default.. will get overridden by any sends with a freq defined */ rr3->carrier = 38000; + /* led control */ + rr3->led.name = "redrat3:red:feedback"; + rr3->led.default_trigger = "rc-feedback"; + rr3->led.brightness_set = redrat3_brightness_set; + retval = led_classdev_register(&intf->dev, &rr3->led); + if (retval) + goto error; + + atomic_set(&rr3->flash, 0); + rr3->flash_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rr3->flash_urb) { + retval = -ENOMEM; + goto led_free_error; + } + + /* setup packet is 'c0 b9 0000 0000 0001' */ + rr3->flash_control.bRequestType = 0xc0; + rr3->flash_control.bRequest = RR3_BLINK_LED; + rr3->flash_control.wLength = cpu_to_le16(1); + + usb_fill_control_urb(rr3->flash_urb, udev, usb_rcvctrlpipe(udev, 0), + (unsigned char *)&rr3->flash_control, + &rr3->flash_in_buf, sizeof(rr3->flash_in_buf), + redrat3_led_complete, rr3); + rr3->rc = redrat3_init_rc_dev(rr3); if (!rr3->rc) { retval = -ENOMEM; - goto error; + goto led_free_error; } setup_timer(&rr3->rx_timeout, redrat3_rx_timeout, (unsigned long)rr3); @@ -1006,6 +1077,8 @@ static int redrat3_dev_probe(struct usb_interface *intf, rr3_ftr(dev, "Exiting %s\n", __func__); return 0; +led_free_error: + led_classdev_unregister(&rr3->led); error: redrat3_delete(rr3, rr3->udev); @@ -1027,6 +1100,7 @@ static void redrat3_dev_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); rc_unregister_device(rr3->rc); + led_classdev_unregister(&rr3->led); del_timer_sync(&rr3->rx_timeout); redrat3_delete(rr3, udev); @@ -1037,7 +1111,9 @@ static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message) { struct redrat3_dev *rr3 = usb_get_intfdata(intf); rr3_ftr(rr3->dev, "suspend\n"); + led_classdev_suspend(&rr3->led); usb_kill_urb(rr3->read_urb); + usb_kill_urb(rr3->flash_urb); return 0; } @@ -1047,6 +1123,7 @@ static int redrat3_dev_resume(struct usb_interface *intf) rr3_ftr(rr3->dev, "resume\n"); if (usb_submit_urb(rr3->read_urb, GFP_ATOMIC)) return -EIO; + led_classdev_resume(&rr3->led); return 0; } -- cgit v0.10.2 From 5704e76c86a6bccbe41632041744a481c5bcb247 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 30 Jul 2013 19:00:02 -0300 Subject: [media] ttusbir: wire up rc feedback led Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index 891762d..d8de205 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -302,6 +302,7 @@ static int ttusbir_probe(struct usb_interface *intf, ttusbir_bulk_complete, tt); tt->led.name = "ttusbir:green:power"; + tt->led.default_trigger = "rc-feedback"; tt->led.brightness_set = ttusbir_brightness_set; tt->led.brightness_get = ttusbir_brightness_get; tt->is_led_on = tt->led_on = true; -- cgit v0.10.2 From 1ac7fdeeb52b05e108713d1ccc63e714c2489717 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 30 Jul 2013 19:00:03 -0300 Subject: [media] winbond: wire up rc feedback led MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note that with the rc-feedback trigger, the cir-rx trigger is now redundant. The cir-tx trigger is not used by default; if this functionality is desired then it should exist in rc-core, not in a driver. Also make sure that the led is suspended on suspend. Signed-off-by: Sean Young 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 0bf4ac7..11e84bc 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -250,7 +250,6 @@ config IR_WINBOND_CIR depends on RC_CORE select NEW_LEDS select LEDS_CLASS - select LEDS_TRIGGERS select BITREVERSE ---help--- Say Y here if you want to use the IR remote functionality found diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 87af2d3..98bd496 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -213,13 +213,11 @@ struct wbcir_data { /* RX state */ enum wbcir_rxstate rxstate; - struct led_trigger *rxtrigger; int carrier_report_enabled; u32 pulse_duration; /* TX state */ enum wbcir_txstate txstate; - struct led_trigger *txtrigger; u32 txlen; u32 txoff; u32 *txbuf; @@ -366,14 +364,11 @@ wbcir_idle_rx(struct rc_dev *dev, bool idle) { struct wbcir_data *data = dev->priv; - if (!idle && data->rxstate == WBCIR_RXSTATE_INACTIVE) { + if (!idle && data->rxstate == WBCIR_RXSTATE_INACTIVE) data->rxstate = WBCIR_RXSTATE_ACTIVE; - led_trigger_event(data->rxtrigger, LED_FULL); - } if (idle && data->rxstate != WBCIR_RXSTATE_INACTIVE) { data->rxstate = WBCIR_RXSTATE_INACTIVE; - led_trigger_event(data->rxtrigger, LED_OFF); if (data->carrier_report_enabled) wbcir_carrier_report(data); @@ -425,7 +420,6 @@ wbcir_irq_tx(struct wbcir_data *data) case WBCIR_TXSTATE_INACTIVE: /* TX FIFO empty */ space = 16; - led_trigger_event(data->txtrigger, LED_FULL); break; case WBCIR_TXSTATE_ACTIVE: /* TX FIFO low (3 bytes or less) */ @@ -464,7 +458,6 @@ wbcir_irq_tx(struct wbcir_data *data) /* Clear TX underrun bit */ outb(WBCIR_TX_UNDERRUN, data->sbase + WBCIR_REG_SP3_ASCR); wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR); - led_trigger_event(data->txtrigger, LED_OFF); kfree(data->txbuf); data->txbuf = NULL; data->txstate = WBCIR_TXSTATE_INACTIVE; @@ -878,15 +871,13 @@ finish: */ wbcir_set_irqmask(data, WBCIR_IRQ_NONE); disable_irq(data->irq); - - /* Disable LED */ - led_trigger_event(data->rxtrigger, LED_OFF); - led_trigger_event(data->txtrigger, LED_OFF); } static int wbcir_suspend(struct pnp_dev *device, pm_message_t state) { + struct wbcir_data *data = pnp_get_drvdata(device); + led_classdev_suspend(&data->led); wbcir_shutdown(device); return 0; } @@ -1015,6 +1006,7 @@ wbcir_resume(struct pnp_dev *device) wbcir_init_hw(data); enable_irq(data->irq); + led_classdev_resume(&data->led); return 0; } @@ -1058,25 +1050,13 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n", data->wbase, data->ebase, data->sbase, data->irq); - led_trigger_register_simple("cir-tx", &data->txtrigger); - if (!data->txtrigger) { - err = -ENOMEM; - goto exit_free_data; - } - - led_trigger_register_simple("cir-rx", &data->rxtrigger); - if (!data->rxtrigger) { - err = -ENOMEM; - goto exit_unregister_txtrigger; - } - data->led.name = "cir::activity"; - data->led.default_trigger = "cir-rx"; + data->led.default_trigger = "rc-feedback"; data->led.brightness_set = wbcir_led_brightness_set; data->led.brightness_get = wbcir_led_brightness_get; err = led_classdev_register(&device->dev, &data->led); if (err) - goto exit_unregister_rxtrigger; + goto exit_free_data; data->dev = rc_allocate_device(); if (!data->dev) { @@ -1156,10 +1136,6 @@ exit_free_rc: rc_free_device(data->dev); exit_unregister_led: led_classdev_unregister(&data->led); -exit_unregister_rxtrigger: - led_trigger_unregister_simple(data->rxtrigger); -exit_unregister_txtrigger: - led_trigger_unregister_simple(data->txtrigger); exit_free_data: kfree(data); pnp_set_drvdata(device, NULL); @@ -1187,8 +1163,6 @@ wbcir_remove(struct pnp_dev *device) rc_unregister_device(data->dev); - led_trigger_unregister_simple(data->rxtrigger); - led_trigger_unregister_simple(data->txtrigger); led_classdev_unregister(&data->led); /* This is ok since &data->led isn't actually used */ -- cgit v0.10.2 From a517cca6b24fc54ac209e44118ec8962051662e3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 9 Aug 2013 08:11:25 -0300 Subject: [media] media: vb2: Fix potential deadlock in vb2_prepare_buffer Commit b037c0fde22b1d3cd0b3c3717d28e54619fc1592 ("media: vb2: fix potential deadlock in mmap vs. get_userptr handling") fixes an AB-BA deadlock related to the mmap_sem and driver locks. The same deadlock can occur in vb2_prepare_buffer(), fix it the same way. Signed-off-by: Laurent Pinchart Reviewed-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 35a5b8f..fb6802c 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1248,50 +1248,84 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) */ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) { + struct rw_semaphore *mmap_sem = NULL; struct vb2_buffer *vb; int ret; + /* + * In case of user pointer buffers vb2 allocator needs to get direct + * access to userspace pages. This requires getting read access on + * mmap semaphore in the current process structure. The same + * semaphore is taken before calling mmap operation, while both mmap + * and prepare_buf are called by the driver or v4l2 core with driver's + * lock held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in + * mmap and driver's lock then mmap_sem in prepare_buf) the videobuf2 + * core release driver's lock, takes mmap_sem and then takes again + * driver's lock. + * + * To avoid race with other vb2 calls, which might be called after + * releasing driver's lock, this operation is performed at the + * beggining of prepare_buf processing. This way the queue status is + * consistent after getting driver's lock back. + */ + if (q->memory == V4L2_MEMORY_USERPTR) { + mmap_sem = ¤t->mm->mmap_sem; + call_qop(q, wait_prepare, q); + down_read(mmap_sem); + call_qop(q, wait_finish, q); + } + if (q->fileio) { dprintk(1, "%s(): file io in progress\n", __func__); - return -EBUSY; + ret = -EBUSY; + goto unlock; } if (b->type != q->type) { dprintk(1, "%s(): invalid buffer type\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto unlock; } if (b->index >= q->num_buffers) { dprintk(1, "%s(): buffer index out of range\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto unlock; } vb = q->bufs[b->index]; if (NULL == vb) { /* Should never happen */ dprintk(1, "%s(): buffer is NULL\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto unlock; } if (b->memory != q->memory) { dprintk(1, "%s(): invalid memory type\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto unlock; } if (vb->state != VB2_BUF_STATE_DEQUEUED) { dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state); - return -EINVAL; + ret = -EINVAL; + goto unlock; } ret = __verify_planes_array(vb, b); if (ret < 0) - return ret; + goto unlock; + ret = __buf_prepare(vb, b); if (ret < 0) - return ret; + goto unlock; __fill_v4l2_buffer(vb, b); - return 0; +unlock: + if (mmap_sem) + up_read(mmap_sem); + return ret; } EXPORT_SYMBOL_GPL(vb2_prepare_buf); -- cgit v0.10.2 From 012043b8346b2e288a791ca4a0f4f9db815b8a0a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 9 Aug 2013 08:11:26 -0300 Subject: [media] media: vb2: Share code between vb2_prepare_buf and vb2_qbuf The two operations are very similar, refactor most of the code in a helper function. Signed-off-by: Laurent Pinchart Reviewed-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 fb6802c..594c75e 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1231,42 +1231,31 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) return ret; } -/** - * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_prepare_buf - * handler in driver - * - * Should be called from vidioc_prepare_buf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) calls buf_prepare callback in the driver (if provided), in which - * driver-specific buffer initialization can be performed, - * - * The return values from this function are intended to be directly returned - * from vidioc_prepare_buf handler in driver. - */ -int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) +static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, + const char *opname, + int (*handler)(struct vb2_queue *, + struct v4l2_buffer *, + struct vb2_buffer *)) { struct rw_semaphore *mmap_sem = NULL; struct vb2_buffer *vb; int ret; /* - * In case of user pointer buffers vb2 allocator needs to get direct - * access to userspace pages. This requires getting read access on - * mmap semaphore in the current process structure. The same - * semaphore is taken before calling mmap operation, while both mmap - * and prepare_buf are called by the driver or v4l2 core with driver's - * lock held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in - * mmap and driver's lock then mmap_sem in prepare_buf) the videobuf2 - * core release driver's lock, takes mmap_sem and then takes again - * driver's lock. + * In case of user pointer buffers vb2 allocators need to get direct + * access to userspace pages. This requires getting the mmap semaphore + * for read access in the current process structure. The same semaphore + * is taken before calling mmap operation, while both qbuf/prepare_buf + * and mmap are called by the driver or v4l2 core with the driver's lock + * held. To avoid an AB-BA deadlock (mmap_sem then driver's lock in mmap + * and driver's lock then mmap_sem in qbuf/prepare_buf) the videobuf2 + * core releases the driver's lock, takes mmap_sem and then takes the + * driver's lock again. * - * To avoid race with other vb2 calls, which might be called after - * releasing driver's lock, this operation is performed at the - * beggining of prepare_buf processing. This way the queue status is - * consistent after getting driver's lock back. + * To avoid racing with other vb2 calls, which might be called after + * releasing the driver's lock, this operation is performed at the + * beginning of qbuf/prepare_buf processing. This way the queue status + * is consistent after getting the driver's lock back. */ if (q->memory == V4L2_MEMORY_USERPTR) { mmap_sem = ¤t->mm->mmap_sem; @@ -1276,19 +1265,19 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) } if (q->fileio) { - dprintk(1, "%s(): file io in progress\n", __func__); + dprintk(1, "%s(): file io in progress\n", opname); ret = -EBUSY; goto unlock; } if (b->type != q->type) { - dprintk(1, "%s(): invalid buffer type\n", __func__); + dprintk(1, "%s(): invalid buffer type\n", opname); ret = -EINVAL; goto unlock; } if (b->index >= q->num_buffers) { - dprintk(1, "%s(): buffer index out of range\n", __func__); + dprintk(1, "%s(): buffer index out of range\n", opname); ret = -EINVAL; goto unlock; } @@ -1296,131 +1285,83 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) vb = q->bufs[b->index]; if (NULL == vb) { /* Should never happen */ - dprintk(1, "%s(): buffer is NULL\n", __func__); + dprintk(1, "%s(): buffer is NULL\n", opname); ret = -EINVAL; goto unlock; } if (b->memory != q->memory) { - dprintk(1, "%s(): invalid memory type\n", __func__); + dprintk(1, "%s(): invalid memory type\n", opname); ret = -EINVAL; goto unlock; } - if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state); - ret = -EINVAL; - goto unlock; - } ret = __verify_planes_array(vb, b); - if (ret < 0) + if (ret) goto unlock; - ret = __buf_prepare(vb, b); - if (ret < 0) + ret = handler(q, b, vb); + if (ret) goto unlock; + /* Fill buffer information for the userspace */ __fill_v4l2_buffer(vb, b); + dprintk(1, "%s() of buffer %d succeeded\n", opname, vb->v4l2_buf.index); unlock: if (mmap_sem) up_read(mmap_sem); return ret; } -EXPORT_SYMBOL_GPL(vb2_prepare_buf); + +static int __vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, + struct vb2_buffer *vb) +{ + if (vb->state != VB2_BUF_STATE_DEQUEUED) { + dprintk(1, "%s(): invalid buffer state %d\n", __func__, + vb->state); + return -EINVAL; + } + + return __buf_prepare(vb, b); +} /** - * vb2_qbuf() - Queue a buffer from userspace + * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_qbuf handler - * in driver + * @b: buffer structure passed from userspace to vidioc_prepare_buf + * handler in driver * - * Should be called from vidioc_qbuf ioctl handler of a driver. + * Should be called from vidioc_prepare_buf ioctl handler of a driver. * This function: * 1) verifies the passed buffer, - * 2) if necessary, calls buf_prepare callback in the driver (if provided), in - * which driver-specific buffer initialization can be performed, - * 3) if streaming is on, queues the buffer in driver by the means of buf_queue - * callback for processing. + * 2) calls buf_prepare callback in the driver (if provided), in which + * driver-specific buffer initialization can be performed, * * The return values from this function are intended to be directly returned - * from vidioc_qbuf handler in driver. + * from vidioc_prepare_buf handler in driver. */ -int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) +int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) { - struct rw_semaphore *mmap_sem = NULL; - struct vb2_buffer *vb; - int ret = 0; - - /* - * In case of user pointer buffers vb2 allocator needs to get direct - * access to userspace pages. This requires getting read access on - * mmap semaphore in the current process structure. The same - * semaphore is taken before calling mmap operation, while both mmap - * and qbuf are called by the driver or v4l2 core with driver's lock - * held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in - * mmap and driver's lock then mmap_sem in qbuf) the videobuf2 core - * release driver's lock, takes mmap_sem and then takes again driver's - * lock. - * - * To avoid race with other vb2 calls, which might be called after - * releasing driver's lock, this operation is performed at the - * beggining of qbuf processing. This way the queue status is - * consistent after getting driver's lock back. - */ - if (q->memory == V4L2_MEMORY_USERPTR) { - mmap_sem = ¤t->mm->mmap_sem; - call_qop(q, wait_prepare, q); - down_read(mmap_sem); - call_qop(q, wait_finish, q); - } - - if (q->fileio) { - dprintk(1, "qbuf: file io in progress\n"); - ret = -EBUSY; - goto unlock; - } - - if (b->type != q->type) { - dprintk(1, "qbuf: invalid buffer type\n"); - ret = -EINVAL; - goto unlock; - } - - if (b->index >= q->num_buffers) { - dprintk(1, "qbuf: buffer index out of range\n"); - ret = -EINVAL; - goto unlock; - } - - vb = q->bufs[b->index]; - if (NULL == vb) { - /* Should never happen */ - dprintk(1, "qbuf: buffer is NULL\n"); - ret = -EINVAL; - goto unlock; - } + return vb2_queue_or_prepare_buf(q, b, "prepare_buf", __vb2_prepare_buf); +} +EXPORT_SYMBOL_GPL(vb2_prepare_buf); - if (b->memory != q->memory) { - dprintk(1, "qbuf: invalid memory type\n"); - ret = -EINVAL; - goto unlock; - } - ret = __verify_planes_array(vb, b); - if (ret) - goto unlock; +static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b, + struct vb2_buffer *vb) +{ + int ret; switch (vb->state) { case VB2_BUF_STATE_DEQUEUED: ret = __buf_prepare(vb, b); if (ret) - goto unlock; + return ret; case VB2_BUF_STATE_PREPARED: break; default: dprintk(1, "qbuf: buffer already in use\n"); - ret = -EINVAL; - goto unlock; + return -EINVAL; } /* @@ -1437,14 +1378,29 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) if (q->streaming) __enqueue_in_driver(vb); - /* Fill buffer information for the userspace */ - __fill_v4l2_buffer(vb, b); + return 0; +} - dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index); -unlock: - if (mmap_sem) - up_read(mmap_sem); - return ret; +/** + * vb2_qbuf() - Queue a buffer from userspace + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_qbuf handler + * in driver + * + * Should be called from vidioc_qbuf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) if necessary, calls buf_prepare callback in the driver (if provided), in + * which driver-specific buffer initialization can be performed, + * 3) if streaming is on, queues the buffer in driver by the means of buf_queue + * callback for processing. + * + * The return values from this function are intended to be directly returned + * from vidioc_qbuf handler in driver. + */ +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + return vb2_queue_or_prepare_buf(q, b, "qbuf", __vb2_qbuf); } EXPORT_SYMBOL_GPL(vb2_qbuf); -- cgit v0.10.2 From 62a37dc7a27366e915413884a8b69f8fbf9d72ea Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 9 Aug 2013 08:46:11 -0300 Subject: [media] MAINTAINERS: Add entry for the Aptina PLL library Add a maintainers entry for the Aptina PLL library, and rename the Aptina sensors entries to make it clear they refer to Aptina camera sensors. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index bf61e04..9b12947 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -638,6 +638,12 @@ S: Maintained F: drivers/net/appletalk/ F: net/appletalk/ +APTINA CAMERA SENSOR PLL +M: Laurent Pinchart +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/aptina-pll.* + ARASAN COMPACT FLASH PATA CONTROLLER M: Viresh Kumar L: linux-ide@vger.kernel.org @@ -5496,7 +5502,7 @@ L: platform-driver-x86@vger.kernel.org S: Supported F: drivers/platform/x86/msi-wmi.c -MT9M032 SENSOR DRIVER +MT9M032 APTINA SENSOR DRIVER M: Laurent Pinchart L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git @@ -5504,7 +5510,7 @@ S: Maintained F: drivers/media/i2c/mt9m032.c F: include/media/mt9m032.h -MT9P031 SENSOR DRIVER +MT9P031 APTINA CAMERA SENSOR M: Laurent Pinchart L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git @@ -5512,7 +5518,7 @@ S: Maintained F: drivers/media/i2c/mt9p031.c F: include/media/mt9p031.h -MT9T001 SENSOR DRIVER +MT9T001 APTINA CAMERA SENSOR M: Laurent Pinchart L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git @@ -5520,7 +5526,7 @@ S: Maintained F: drivers/media/i2c/mt9t001.c F: include/media/mt9t001.h -MT9V032 SENSOR DRIVER +MT9V032 APTINA CAMERA SENSOR M: Laurent Pinchart L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git -- cgit v0.10.2 From 15472faf12591a3667fe729d66ae9bf48c8bd1bb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 9 Aug 2013 08:53:25 -0300 Subject: [media] cx23885-dvb: use a better approach to hook set_frontend When the frontend drivers got converted to DVBv5 API, the original hook that tracked when a frontend is set got removed, being replaced by an approach that would use the gate control. That doesn't work fine with some boards. Also, the code were called more times than desired. Replace it by a logic that will hook the dvb set_frontend ops, with works with both DVBv3 and DVBv5 calls. Tested on a Mygica X8502 OEM board. Tested-by: Alfredo Delaiti 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 bb291c6..a25a037 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -119,8 +119,6 @@ static void dvb_buf_release(struct videobuf_queue *q, cx23885_free_buffer(q, (struct cx23885_buffer *)vb); } -static int cx23885_dvb_set_frontend(struct dvb_frontend *fe); - static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open) { struct videobuf_dvb_frontends *f; @@ -135,12 +133,6 @@ static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open) if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl) fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open); - - /* - * FIXME: Improve this path to avoid calling the - * cx23885_dvb_set_frontend() every time it passes here. - */ - cx23885_dvb_set_frontend(fe->dvb.frontend); } static struct videobuf_queue_ops dvb_qops = { @@ -561,9 +553,21 @@ static int cx23885_dvb_set_frontend(struct dvb_frontend *fe) cx23885_gpio_set(dev, GPIO_0); break; } + + /* Call the real set_frontend */ + if (port->set_frontend) + return port->set_frontend(fe); + return 0; } +static void cx23885_set_frontend_hook(struct cx23885_tsport *port, + struct dvb_frontend *fe) +{ + port->set_frontend = fe->ops.set_frontend; + fe->ops.set_frontend = cx23885_dvb_set_frontend; +} + static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = { .prod = LGS8GXX_PROD_LGS8G75, .demod_address = 0x19, @@ -771,6 +775,8 @@ static int dvb_register(struct cx23885_tsport *port) 0x60, &dev->i2c_bus[1].i2c_adap, &hauppauge_hvr127x_config); } + if (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1275) + cx23885_set_frontend_hook(port, fe0->dvb.frontend); break; case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: @@ -1106,6 +1112,7 @@ static int dvb_register(struct cx23885_tsport *port) &i2c_bus2->i2c_adap, &mygica_x8506_xc5000_config); } + cx23885_set_frontend_hook(port, fe0->dvb.frontend); break; case CX23885_BOARD_MAGICPRO_PROHDTVE2: i2c_bus = &dev->i2c_bus[0]; @@ -1119,6 +1126,7 @@ static int dvb_register(struct cx23885_tsport *port) &i2c_bus2->i2c_adap, &magicpro_prohdtve2_xc5000_config); } + cx23885_set_frontend_hook(port, fe0->dvb.frontend); break; case CX23885_BOARD_HAUPPAUGE_HVR1850: i2c_bus = &dev->i2c_bus[0]; diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 5687d3f..038caf5 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -320,6 +320,8 @@ struct cx23885_tsport { /* Workaround for a temp dvb_frontend that the tuner can attached to */ struct dvb_frontend analog_fe; + + int (*set_frontend)(struct dvb_frontend *fe); }; struct cx23885_kernel_ir { -- cgit v0.10.2 From 9d32069faacdc81fe1dcb5d297c32a3ac81da8f0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 9 Aug 2013 08:53:26 -0300 Subject: [media] mb86a20s: Fix TS parallel mode changeset 768e6dadd74 caused a regression on using mb86a20s in parallel mode, as the parallel mode selection got overriden by mb86a20s_init2. Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 856374b..2c7217f 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -157,7 +157,6 @@ static struct regdata mb86a20s_init2[] = { { 0x45, 0x04 }, /* CN symbol 4 */ { 0x48, 0x04 }, /* CN manual mode */ - { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ { 0x50, 0xd6 }, { 0x51, 0x1f }, { 0x50, 0xd2 }, { 0x51, 0x03 }, { 0x50, 0xd7 }, { 0x51, 0xbf }, @@ -1860,16 +1859,15 @@ static int mb86a20s_initfe(struct dvb_frontend *fe) dev_dbg(&state->i2c->dev, "%s: IF=%d, IF reg=0x%06llx\n", __func__, state->if_freq, (long long)pll); - if (!state->config->is_serial) { + if (!state->config->is_serial) regD5 &= ~1; - rc = mb86a20s_writereg(state, 0x50, 0xd5); - if (rc < 0) - goto err; - rc = mb86a20s_writereg(state, 0x51, regD5); - if (rc < 0) - goto err; - } + rc = mb86a20s_writereg(state, 0x50, 0xd5); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x51, regD5); + if (rc < 0) + goto err; rc = mb86a20s_writeregdata(state, mb86a20s_init2); if (rc < 0) -- cgit v0.10.2 From 0d1b526516396d7c34ca7c90e6781dc0c312d272 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 9 Aug 2013 08:53:27 -0300 Subject: [media] cx23885: Add DTV support for Mygica X8502/X8507 boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those boards were missing the ISDB-T support. Most of the work on this patch were done by Alfredo. My work here were to port this patch from Kernel 3.2 to upstream, fix the issue caused by the set_frontend bad hook, and add the Kconfig bits. Tested on a X8502 board rebranded as: "Leadership - Placa PCI-e de Captura de Vídeo Híbrida" - product code 3800. Thanks-to: Alfredo Delaiti Tested-by: Alfredo Delaiti Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index b3688aa..5104c80 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -29,6 +29,7 @@ config VIDEO_CX23885 select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MB86A20S 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 7e923f8..6a71a96 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -528,11 +528,12 @@ struct cx23885_board cx23885_boards[] = { } }, }, [CX23885_BOARD_MYGICA_X8507] = { - .name = "Mygica X8507", + .name = "Mygica X8502/X8507 ISDB-T", .tuner_type = TUNER_XC5000, .tuner_addr = 0x61, .tuner_bus = 1, .porta = CX23885_ANALOG_VIDEO, + .portb = CX23885_MPEG_DVB, .input = { { .type = CX23885_VMUX_TELEVISION, @@ -1281,7 +1282,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) case CX23885_BOARD_MYGICA_X8507: /* GPIO-0 (0)Analog / (1)Digital TV */ /* GPIO-1 reset XC5000 */ - /* GPIO-2 reset LGS8GL5 / LGS8G75 */ + /* GPIO-2 demod reset */ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1); cx23885_gpio_clear(dev, GPIO_1 | GPIO_2); mdelay(100); @@ -1677,6 +1678,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) break; case CX23885_BOARD_MYGICA_X8506: case CX23885_BOARD_MAGICPRO_PROHDTVE2: + case CX23885_BOARD_MYGICA_X8507: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->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 a25a037..971e4ff 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -69,6 +69,7 @@ #include "stb6100_cfg.h" #include "tda10071.h" #include "a8293.h" +#include "mb86a20s.h" static unsigned int debug; @@ -492,6 +493,15 @@ static struct xc5000_config mygica_x8506_xc5000_config = { .if_khz = 5380, }; +static struct mb86a20s_config mygica_x8507_mb86a20s_config = { + .demod_address = 0x10, +}; + +static struct xc5000_config mygica_x8507_xc5000_config = { + .i2c_address = 0x61, + .if_khz = 4000, +}; + static struct stv090x_config prof_8000_stv090x_config = { .device = STV0903, .demod_mode = STV090x_SINGLE, @@ -548,6 +558,7 @@ static int cx23885_dvb_set_frontend(struct dvb_frontend *fe) } break; case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MYGICA_X8507: case CX23885_BOARD_MAGICPRO_PROHDTVE2: /* Select Digital TV */ cx23885_gpio_set(dev, GPIO_0); @@ -1114,6 +1125,20 @@ static int dvb_register(struct cx23885_tsport *port) } cx23885_set_frontend_hook(port, fe0->dvb.frontend); break; + case CX23885_BOARD_MYGICA_X8507: + i2c_bus = &dev->i2c_bus[0]; + i2c_bus2 = &dev->i2c_bus[1]; + fe0->dvb.frontend = dvb_attach(mb86a20s_attach, + &mygica_x8507_mb86a20s_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(xc5000_attach, + fe0->dvb.frontend, + &i2c_bus2->i2c_adap, + &mygica_x8507_xc5000_config); + } + cx23885_set_frontend_hook(port, fe0->dvb.frontend); + break; case CX23885_BOARD_MAGICPRO_PROHDTVE2: i2c_bus = &dev->i2c_bus[0]; i2c_bus2 = &dev->i2c_bus[1]; -- cgit v0.10.2 From ec532503209053bbee0c7dac410031e50835e01a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Wed, 14 Aug 2013 05:24:39 -0300 Subject: [media] siano: fix divide error on 0 counters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GIT_AUTHOR_DATE=1376465691 I took a quick look at the code and wonder if the problem is caused by an initial zero statistics message? This is all just a wild guess, but if it is correct, then the attached untested patch might fix it... Bjørn >From d78a0599d5b5d4da384eae08bf7da316389dfbe5 Mon Sep 17 00:00:00 2001 ts_packets and ets_packets counters can be 0. Don't fall over if they are. Fixes: [ 846.851711] divide error: 0000 [#1] SMP [ 846.851806] Modules linked in: smsdvb dvb_core ir_lirc_codec lirc_dev ir_sanyo_decoder ir_mce_kbd_decoder ir_sony_decoder ir_jvc_decoder ir_rc6_decoder ir_rc5_decoder ir_nec_decoder rc_hauppauge smsusb smsmdtv rc_core pci_stub vboxpci(O) vboxnetadp(O) vboxnetflt(O) vboxdrv(O) parport_pc ppdev lp parport cpufreq_userspace cpufreq_powersave cpufreq_stats cpufreq_conservative rfcomm bnep binfmt_misc uinput nfsd auth_rpcgss oid_registry nfs_acl nfs lockd dns_resolver fscache sunrpc ext4 jbd2 fuse tp_smapi(O) thinkpad_ec(O) loop firewire_sbp2 dm_crypt snd_hda_codec_conexant snd_hda_intel snd_hda_codec snd_hwdep snd_pcm_oss snd_mixer_oss snd_pcm thinkpad_acpi nvram snd_page_alloc hid_generic snd_seq_midi snd_seq_midi_event arc4 usbhid snd_rawmidi uvcvideo hid iwldvm coretemp kvm_intel mac8021 1 cdc_wdm [ 846.853477] cdc_acm snd_seq videobuf2_vmalloc videobuf2_memops videobuf2_core videodev media kvm radeon r852 ttm joydev cdc_ether usbnet pcmcia mii sm_common nand btusb drm_kms_helper tpm_tis acpi_cpufreq bluetooth iwlwifi nand_ecc drm nand_ids i2c_i801 mtd snd_seq_device iTCO_wdt iTCO_vendor_support r592 memstick lpc_ich mperf tpm yenta_socket pcmcia_rsrc pcmcia_core cfg80211 snd_timer snd pcspkr i2c_algo_bit crc16 i2c_core tpm_bios processor mfd_core wmi psmouse mei_me rfkill mei serio_raw soundcore evdev battery button video ac microcode ext3 mbcache jbd md_mod dm_mirror dm_region_hash dm_log dm_mod sg sr_mod sd_mod cdrom crc_t10dif firewire_ohci sdhci_pci sdhci mmc_core firewire_core crc_itu_t thermal thermal_sys ahci libahci ehci_pci uhci_hcd ehci_hcd libata scsi_mod usbcore e1000 e usb_common [ 846.855310] ptp pps_core [ 846.855356] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G O 3.10-2-amd64 #1 Debian 3.10.5-1 [ 846.855490] Hardware name: LENOVO 4061WFA/4061WFA, BIOS 6FET92WW (3.22 ) 12/14/2011 [ 846.855609] task: ffffffff81613400 ti: ffffffff81600000 task.ti: ffffffff81600000 [ 846.855636] RIP: 0010:[] [] smsdvb_onresponse+0x264/0xa86 [smsdvb] [ 846.863906] RSP: 0018:ffff88013bc03cf0 EFLAGS: 00010046 [ 846.863906] RAX: 0000000000000000 RBX: ffff880133bf6000 RCX: 0000000000000000 [ 846.863906] RDX: 0000000000000000 RSI: ffff88005d3b58c0 RDI: ffff880133bf6000 [ 846.863906] RBP: ffff88005d1da000 R08: 0000000000000058 R09: 0000000000000015 [ 846.863906] R10: 0000000000001a0d R11: 000000000000021a R12: ffff88005d3b58c0 [ 846.863906] R13: ffff88005d1da008 R14: 00000000ffffff8d R15: ffff880036cf5060 [ 846.863906] FS: 0000000000000000(0000) GS:ffff88013bc00000(0000) knlGS:0000000000000000 [ 846.863906] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 846.863906] CR2: 00007f3a4b69ae50 CR3: 0000000036dac000 CR4: 00000000000407f0 [ 846.863906] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 846.863906] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 846.863906] Stack: [ 846.863906] ffff88007a102000 ffff88005d1da000 ffff88005d3b58c0 0000000000085824 [ 846.863906] ffffffffa08c5aa3 ffff88005d1da000 ffff8800a6907390 ffff8800a69073b0 [ 846.863906] ffff8800a6907000 ffffffffa08b642c 000000000000021a ffff8800a69073b0 [ 846.863906] Call Trace: [ 846.863906] [ 846.863906] [ 846.863906] [] ? smscore_onresponse+0x1d5/0x353 [smsmdtv] [ 846.863906] [] ? smsusb_onresponse+0x146/0x192 [smsusb] [ 846.863906] [] ? usb_hcd_giveback_urb+0x6c/0xac [usbcore] [ 846.863906] [] ? ehci_urb_done+0x62/0x72 [ehci_hcd] [ 846.863906] [] ? qh_completions+0x91/0x364 [ehci_hcd] [ 846.863906] [] ? ehci_work+0x8a/0x68e [ehci_hcd] [ 846.863906] [] ? timekeeping_get_ns.constprop.10+0xd/0x31 [ 846.863906] [] ? update_cfs_rq_blocked_load+0xde/0xec [ 846.863906] [] ? run_posix_cpu_timers+0x25/0x575 [ 846.863906] [] ? ehci_irq+0x211/0x23d [ehci_hcd] [ 846.863906] [] ? usb_hcd_irq+0x31/0x48 [usbcore] [ 846.863906] [] ? handle_irq_event_percpu+0x49/0x1a4 [ 846.863906] [] ? handle_irq_event+0x32/0x4b [ 846.863906] [] ? handle_fasteoi_irq+0x80/0xb6 [ 846.863906] [] ? handle_irq+0x18/0x20 [ 846.863906] [] ? do_IRQ+0x40/0x95 [ 846.863906] [] ? common_interrupt+0x6d/0x6d [ 846.863906] [ 846.863906] [ 846.863906] [] ? arch_local_irq_enable+0x4/0x8 [ 846.863906] [] ? cpuidle_enter_state+0x52/0xc1 [ 846.863906] [] ? cpuidle_idle_call+0xd4/0x143 [ 846.863906] [] ? arch_cpu_idle+0x5/0x17 [ 846.863906] [] ? cpu_startup_entry+0x10d/0x187 [ 846.863906] [] ? start_kernel+0x3e8/0x3f3 [ 846.863906] [] ? repair_env_string+0x54/0x54 [ 846.863906] [] ? x86_64_start_kernel+0xf2/0xfd [ 846.863906] Code: 25 09 00 00 c6 83 da 08 00 00 03 8b 45 54 48 01 83 b6 08 00 00 8b 45 50 48 01 83 db 08 00 00 8b 4d 18 69 c1 ff ff 00 00 03 4d 14 <48> f7 f1 89 83 a8 09 00 00 e9 68 fe ff ff 48 8b 7f 10 e8 79 92 [ 846.863906] RIP [] smsdvb_onresponse+0x264/0xa86 [smsdvb] [ 846.863906] RSP Reference: http://bugs.debian.org/719623 Reported-by: Johannes Rohr Signed-off-by: Bjørn Mork Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c index 0862622..63676a8 100644 --- a/drivers/media/common/siano/smsdvb-main.c +++ b/drivers/media/common/siano/smsdvb-main.c @@ -276,7 +276,8 @@ static void smsdvb_update_per_slices(struct smsdvb_client_t *client, /* Legacy PER/BER */ tmp = p->ets_packets * 65535; - do_div(tmp, p->ts_packets + p->ets_packets); + if (p->ts_packets + p->ets_packets) + do_div(tmp, p->ts_packets + p->ets_packets); client->legacy_per = tmp; } -- cgit v0.10.2 From 5be789fc84a752946f6bdf263b053ed7d4a12a1c Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 14 Aug 2013 05:11:15 -0300 Subject: [media] marvell-ccic/mmp-driver.c: simplify use of devm_ioremap_resource Remove unneeded error handling on the result of a call to platform_get_resource when the value is passed to devm_ioremap_resource. A simplified version of the semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression pdev,res,n,e,e1; expression ret != 0; identifier l; @@ - res = platform_get_resource(pdev, IORESOURCE_MEM, n); ... when != res - if (res == NULL) { ... \(goto l;\|return ret;\) } ... when != res + res = platform_get_resource(pdev, IORESOURCE_MEM, n); e = devm_ioremap_resource(e1, res); // Signed-off-by: Julia Lawall Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index f06daa4..b5a19af 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -396,10 +396,6 @@ static int mmpcam_probe(struct platform_device *pdev) * Get our I/O memory. */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "no iomem resource!\n"); - return -ENODEV; - } mcam->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(mcam->regs)) return PTR_ERR(mcam->regs); @@ -409,10 +405,6 @@ static int mmpcam_probe(struct platform_device *pdev) * should really be managed outside of this driver? */ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res == NULL) { - dev_err(&pdev->dev, "no power resource!\n"); - return -ENODEV; - } cam->power_regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(cam->power_regs)) return PTR_ERR(cam->power_regs); -- cgit v0.10.2 From f02dcdd1784d2b56ffa8c528248b60ef142e921d Mon Sep 17 00:00:00 2001 From: Juergen Lock Date: Fri, 16 Aug 2013 15:00:24 -0300 Subject: [media] media: rc: rdev->open or rdev->close can be NULL At least technisat-usb2.c doesn't set these... Signed-off-by: Juergen Lock Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index aa5d8e7..46da365 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -711,7 +711,7 @@ int rc_open(struct rc_dev *rdev) return -EINVAL; mutex_lock(&rdev->lock); - if (!rdev->users++) + if (!rdev->users++ && rdev->open != NULL) rval = rdev->open(rdev); if (rval) @@ -735,7 +735,7 @@ void rc_close(struct rc_dev *rdev) if (rdev) { mutex_lock(&rdev->lock); - if (!--rdev->users) + if (!--rdev->users && rdev->close != NULL) rdev->close(rdev); mutex_unlock(&rdev->lock); -- cgit v0.10.2 From 8b1255a298ad16678d871abc2f86d99bea787a08 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Fri, 16 Aug 2013 21:29:02 -0300 Subject: [media] cx231xx: Add support for KWorld UB445-U The KWorld UB445-U is similar to the UB430-AF but with a Samsung S5H1411 frontend. Luckily all of the hardware is already well supported, just the device and USB ids need to be added to get it to work. Signed-off-by: Johannes Erdfelt 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 27948e1..a384f80 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -443,6 +443,44 @@ struct cx231xx_board cx231xx_boards[] = { .gpio = NULL, } }, }, + [CX231XX_BOARD_KWORLD_UB445_USB_HYBRID] = { + .name = "Kworld UB445 USB Hybrid", + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x11, /* According with PV cxPolaris.inf file */ + .tuner_sif_gpio = -1, + .tuner_scl_gpio = -1, + .tuner_sda_gpio = -1, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 2, + .demod_i2c_master = 1, + .ir_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x10, + .norm = V4L2_STD_NTSC_M, + .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, + } }, + }, [CX231XX_BOARD_PV_PLAYTV_USB_HYBRID] = { .name = "Pixelview PlayTV USB Hybrid", .tuner_type = TUNER_NXP_TDA18271, @@ -703,6 +741,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_PV_XCAPTURE_USB}, {USB_DEVICE(0x1b80, 0xe424), .driver_info = CX231XX_BOARD_KWORLD_UB430_USB_HYBRID}, + {USB_DEVICE(0x1b80, 0xe421), + .driver_info = CX231XX_BOARD_KWORLD_UB445_USB_HYBRID}, {USB_DEVICE(0x1f4d, 0x0237), .driver_info = CX231XX_BOARD_ICONBIT_U100}, {USB_DEVICE(0x0fd9, 0x0037), diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index 14e2610..4504bc6 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -657,6 +657,7 @@ static int dvb_init(struct cx231xx *dev) } break; case CX231XX_BOARD_CNXT_RDU_253S: + case CX231XX_BOARD_KWORLD_UB445_USB_HYBRID: dev->dvb->frontend = dvb_attach(s5h1411_attach, &tda18271_s5h1411_config, diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index e812119..babca7f 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -72,6 +72,7 @@ #define CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC 15 #define CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2 16 #define CX231XX_BOARD_OTG102 17 +#define CX231XX_BOARD_KWORLD_UB445_USB_HYBRID 18 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 -- cgit v0.10.2 From 976f375df1730dd16aa7c101298ec47bdd338d79 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 18 Aug 2013 08:15:37 -0300 Subject: [media] media/v4l2: VIDEO_SH_VEU should depend on HAS_DMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If NO_DMA=y: drivers/media/v4l2-core/videobuf2-dma-contig.c: In function ‘vb2_dc_mmap’: drivers/media/v4l2-core/videobuf2-dma-contig.c:204: error: implicit declaration of function ‘dma_mmap_coherent’ drivers/media/v4l2-core/videobuf2-dma-contig.c: In function ‘vb2_dc_get_base_sgt’: drivers/media/v4l2-core/videobuf2-dma-contig.c:387: error: implicit declaration of function ‘dma_get_sgtable’ Commit da508f5799659241a359e2d07abb8af905f6291c ("[media] media/v4l2: VIDEOBUF2_DMA_CONTIG should depend on HAS_DMA") added a dependency on HAS_DMA to VIDEO_SH_VEU, as it selects VIDEOBUF2_DMA_CONTIG. However, this got lost in the merge conflict resolution in commit df90e2258950fd631cdbf322c1ee1f22068391aa ("Merge branch 'devel-for-v3.10' into v4l_for_linus"). Re-add the dependency to fix this. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 9a44e06..8068d7b 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -203,7 +203,7 @@ config VIDEO_SAMSUNG_EXYNOS_GSC config VIDEO_SH_VEU tristate "SuperH VEU mem2mem video processing driver" - depends on VIDEO_DEV && VIDEO_V4L2 && GENERIC_HARDIRQS + depends on VIDEO_DEV && VIDEO_V4L2 && GENERIC_HARDIRQS && HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help -- cgit v0.10.2 From fea564a5f6a4ed6d3241aa90714ae2f0894a768a Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Tue, 13 Aug 2013 02:58:07 -0300 Subject: [media] v4l2-mem2mem: clear m2m context from job_queue before ctx streamoff When streamoff is called on the context and the context is added to the job_queue, 1] sometimes device_run receives the empty vb2 buffers (as v4l2_m2m_streamoff is dropping the ready queue). 2] sometimes v4l2_m2m_job_finish may not succeed as the m2m_dev->curr_ctx is made NULL in the v4l2_m2m_streamoff() The above points may stop the execution of the other queued contexts. This patch makes sure that before streamoff is executed on any context, that context should "not be running" or "not queued" in the job_queue. 1] If the current context is running, then abort job will be called. 2] If the current context is queued, then the context will be removed from the job_queue. Signed-off-by: Shaik Ameer Basha Signed-off-by: Kamil Debski 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 89b9067..7c43712 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -266,6 +266,39 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) } /** + * v4l2_m2m_cancel_job() - cancel pending jobs for the context + * + * In case of streamoff or release called on any context, + * 1] If the context is currently running, then abort job will be called + * 2] If the context is queued, then the context will be removed from + * the job_queue + */ +static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct v4l2_m2m_dev *m2m_dev; + unsigned long flags; + + m2m_dev = m2m_ctx->m2m_dev; + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + if (m2m_ctx->job_flags & TRANS_RUNNING) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); + dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx); + wait_event(m2m_ctx->finished, + !(m2m_ctx->job_flags & TRANS_RUNNING)); + } else if (m2m_ctx->job_flags & TRANS_QUEUED) { + list_del(&m2m_ctx->queue); + m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("m2m_ctx: %p had been on queue and was removed\n", + m2m_ctx); + } else { + /* Do nothing, was not on queue/running */ + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + } +} + +/** * v4l2_m2m_job_finish() - inform the framework that a job has been finished * and have it clean up * @@ -436,6 +469,9 @@ int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, unsigned long flags_job, flags; int ret; + /* wait until the current context is dequeued from job_queue */ + v4l2_m2m_cancel_job(m2m_ctx); + q_ctx = get_queue_ctx(m2m_ctx, type); ret = vb2_streamoff(&q_ctx->q, type); if (ret) @@ -658,27 +694,8 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init); */ void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx) { - struct v4l2_m2m_dev *m2m_dev; - unsigned long flags; - - m2m_dev = m2m_ctx->m2m_dev; - - spin_lock_irqsave(&m2m_dev->job_spinlock, flags); - if (m2m_ctx->job_flags & TRANS_RUNNING) { - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); - dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx); - wait_event(m2m_ctx->finished, !(m2m_ctx->job_flags & TRANS_RUNNING)); - } else if (m2m_ctx->job_flags & TRANS_QUEUED) { - list_del(&m2m_ctx->queue); - m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - dprintk("m2m_ctx: %p had been on queue and was removed\n", - m2m_ctx); - } else { - /* Do nothing, was not on queue/running */ - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - } + /* wait until the current context is dequeued from job_queue */ + v4l2_m2m_cancel_job(m2m_ctx); vb2_queue_release(&m2m_ctx->cap_q_ctx.q); vb2_queue_release(&m2m_ctx->out_q_ctx.q); -- cgit v0.10.2 From f82bc203eeddb271c08637577544b30af43e047b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 21 Aug 2013 11:14:16 -0300 Subject: [media] coda: Fix error paths Some resources were not being released in the error path and some were released in the incorrect order. Signed-off-by: Fabio Estevam 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 73460de..0894d800 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -2381,15 +2381,17 @@ static int coda_open(struct file *file) int ret; int idx; - idx = coda_next_free_instance(dev); - if (idx >= CODA_MAX_INSTANCES) - return -EBUSY; - set_bit(idx, &dev->instance_mask); - ctx = kzalloc(sizeof *ctx, GFP_KERNEL); if (!ctx) return -ENOMEM; + idx = coda_next_free_instance(dev); + if (idx >= CODA_MAX_INSTANCES) { + ret = -EBUSY; + goto err_coda_max; + } + set_bit(idx, &dev->instance_mask); + INIT_WORK(&ctx->skip_run, coda_skip_run); v4l2_fh_init(&ctx->fh, video_devdata(file)); file->private_data = &ctx->fh; @@ -2403,6 +2405,9 @@ static int coda_open(struct file *file) default: ctx->reg_idx = idx; } + + clk_prepare_enable(dev->clk_per); + clk_prepare_enable(dev->clk_ahb); set_default_params(ctx); ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &coda_queue_init); @@ -2411,12 +2416,12 @@ static int coda_open(struct file *file) v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n", __func__, ret); - goto err; + goto err_ctx_init; } ret = coda_ctrls_setup(ctx); if (ret) { v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n"); - goto err; + goto err_ctrls_setup; } ctx->fh.ctrl_handler = &ctx->ctrls; @@ -2424,7 +2429,7 @@ static int coda_open(struct file *file) ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf"); - goto err; + goto err_dma_alloc; } ctx->bitstream.size = CODA_MAX_FRAME_SIZE; @@ -2433,7 +2438,7 @@ static int coda_open(struct file *file) if (!ctx->bitstream.vaddr) { v4l2_err(&dev->v4l2_dev, "failed to allocate bitstream ringbuffer"); ret = -ENOMEM; - goto err; + goto err_dma_writecombine; } kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, ctx->bitstream.size); @@ -2444,17 +2449,27 @@ static int coda_open(struct file *file) list_add(&ctx->list, &dev->instances); coda_unlock(ctx); - clk_prepare_enable(dev->clk_per); - clk_prepare_enable(dev->clk_ahb); - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n", ctx->idx, ctx); return 0; -err: +err_dma_writecombine: + coda_free_context_buffers(ctx); + if (ctx->dev->devtype->product == CODA_DX6) + coda_free_aux_buf(dev, &ctx->workbuf); + coda_free_aux_buf(dev, &ctx->parabuf); +err_dma_alloc: + v4l2_ctrl_handler_free(&ctx->ctrls); +err_ctrls_setup: + v4l2_m2m_ctx_release(ctx->m2m_ctx); +err_ctx_init: + clk_disable_unprepare(dev->clk_ahb); + clk_disable_unprepare(dev->clk_per); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); + clear_bit(ctx->idx, &dev->instance_mask); +err_coda_max: kfree(ctx); return ret; } @@ -2496,8 +2511,8 @@ static int coda_release(struct file *file) coda_free_aux_buf(dev, &ctx->parabuf); v4l2_ctrl_handler_free(&ctx->ctrls); - clk_disable_unprepare(dev->clk_per); clk_disable_unprepare(dev->clk_ahb); + clk_disable_unprepare(dev->clk_per); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); clear_bit(ctx->idx, &dev->instance_mask); -- cgit v0.10.2 From 79830db28622b3ee183a30903d50cbd92eb0fb31 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 21 Aug 2013 11:14:17 -0300 Subject: [media] coda: Check the return value from clk_prepare_enable() clk_prepare_enable() may fail, so let's check its return value and propagate it in the case of error. Signed-off-by: Fabio Estevam 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 0894d800..04ced56 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -2406,8 +2406,14 @@ static int coda_open(struct file *file) ctx->reg_idx = idx; } - clk_prepare_enable(dev->clk_per); - clk_prepare_enable(dev->clk_ahb); + 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; + set_default_params(ctx); ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &coda_queue_init); @@ -2465,7 +2471,9 @@ err_ctrls_setup: v4l2_m2m_ctx_release(ctx->m2m_ctx); err_ctx_init: clk_disable_unprepare(dev->clk_ahb); +err_clk_ahb: clk_disable_unprepare(dev->clk_per); +err_clk_per: v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); clear_bit(ctx->idx, &dev->instance_mask); @@ -2873,10 +2881,15 @@ static int coda_hw_init(struct coda_dev *dev) u16 product, major, minor, release; u32 data; u16 *p; - int i; + int i, ret; + + ret = clk_prepare_enable(dev->clk_per); + if (ret) + return ret; - clk_prepare_enable(dev->clk_per); - clk_prepare_enable(dev->clk_ahb); + ret = clk_prepare_enable(dev->clk_ahb); + if (ret) + goto err_clk_ahb; /* * Copy the first CODA_ISRAM_SIZE in the internal SRAM. @@ -2985,6 +2998,10 @@ static int coda_hw_init(struct coda_dev *dev) } return 0; + +err_clk_ahb: + clk_disable_unprepare(dev->clk_per); + return ret; } static void coda_fw_callback(const struct firmware *fw, void *context) -- cgit v0.10.2 From edcaa49a2a8a6854c54a5f400b803c63e68a459c Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 21 Aug 2013 11:14:18 -0300 Subject: [media] coda: No need to check the return value of platform_get_resource() When using devm_ioremap_resource(), we do not need to check the return value of platform_get_resource(), so just remove it. Signed-off-by: Fabio Estevam Acked-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 04ced56..999f9dc 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -3154,11 +3154,6 @@ static int coda_probe(struct platform_device *pdev) /* Get memory for physical registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get memory region resource\n"); - return -ENOENT; - } - dev->regs_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(dev->regs_base)) return PTR_ERR(dev->regs_base); -- cgit v0.10.2 From d1d70aa69db3d09240e9faf6bf68a044b9107480 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sun, 11 Aug 2013 02:02:24 -0300 Subject: [media] media: OF: add "sync-on-green-active" property This patch adds 'sync-on-green-active' property as part of endpoint property. Signed-off-by: Lad, Prabhakar Acked-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt index e022d2d..ce719f8 100644 --- a/Documentation/devicetree/bindings/media/video-interfaces.txt +++ b/Documentation/devicetree/bindings/media/video-interfaces.txt @@ -88,6 +88,8 @@ Optional endpoint properties - field-even-active: field signal level during the even field data transmission. - pclk-sample: sample data on rising (1) or falling (0) edge of the pixel clock signal. +- sync-on-green-active: active state of Sync-on-green (SoG) signal, 0/1 for + LOW/HIGH respectively. - data-lanes: an array of physical data lane indexes. Position of an entry determines the logical lane number, while the value of an entry indicates physical lane, e.g. for 2-lane MIPI CSI-2 bus we could have diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index ed305d8..a6478dc 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -100,6 +100,10 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node, if (!of_property_read_u32(node, "data-shift", &v)) bus->data_shift = v; + if (!of_property_read_u32(node, "sync-on-green-active", &v)) + flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH : + V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW; + bus->flags = flags; } diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h index 83ae07e..395c4a9 100644 --- a/include/media/v4l2-mediabus.h +++ b/include/media/v4l2-mediabus.h @@ -40,6 +40,9 @@ #define V4L2_MBUS_FIELD_EVEN_HIGH (1 << 10) /* FIELD = 1/0 - Field1 (odd)/Field2 (even) */ #define V4L2_MBUS_FIELD_EVEN_LOW (1 << 11) +/* Active state of Sync-on-green (SoG) signal, 0/1 for LOW/HIGH respectively. */ +#define V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH (1 << 12) +#define V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW (1 << 13) /* Serial flags */ /* How many lanes the client can use */ -- cgit v0.10.2 From c0d9644fa01892653bc9e31ef4296e78f8d8c1ca Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sun, 11 Aug 2013 02:25:21 -0300 Subject: [media] media: i2c: tvp7002: add OF support add OF support for the tvp7002 driver. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/devicetree/bindings/media/i2c/tvp7002.txt b/Documentation/devicetree/bindings/media/i2c/tvp7002.txt new file mode 100644 index 0000000..5f28b5d --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/tvp7002.txt @@ -0,0 +1,53 @@ +* Texas Instruments TV7002 video decoder + +The TVP7002 device supports digitizing of video and graphics signal in RGB and +YPbPr color space. + +Required Properties : +- compatible : Must be "ti,tvp7002" + +Optional Properties: +- hsync-active: HSYNC Polarity configuration for the bus. Default value when + this property is not specified is <0>. + +- vsync-active: VSYNC Polarity configuration for the bus. Default value when + this property is not specified is <0>. + +- pclk-sample: Clock polarity of the bus. Default value when this property is + not specified is <0>. + +- sync-on-green-active: Active state of Sync-on-green signal property of the + endpoint. + 0 = Normal Operation (Active Low, Default) + 1 = Inverted operation + +- field-even-active: Active-high Field ID output polarity control of the bus. + Under normal operation, the field ID output is set to logic 1 for an odd field + (field 1) and set to logic 0 for an even field (field 0). + 0 = Normal Operation (Active Low, Default) + 1 = FID output polarity inverted + +For further reading of port node refer Documentation/devicetree/bindings/media/ +video-interfaces.txt. + +Example: + + i2c0@1c22000 { + ... + ... + tvp7002@5c { + compatible = "ti,tvp7002"; + reg = <0x5c>; + + port { + tvp7002_1: endpoint { + hsync-active = <1>; + vsync-active = <1>; + pclk-sample = <0>; + sync-on-green-active = <1>; + field-even-active = <0>; + }; + }; + }; + ... + }; diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index f6b1f3f..24a08fa 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -35,6 +35,8 @@ #include #include #include +#include + #include "tvp7002_reg.h" MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver"); @@ -943,6 +945,48 @@ static const struct v4l2_subdev_ops tvp7002_ops = { .pad = &tvp7002_pad_ops, }; +static struct tvp7002_config * +tvp7002_get_pdata(struct i2c_client *client) +{ + struct v4l2_of_endpoint bus_cfg; + struct tvp7002_config *pdata; + struct device_node *endpoint; + unsigned int flags; + + if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) + return client->dev.platform_data; + + endpoint = v4l2_of_get_next_endpoint(client->dev.of_node, NULL); + if (!endpoint) + return NULL; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + goto done; + + v4l2_of_parse_endpoint(endpoint, &bus_cfg); + flags = bus_cfg.bus.parallel.flags; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + pdata->hs_polarity = 1; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + pdata->vs_polarity = 1; + + if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + pdata->clk_polarity = 1; + + if (flags & V4L2_MBUS_FIELD_EVEN_HIGH) + pdata->fid_polarity = 1; + + if (flags & V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH) + pdata->sog_polarity = 1; + +done: + of_node_put(endpoint); + return pdata; +} + /* * tvp7002_probe - Probe a TVP7002 device * @c: ptr to i2c_client struct @@ -954,32 +998,32 @@ static const struct v4l2_subdev_ops tvp7002_ops = { */ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) { + struct tvp7002_config *pdata = tvp7002_get_pdata(c); struct v4l2_subdev *sd; struct tvp7002 *device; struct v4l2_dv_timings timings; int polarity_a; int polarity_b; u8 revision; - int error; + if (pdata == NULL) { + dev_err(&c->dev, "No platform data\n"); + return -EINVAL; + } + /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(c->adapter, I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) return -EIO; - if (!c->dev.platform_data) { - v4l_err(c, "No platform data!!\n"); - return -ENODEV; - } - device = devm_kzalloc(&c->dev, sizeof(struct tvp7002), GFP_KERNEL); if (!device) return -ENOMEM; sd = &device->sd; - device->pdata = c->dev.platform_data; + device->pdata = pdata; device->current_timings = tvp7002_timings; /* Tell v4l2 the device is ready */ @@ -1084,9 +1128,18 @@ static const struct i2c_device_id tvp7002_id[] = { }; MODULE_DEVICE_TABLE(i2c, tvp7002_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tvp7002_of_match[] = { + { .compatible = "ti,tvp7002", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, tvp7002_of_match); +#endif + /* I2C driver data */ static struct i2c_driver tvp7002_driver = { .driver = { + .of_match_table = of_match_ptr(tvp7002_of_match), .owner = THIS_MODULE, .name = TVP7002_MODULE_NAME, }, -- cgit v0.10.2 From fbbb29a671b5082c9ffafed0d1b5f0ae52b83452 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 18 Aug 2013 16:05:19 -0300 Subject: [media] v4l2-ctrl: Suppress build warning from v4l2_ctrl_new_std_menu() Prevent following build warning: drivers/media/v4l2-core/v4l2-ctrls.c: In function v4l2_ctrl_new_std_menu: drivers/media/v4l2-core/v4l2-ctrls.c:1768:15: warning: 'qmenu_int_len' may be used uninitialized in this function Signed-off-by: Sylwester Nawrocki Cc: Arun Kumar K 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 c6dc1fd..c3f0803 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1763,9 +1763,9 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, { const char * const *qmenu = NULL; const s64 *qmenu_int = NULL; + unsigned int qmenu_int_len = 0; const char *name; enum v4l2_ctrl_type type; - unsigned int qmenu_int_len; s32 min; s32 step; u32 flags; -- cgit v0.10.2 From 1056e4388b0454917a512618c8416a98628fc9ce Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Tue, 13 Aug 2013 11:04:06 -0300 Subject: [media] v4l2-dev: Fix race condition on __video_register_device When 2 devices are registered at the same time, in Part 2 both will get the same minor number, making Part6 crash, because it cannot create a create a device with a duplicated minor: [ 7.157648] ------------[ cut here ]------------ [ 7.157666] WARNING: at fs/sysfs/dir.c:530 sysfs_add_one+0xbd/0xe0() [ 7.157669] sysfs: cannot create duplicate filename '/dev/char/81:1' [ 7.157672] Modules linked in: qtec_xform(+) qt5023_video(+) videobuf2_vmalloc videobuf2_dma_sg videobuf2_memops videobuf2_core gpio_xilinx(+) qtec_white qtec_cmosis(+) qtec_pcie qt5023 [ 7.157694] CPU: 0 PID: 120 Comm: systemd-udevd Not tainted 3.10.0-qtec-standard #8 [ 7.157698] Hardware name: QTechnology QT5022/QT5022, BIOS PM_2.1.0.309 X64 05/23/2013 [ 7.157702] 0000000000000009 ffff8801788358e8 ffffffff8176c487 ffff880178835928 [ 7.157707] ffffffff8106f6f0 ffff880175759770 00000000ffffffef ffff880175759930 [ 7.157712] ffff8801788359e8 ffff880178bff000 ffff880175759930 ffff880178835988 [ 7.157718] Call Trace: [ 7.157728] [] dump_stack+0x19/0x1b [ 7.157735] [] warn_slowpath_common+0x70/0xa0 [ 7.157740] [] warn_slowpath_fmt+0x46/0x50 [ 7.157746] [] ? strlcat+0x65/0x90 [ 7.157750] [] sysfs_add_one+0xbd/0xe0 [ 7.157755] [] sysfs_do_create_link_sd+0xdb/0x200 [ 7.157760] [] sysfs_create_link+0x21/0x40 [ 7.157765] [] device_add+0x21b/0x6d0 [ 7.157772] [] ? pm_runtime_init+0xe5/0xf0 [ 7.157776] [] device_register+0x1e/0x30 [ 7.157782] [] __video_register_device+0x313/0x610 [ 7.157791] [] qtec_xform_probe+0x465/0x7a4 [qtec_xform] [ 7.157797] [] platform_drv_probe+0x43/0x80 [ 7.157802] [] ? driver_sysfs_add+0x7a/0xb0 [ 7.157807] [] driver_probe_device+0x8b/0x3a0 [ 7.157812] [] __driver_attach+0xab/0xb0 [ 7.157816] [] ? driver_probe_device+0x3a0/0x3a0 [ 7.157820] [] bus_for_each_dev+0x5d/0xa0 [ 7.157825] [] driver_attach+0x1e/0x20 [ 7.157829] [] bus_add_driver+0x10e/0x280 [ 7.157833] [] ? 0xffffffffa0098fff [ 7.157837] [] ? 0xffffffffa0098fff [ 7.157842] [] driver_register+0x77/0x170 [ 7.157848] [] ? __vunmap+0x9c/0x110 [ 7.157852] [] ? 0xffffffffa0098fff [ 7.157857] [] platform_driver_register+0x46/0x50 [ 7.157863] [] qtec_xform_plat_driver_init+0x10/0x12 [qtec_xform] [ 7.157869] [] do_one_initcall+0xea/0x1a0 [ 7.157875] [] load_module+0x1a91/0x2630 [ 7.157880] [] ? ddebug_proc_show+0xe0/0xe0 [ 7.157887] [] ? page_fault+0x22/0x30 [ 7.157892] [] SyS_init_module+0xea/0x140 [ 7.157898] [] tracesys+0xd0/0xd5 [ 7.157902] ---[ end trace 660cc3a65a4bf01b ]--- [ 7.157939] __video_register_device: device_register failed Signed-off-by: Ricardo Ribalda Delgado Acked-by: Sakari Ailus Acked-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 c8859d6..b2f207f 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -868,6 +868,7 @@ int __video_register_device(struct video_device *vdev, int type, int nr, /* Should not happen since we thought this minor was free */ WARN_ON(video_device[vdev->minor] != NULL); + video_device[vdev->minor] = vdev; vdev->index = get_index(vdev); mutex_unlock(&videodev_lock); @@ -930,9 +931,6 @@ int __video_register_device(struct video_device *vdev, int type, int nr, #endif /* Part 6: Activate this minor. The char device can now be used. */ set_bit(V4L2_FL_REGISTERED, &vdev->flags); - mutex_lock(&videodev_lock); - video_device[vdev->minor] = vdev; - mutex_unlock(&videodev_lock); return 0; @@ -940,6 +938,7 @@ cleanup: mutex_lock(&videodev_lock); if (vdev->cdev) cdev_del(vdev->cdev); + video_device[vdev->minor] = NULL; devnode_clear(vdev); mutex_unlock(&videodev_lock); /* Mark this video device as never having been registered. */ -- cgit v0.10.2 From c3e75c7d5e203b6d1d16519bef8571b5d228b7fe Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Wed, 14 Aug 2013 04:58:38 -0300 Subject: [media] v4l2-dv-timings: fix CVT calculation This patch fixes two errors that caused incorrect format detections: The first bug is in the calculation of the vertical backporch: the combined period of vsync and backporch must *exceed* a certain minimum value, and not be equal to it. The second bug is a rounding error in the reduced blanking calculation: expand the ideal_duty_cylce to be in parts per ten thousand to avoid rounding errors. Signed-off-by: Martin Bugge Cc: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index f20b316..72cf224 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -286,14 +286,14 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, /* Vertical */ if (reduced_blanking) { v_fp = CVT_RB_V_FPORCH; - v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 999999) / 1000000; + v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 1999999) / 1000000; v_bp -= vsync + v_fp; if (v_bp < CVT_RB_MIN_V_BPORCH) v_bp = CVT_RB_MIN_V_BPORCH; } else { v_fp = CVT_MIN_V_PORCH_RND; - v_bp = (CVT_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; + v_bp = (CVT_MIN_VSYNC_BP * hfreq + 1999999) / 1000000 - vsync; if (v_bp < CVT_MIN_V_BPORCH) v_bp = CVT_MIN_V_BPORCH; @@ -337,17 +337,16 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, frame_width = image_width + CVT_RB_H_BLANK; } else { + unsigned ideal_duty_cycle_per_myriad = + 100 * CVT_C_PRIME - (CVT_M_PRIME * 100000) / hfreq; int h_blank; - unsigned ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * 1000) / hfreq; - h_blank = (image_width * ideal_duty_cycle + (100 - ideal_duty_cycle) / 2) / - (100 - ideal_duty_cycle); - h_blank = h_blank - h_blank % (2 * CVT_CELL_GRAN); + if (ideal_duty_cycle_per_myriad < 2000) + ideal_duty_cycle_per_myriad = 2000; - if (h_blank * 100 / image_width < 20) { - h_blank = image_width / 5; - h_blank = (h_blank + 0x7) & ~0x7; - } + h_blank = image_width * ideal_duty_cycle_per_myriad / + (10000 - ideal_duty_cycle_per_myriad); + h_blank = (h_blank / (2 * CVT_CELL_GRAN)) * 2 * CVT_CELL_GRAN; pix_clk = (image_width + h_blank) * hfreq; pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; -- cgit v0.10.2 From 25a64ac95055d87e2e7b86ba3b9a81e91cb55a39 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Wed, 14 Aug 2013 07:58:45 -0300 Subject: [media] adv7604: debounce "format change" notifications The bridge driver is only notified when the input status has changed since the previous interrupt. Signed-off-by: Mats Randgaard 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 3ec7ec0..d093092 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -77,6 +77,7 @@ struct adv7604_state { struct delayed_work delayed_work_enable_hotplug; bool connector_hdmi; bool restart_stdi_once; + u32 prev_input_status; /* i2c clients */ struct i2c_client *i2c_avlink; @@ -1535,6 +1536,7 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct adv7604_state *state = to_state(sd); u8 fmt_change, fmt_change_digital, tx_5v; + u32 input_status; /* format change */ fmt_change = io_read(sd, 0x43) & 0x98; @@ -1545,9 +1547,18 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) io_write(sd, 0x6c, fmt_change_digital); if (fmt_change || fmt_change_digital) { v4l2_dbg(1, debug, sd, - "%s: ADV7604_FMT_CHANGE, fmt_change = 0x%x, fmt_change_digital = 0x%x\n", + "%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n", __func__, fmt_change, fmt_change_digital); - v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL); + + adv7604_g_input_status(sd, &input_status); + if (input_status != state->prev_input_status) { + v4l2_dbg(1, debug, sd, + "%s: input_status = 0x%x, prev_input_status = 0x%x\n", + __func__, input_status, state->prev_input_status); + state->prev_input_status = input_status; + v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL); + } + if (handled) *handled = true; } @@ -1953,6 +1964,10 @@ static int adv7604_probe(struct i2c_client *client, return -ENOMEM; } + /* initialize variables */ + state->restart_stdi_once = true; + state->prev_input_status = ~0; + /* platform data */ if (!pdata) { v4l_err(client, "No platform data!\n"); @@ -2036,7 +2051,6 @@ static int adv7604_probe(struct i2c_client *client, v4l2_err(sd, "failed to create all i2c clients\n"); goto err_i2c; } - state->restart_stdi_once = true; /* work queues */ state->work_queues = create_singlethread_workqueue(client->name); -- cgit v0.10.2 From bb88f325a4685de821d4de352c987bc30bd4ebca Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Wed, 14 Aug 2013 08:52:46 -0300 Subject: [media] adv7604: pixel-clock depends on deep-color-mode The frequency calculation has to take deep-color mode into account. While we're at it, also log the deep-color mode in log_status. Signed-off-by: Martin Bugge 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 d093092..6ffe25a 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -992,6 +992,11 @@ static inline bool no_lock_tmds(struct v4l2_subdev *sd) return (io_read(sd, 0x6a) & 0xe0) != 0xe0; } +static inline bool is_hdmi(struct v4l2_subdev *sd) +{ + return hdmi_read(sd, 0x05) & 0x80; +} + static inline bool no_lock_sspd(struct v4l2_subdev *sd) { /* TODO channel 2 */ @@ -1244,12 +1249,21 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd, V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; if (DIGITAL_INPUT) { + uint32_t freq; + timings->type = V4L2_DV_BT_656_1120; bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08); bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a); - bt->pixelclock = (hdmi_read(sd, 0x06) * 1000000) + + freq = (hdmi_read(sd, 0x06) * 1000000) + ((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000; + if (is_hdmi(sd)) { + /* adjust for deep color mode */ + unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; + + freq = freq * 8 / bits_per_channel; + } + bt->pixelclock = freq; bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 + hdmi_read(sd, 0x21); bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 + @@ -1637,7 +1651,7 @@ static void print_avi_infoframe(struct v4l2_subdev *sd) u8 avi_len; u8 avi_ver; - if (!(hdmi_read(sd, 0x05) & 0x80)) { + if (!is_hdmi(sd)) { v4l2_info(sd, "receive DVI-D signal (AVI infoframe not supported)\n"); return; } @@ -1698,6 +1712,12 @@ static int adv7604_log_status(struct v4l2_subdev *sd) "RGB limited range (16-235)", "RGB full range (0-255)", }; + char *deep_color_mode_txt[4] = { + "8-bits per channel", + "10-bits per channel", + "12-bits per channel", + "16-bits per channel (not supported)" + }; v4l2_info(sd, "-----Chip status-----\n"); v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); @@ -1756,7 +1776,9 @@ static int adv7604_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "-----HDMI status-----\n"); v4l2_info(sd, "HDCP encrypted content: %s\n", hdmi_read(sd, 0x05) & 0x40 ? "true" : "false"); - + if (is_hdmi(sd)) + v4l2_info(sd, "deep color mode: %s\n", + deep_color_mode_txt[(hdmi_read(sd, 0x0b) >> 5) & 0x3]); print_avi_infoframe(sd); } -- cgit v0.10.2 From 76eb2d30140d6a7072b7faf33b109c5a4e82ee4a Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Wed, 14 Aug 2013 08:56:57 -0300 Subject: [media] adv7604: improve log_status for HDMI/DVI-D signals Don't log if there is no signal. If there is a signal, then also log HDCP and audio status. Signed-off-by: Mats Randgaard 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 6ffe25a..34fcdf3 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1758,6 +1758,9 @@ static int adv7604_log_status(struct v4l2_subdev *sd) adv7604_print_timings(sd, &timings, "Detected format:", true); adv7604_print_timings(sd, &state->timings, "Configured format:", true); + if (no_signal(sd)) + return 0; + v4l2_info(sd, "-----Color space-----\n"); v4l2_info(sd, "RGB quantization range ctrl: %s\n", rgb_quantization_range_txt[state->rgb_quantization_range]); @@ -1767,18 +1770,41 @@ static int adv7604_log_status(struct v4l2_subdev *sd) (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr", (reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)", ((reg_io_0x02 & 0x04) ^ (reg_io_0x02 & 0x01)) ? - "enabled" : "disabled"); + "enabled" : "disabled"); v4l2_info(sd, "Color space conversion: %s\n", csc_coeff_sel_rb[cp_read(sd, 0xfc) >> 4]); - /* Digital video */ - if (DIGITAL_INPUT) { - v4l2_info(sd, "-----HDMI status-----\n"); - v4l2_info(sd, "HDCP encrypted content: %s\n", - hdmi_read(sd, 0x05) & 0x40 ? "true" : "false"); - if (is_hdmi(sd)) - v4l2_info(sd, "deep color mode: %s\n", - deep_color_mode_txt[(hdmi_read(sd, 0x0b) >> 5) & 0x3]); + if (!DIGITAL_INPUT) + return 0; + + v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); + v4l2_info(sd, "HDCP encrypted content: %s\n", (hdmi_read(sd, 0x05) & 0x40) ? "true" : "false"); + v4l2_info(sd, "HDCP keys read: %s%s\n", + (hdmi_read(sd, 0x04) & 0x20) ? "yes" : "no", + (hdmi_read(sd, 0x04) & 0x10) ? "ERROR" : ""); + if (!is_hdmi(sd)) { + bool audio_pll_locked = hdmi_read(sd, 0x04) & 0x01; + bool audio_sample_packet_detect = hdmi_read(sd, 0x18) & 0x01; + bool audio_mute = io_read(sd, 0x65) & 0x40; + + v4l2_info(sd, "Audio: pll %s, samples %s, %s\n", + audio_pll_locked ? "locked" : "not locked", + audio_sample_packet_detect ? "detected" : "not detected", + audio_mute ? "muted" : "enabled"); + if (audio_pll_locked && audio_sample_packet_detect) { + v4l2_info(sd, "Audio format: %s\n", + (hdmi_read(sd, 0x07) & 0x20) ? "multi-channel" : "stereo"); + } + v4l2_info(sd, "Audio CTS: %u\n", (hdmi_read(sd, 0x5b) << 12) + + (hdmi_read(sd, 0x5c) << 8) + + (hdmi_read(sd, 0x5d) & 0xf0)); + v4l2_info(sd, "Audio N: %u\n", ((hdmi_read(sd, 0x5d) & 0x0f) << 16) + + (hdmi_read(sd, 0x5e) << 8) + + hdmi_read(sd, 0x5f)); + v4l2_info(sd, "AV Mute: %s\n", (hdmi_read(sd, 0x04) & 0x40) ? "on" : "off"); + + v4l2_info(sd, "Deep color mode: %s\n", deep_color_mode_txt[(hdmi_read(sd, 0x0b) & 0x60) >> 5]); + print_avi_infoframe(sd); } -- cgit v0.10.2 From 70028fe646e63dd01070d1aaf304bbbc80e4a90a Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Wed, 14 Aug 2013 08:59:44 -0300 Subject: [media] adv7604: print flags and standards in timing information Signed-off-by: Mats Randgaard 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 34fcdf3..e732c9b 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1052,7 +1052,8 @@ static int adv7604_g_input_status(struct v4l2_subdev *sd, u32 *status) /* ----------------------------------------------------------------------- */ static void adv7604_print_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings, const char *txt, bool detailed) + struct v4l2_dv_timings *timings, + const char *txt, bool detailed) { struct v4l2_bt_timings *bt = &timings->bt; u32 htot, vtot; @@ -1069,18 +1070,32 @@ static void adv7604_print_timings(struct v4l2_subdev *sd, (htot * vtot)) : 0, htot, vtot); - if (detailed) { - v4l2_info(sd, " horizontal: fp = %d, %ssync = %d, bp = %d\n", - bt->hfrontporch, - (bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-", - bt->hsync, bt->hbackporch); - v4l2_info(sd, " vertical: fp = %d, %ssync = %d, bp = %d\n", - bt->vfrontporch, - (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", - bt->vsync, bt->vbackporch); - v4l2_info(sd, " pixelclock: %lld, flags: 0x%x, standards: 0x%x\n", - bt->pixelclock, bt->flags, bt->standards); - } + if (!detailed) + return; + + v4l2_info(sd, " horizontal: fp = %d, %ssync = %d, bp = %d\n", + bt->hfrontporch, + (bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-", + bt->hsync, bt->hbackporch); + v4l2_info(sd, " vertical: fp = %d, %ssync = %d, bp = %d\n", + bt->vfrontporch, + (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", + bt->vsync, bt->vbackporch); + v4l2_info(sd, " pixelclock: %lld\n", bt->pixelclock); + v4l2_info(sd, " flags (0x%x):%s%s%s%s\n", bt->flags, + (bt->flags & V4L2_DV_FL_REDUCED_BLANKING) ? + " Reduced blanking," : "", + (bt->flags & V4L2_DV_FL_CAN_REDUCE_FPS) ? + " Can reduce FPS," : "", + (bt->flags & V4L2_DV_FL_REDUCED_FPS) ? + " Reduced FPS," : "", + (bt->flags & V4L2_DV_FL_HALF_LINE) ? + " Half line," : ""); + v4l2_info(sd, " standards (0x%x):%s%s%s%s\n", bt->standards, + (bt->standards & V4L2_DV_BT_STD_CEA861) ? " CEA," : "", + (bt->standards & V4L2_DV_BT_STD_DMT) ? " DMT," : "", + (bt->standards & V4L2_DV_BT_STD_CVT) ? " CVT" : "", + (bt->standards & V4L2_DV_BT_STD_GTF) ? " GTF" : ""); } struct stdi_readback { -- cgit v0.10.2 From 7be4f888112eeebd7ce33bca49b3a1df1fd52295 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Wed, 14 Aug 2013 09:23:48 -0300 Subject: [media] ad9389b: no monitor if EDID is wrong state->have_monitor is set to false if the EDID that is read from the monitor has too many segments or wrong CRC. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 7e68d8f..52384e8 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -1019,6 +1019,7 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) segment = ad9389b_rd(sd, 0xc4); if (segment >= EDID_MAX_SEGM) { v4l2_err(sd, "edid segment number too big\n"); + state->have_monitor = false; return false; } v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment); @@ -1032,6 +1033,8 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) } if (!edid_segment_verify_crc(sd, segment)) { /* edid crc error, force reread of edid segment */ + v4l2_err(sd, "%s: edid crc error\n", __func__); + state->have_monitor = false; ad9389b_s_power(sd, false); ad9389b_s_power(sd, true); return false; -- cgit v0.10.2 From 15edc1ccdb29fe7cd55d329948c134c090a88b64 Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Wed, 14 Aug 2013 09:24:33 -0300 Subject: [media] ad9389b: trigger edid re-read by power-cycle chip Signed-off-by: Martin Bugge Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 52384e8..545aabb 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -855,8 +855,10 @@ static void ad9389b_edid_handler(struct work_struct *work) * (DVI connectors are particularly prone to this problem). */ if (state->edid.read_retries) { state->edid.read_retries--; - /* EDID read failed, trigger a retry */ - ad9389b_wr(sd, 0xc9, 0xf); + v4l2_dbg(1, debug, sd, "%s: edid read failed\n", __func__); + state->have_monitor = false; + ad9389b_s_power(sd, false); + ad9389b_s_power(sd, true); queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); return; @@ -1019,7 +1021,6 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) segment = ad9389b_rd(sd, 0xc4); if (segment >= EDID_MAX_SEGM) { v4l2_err(sd, "edid segment number too big\n"); - state->have_monitor = false; return false; } v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment); -- cgit v0.10.2 From 350a1815ded7debd6053da22cd75379c95c8ca4e Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Wed, 14 Aug 2013 09:25:48 -0300 Subject: [media] adv7604: corrected edid crc-calculation Signed-off-by: Martin Bugge Reviewed-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 545aabb..d78fd3d 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -983,12 +983,12 @@ static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd) static bool edid_block_verify_crc(u8 *edid_block) { - int i; u8 sum = 0; + int i; - for (i = 0; i < 127; i++) - sum += *(edid_block + i); - return ((255 - sum + 1) == edid_block[127]); + for (i = 0; i < 128; i++) + sum += edid_block[i]; + return sum == 0; } static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment) -- cgit v0.10.2 From f3b33ede51903e6cc211d9f0bdecc65646b71f17 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Wed, 14 Aug 2013 09:26:28 -0300 Subject: [media] ad9389b: change initial register configuration in ad9389b_setup() - register 0x17: CSC scaling factor was set to +/- 2.0. This register is set by ad9389b_csc_conversion_mode() to the right value. - register 0x3b: bits for pixel repetition and CSC was set to zero, but that is the default value. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index d78fd3d..92cdb25 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -894,11 +894,9 @@ static void ad9389b_setup(struct v4l2_subdev *sd) ad9389b_wr_and_or(sd, 0x15, 0xf1, 0x0); /* Output format: RGB 4:4:4 */ ad9389b_wr_and_or(sd, 0x16, 0x3f, 0x0); - /* CSC fixed point: +/-2, 1st order interpolation 4:2:2 -> 4:4:4 up - conversion, Aspect ratio: 16:9 */ - ad9389b_wr_and_or(sd, 0x17, 0xe1, 0x0e); - /* Disable pixel repetition and CSC */ - ad9389b_wr_and_or(sd, 0x3b, 0x9e, 0x0); + /* 1st order interpolation 4:2:2 -> 4:4:4 up conversion, + Aspect ratio: 16:9 */ + ad9389b_wr_and_or(sd, 0x17, 0xf9, 0x06); /* Output format: RGB 4:4:4, Active Format Information is valid. */ ad9389b_wr_and_or(sd, 0x45, 0xc7, 0x08); /* Underscanned */ -- cgit v0.10.2 From 0216dc2fe666b5cedba54df4c0be6ddc8eb352e1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 15 Aug 2013 08:02:40 -0300 Subject: [media] v4l2-dv-timings: add v4l2_print_dv_timings helper Drivers often have to log the contents of a dv_timings struct. Adding this helper will make it easier for drivers to do so. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 72cf224..917e58c 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -223,6 +223,55 @@ bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, } EXPORT_SYMBOL_GPL(v4l_match_dv_timings); +void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, + const struct v4l2_dv_timings *t, bool detailed) +{ + const struct v4l2_bt_timings *bt = &t->bt; + u32 htot, vtot; + + if (t->type != V4L2_DV_BT_656_1120) + return; + + htot = V4L2_DV_BT_FRAME_WIDTH(bt); + vtot = V4L2_DV_BT_FRAME_HEIGHT(bt); + + if (prefix == NULL) + prefix = ""; + + pr_info("%s: %s%ux%u%s%u (%ux%u)\n", dev_prefix, prefix, + bt->width, bt->height, bt->interlaced ? "i" : "p", + (htot * vtot) > 0 ? ((u32)bt->pixelclock / (htot * vtot)) : 0, + htot, vtot); + + if (!detailed) + return; + + pr_info("%s: horizontal: fp = %u, %ssync = %u, bp = %u\n", + dev_prefix, bt->hfrontporch, + (bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-", + bt->hsync, bt->hbackporch); + pr_info("%s: vertical: fp = %u, %ssync = %u, bp = %u\n", + dev_prefix, bt->vfrontporch, + (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", + bt->vsync, bt->vbackporch); + pr_info("%s: pixelclock: %llu\n", dev_prefix, bt->pixelclock); + pr_info("%s: flags (0x%x):%s%s%s%s\n", dev_prefix, bt->flags, + (bt->flags & V4L2_DV_FL_REDUCED_BLANKING) ? + " REDUCED_BLANKING" : "", + (bt->flags & V4L2_DV_FL_CAN_REDUCE_FPS) ? + " CAN_REDUCE_FPS" : "", + (bt->flags & V4L2_DV_FL_REDUCED_FPS) ? + " REDUCED_FPS" : "", + (bt->flags & V4L2_DV_FL_HALF_LINE) ? + " HALF_LINE" : ""); + pr_info("%s: standards (0x%x):%s%s%s%s\n", dev_prefix, bt->standards, + (bt->standards & V4L2_DV_BT_STD_CEA861) ? " CEA" : "", + (bt->standards & V4L2_DV_BT_STD_DMT) ? " DMT" : "", + (bt->standards & V4L2_DV_BT_STD_CVT) ? " CVT" : "", + (bt->standards & V4L2_DV_BT_STD_GTF) ? " GTF" : ""); +} +EXPORT_SYMBOL_GPL(v4l2_print_dv_timings); + /* * CVT defines * Based on Coordinated Video Timings Standard diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 4c7bb54..696e5c2 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -76,6 +76,15 @@ bool v4l_match_dv_timings(const struct v4l2_dv_timings *measured, const struct v4l2_dv_timings *standard, unsigned pclock_delta); +/** v4l2_print_dv_timings() - log the contents of a dv_timings struct + * @dev_prefix:device prefix for each log line. + * @prefix: additional prefix for each log line, may be NULL. + * @t: the timings data. + * @detailed: if true, give a detailed log. + */ +void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, + const struct v4l2_dv_timings *t, bool detailed); + /** v4l2_detect_cvt - detect if the given timings follow the CVT standard * @frame_height - the total height of the frame (including blanking) in lines. * @hfreq - the horizontal frequency in Hz. -- cgit v0.10.2 From 11d034c8b60c3eebc2a12f5e99a200f55a786230 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 15 Aug 2013 08:05:59 -0300 Subject: [media] ad9389b/adv7604/ths8200: use new v4l2_print_dv_timings helper These three drivers all have code to log the dv_timings contents. Replace that code with the new helper function. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 92cdb25..1c6d352 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -443,20 +443,11 @@ static int ad9389b_log_status(struct v4l2_subdev *sd) vic_detect, vic_sent); } } - if (state->dv_timings.type == V4L2_DV_BT_656_1120) { - struct v4l2_bt_timings *bt = bt = &state->dv_timings.bt; - u32 frame_width = V4L2_DV_BT_FRAME_WIDTH(bt); - u32 frame_height = V4L2_DV_BT_FRAME_HEIGHT(bt); - u32 frame_size = frame_width * frame_height; - - v4l2_info(sd, "timings: %ux%u%s%u (%ux%u). Pix freq. = %u Hz. Polarities = 0x%x\n", - bt->width, bt->height, bt->interlaced ? "i" : "p", - frame_size > 0 ? (unsigned)bt->pixelclock / frame_size : 0, - frame_width, frame_height, - (unsigned)bt->pixelclock, bt->polarities); - } else { + if (state->dv_timings.type == V4L2_DV_BT_656_1120) + v4l2_print_dv_timings(sd->name, "timings: ", + &state->dv_timings, false); + else v4l2_info(sd, "no timings set\n"); - } return 0; } diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index e732c9b..ba8602c 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1051,53 +1051,6 @@ static int adv7604_g_input_status(struct v4l2_subdev *sd, u32 *status) /* ----------------------------------------------------------------------- */ -static void adv7604_print_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings, - const char *txt, bool detailed) -{ - struct v4l2_bt_timings *bt = &timings->bt; - u32 htot, vtot; - - if (timings->type != V4L2_DV_BT_656_1120) - return; - - htot = htotal(bt); - vtot = vtotal(bt); - - v4l2_info(sd, "%s %dx%d%s%d (%dx%d)", - txt, bt->width, bt->height, bt->interlaced ? "i" : "p", - (htot * vtot) > 0 ? ((u32)bt->pixelclock / - (htot * vtot)) : 0, - htot, vtot); - - if (!detailed) - return; - - v4l2_info(sd, " horizontal: fp = %d, %ssync = %d, bp = %d\n", - bt->hfrontporch, - (bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-", - bt->hsync, bt->hbackporch); - v4l2_info(sd, " vertical: fp = %d, %ssync = %d, bp = %d\n", - bt->vfrontporch, - (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", - bt->vsync, bt->vbackporch); - v4l2_info(sd, " pixelclock: %lld\n", bt->pixelclock); - v4l2_info(sd, " flags (0x%x):%s%s%s%s\n", bt->flags, - (bt->flags & V4L2_DV_FL_REDUCED_BLANKING) ? - " Reduced blanking," : "", - (bt->flags & V4L2_DV_FL_CAN_REDUCE_FPS) ? - " Can reduce FPS," : "", - (bt->flags & V4L2_DV_FL_REDUCED_FPS) ? - " Reduced FPS," : "", - (bt->flags & V4L2_DV_FL_HALF_LINE) ? - " Half line," : ""); - v4l2_info(sd, " standards (0x%x):%s%s%s%s\n", bt->standards, - (bt->standards & V4L2_DV_BT_STD_CEA861) ? " CEA," : "", - (bt->standards & V4L2_DV_BT_STD_DMT) ? " DMT," : "", - (bt->standards & V4L2_DV_BT_STD_CVT) ? " CVT" : "", - (bt->standards & V4L2_DV_BT_STD_GTF) ? " GTF" : ""); -} - struct stdi_readback { u16 bl, lcf, lcvs; u8 hs_pol, vs_pol; @@ -1360,8 +1313,8 @@ found: } if (debug > 1) - adv7604_print_timings(sd, timings, - "adv7604_query_dv_timings:", true); + v4l2_print_dv_timings(sd->name, "adv7604_query_dv_timings: ", + timings, true); return 0; } @@ -1403,8 +1356,8 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd, if (debug > 1) - adv7604_print_timings(sd, timings, - "adv7604_s_dv_timings:", true); + v4l2_print_dv_timings(sd->name, "adv7604_s_dv_timings: ", + timings, true); return 0; } @@ -1770,8 +1723,10 @@ static int adv7604_log_status(struct v4l2_subdev *sd) if (adv7604_query_dv_timings(sd, &timings)) v4l2_info(sd, "No video detected\n"); else - adv7604_print_timings(sd, &timings, "Detected format:", true); - adv7604_print_timings(sd, &state->timings, "Configured format:", true); + v4l2_print_dv_timings(sd->name, "Detected format: ", + &timings, true); + v4l2_print_dv_timings(sd->name, "Configured format: ", + &state->timings, true); if (no_signal(sd)) return 0; diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 7a60a8f..49041e5 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -133,39 +133,6 @@ static int ths8200_s_register(struct v4l2_subdev *sd, } #endif -static void ths8200_print_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings, - const char *txt, bool detailed) -{ - struct v4l2_bt_timings *bt = &timings->bt; - u32 htot, vtot; - - if (timings->type != V4L2_DV_BT_656_1120) - return; - - htot = htotal(bt); - vtot = vtotal(bt); - - v4l2_info(sd, "%s %dx%d%s%d (%dx%d)", - txt, bt->width, bt->height, bt->interlaced ? "i" : "p", - (htot * vtot) > 0 ? ((u32)bt->pixelclock / (htot * vtot)) : 0, - htot, vtot); - - if (detailed) { - v4l2_info(sd, " horizontal: fp = %d, %ssync = %d, bp = %d\n", - bt->hfrontporch, - (bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-", - bt->hsync, bt->hbackporch); - v4l2_info(sd, " vertical: fp = %d, %ssync = %d, bp = %d\n", - bt->vfrontporch, - (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", - bt->vsync, bt->vbackporch); - v4l2_info(sd, - " pixelclock: %lld, flags: 0x%x, standards: 0x%x\n", - bt->pixelclock, bt->flags, bt->standards); - } -} - static int ths8200_log_status(struct v4l2_subdev *sd) { struct ths8200_state *state = to_state(sd); @@ -182,9 +149,8 @@ static int ths8200_log_status(struct v4l2_subdev *sd) ths8200_read(sd, THS8200_DTG2_PIXEL_CNT_LSB), (ths8200_read(sd, THS8200_DTG2_LINE_CNT_MSB) & 0x07) * 256 + ths8200_read(sd, THS8200_DTG2_LINE_CNT_LSB)); - ths8200_print_timings(sd, &state->dv_timings, - "Configured format:", true); - + v4l2_print_dv_timings(sd->name, "Configured format:", + &state->dv_timings, true); return 0; } -- cgit v0.10.2 From ef1ed8f5d366a035e532456bd747d34e5cb01ee5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 15 Aug 2013 08:28:47 -0300 Subject: [media] v4l2-dv-timings: rename v4l_match_dv_timings to v4l2_match_dv_timings It's the only function in v4l2-dv-timings.c with the v4l prefix instead of v4l2. Make it consistent with the other functions. 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 ba8602c..a1a9d1e 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -763,7 +763,7 @@ static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, int i; for (i = 0; predef_vid_timings[i].timings.bt.width; i++) { - if (!v4l_match_dv_timings(timings, &predef_vid_timings[i].timings, + if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings, DIGITAL_INPUT ? 250000 : 1000000)) continue; io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */ @@ -1183,7 +1183,7 @@ static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, int i; for (i = 0; adv7604_timings[i].bt.width; i++) { - if (v4l_match_dv_timings(timings, &adv7604_timings[i], + if (v4l2_match_dv_timings(timings, &adv7604_timings[i], DIGITAL_INPUT ? 250000 : 1000000)) { *timings = adv7604_timings[i]; break; diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 1b34c36..4ad9374 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -625,7 +625,7 @@ static int hdmi_s_dv_timings(struct v4l2_subdev *sd, int i; for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++) - if (v4l_match_dv_timings(&hdmi_timings[i].dv_timings, + if (v4l2_match_dv_timings(&hdmi_timings[i].dv_timings, timings, 0)) break; if (i == ARRAY_SIZE(hdmi_timings)) { diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index e68245a..0500c417 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -642,7 +642,7 @@ static int vidioc_s_dv_timings(struct file *file, void *_fh, if (dev->status != STATUS_IDLE) return -EBUSY; for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++) - if (v4l_match_dv_timings(timings, hdpvr_dv_timings + i, 0)) + if (v4l2_match_dv_timings(timings, hdpvr_dv_timings + i, 0)) break; if (i == ARRAY_SIZE(hdpvr_dv_timings)) return -EINVAL; diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 917e58c..1a9d393 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -181,7 +181,7 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, for (i = 0; i < ARRAY_SIZE(timings); i++) { if (v4l2_dv_valid_timings(timings + i, cap) && - v4l_match_dv_timings(t, timings + i, pclock_delta)) { + v4l2_match_dv_timings(t, timings + i, pclock_delta)) { *t = timings[i]; return true; } @@ -191,16 +191,16 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, EXPORT_SYMBOL_GPL(v4l2_find_dv_timings_cap); /** - * v4l_match_dv_timings - check if two timings match + * v4l2_match_dv_timings - check if two timings match * @t1 - compare this v4l2_dv_timings struct... * @t2 - with this struct. * @pclock_delta - the allowed pixelclock deviation. * * Compare t1 with t2 with a given margin of error for the pixelclock. */ -bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, - const struct v4l2_dv_timings *t2, - unsigned pclock_delta) +bool v4l2_match_dv_timings(const struct v4l2_dv_timings *t1, + const struct v4l2_dv_timings *t2, + unsigned pclock_delta) { if (t1->type != t2->type || t1->type != V4L2_DV_BT_656_1120) return false; @@ -221,7 +221,7 @@ bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, return true; return false; } -EXPORT_SYMBOL_GPL(v4l_match_dv_timings); +EXPORT_SYMBOL_GPL(v4l2_match_dv_timings); void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, const struct v4l2_dv_timings *t, bool detailed) diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 696e5c2..43f6b67 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -64,7 +64,7 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, unsigned pclock_delta); -/** v4l_match_dv_timings() - do two timings match? +/** v4l2_match_dv_timings() - do two timings match? * @measured: the measured timings data. * @standard: the timings according to the standard. * @pclock_delta: maximum delta in Hz between standard->pixelclock and @@ -72,9 +72,9 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, * * Returns true if the two timings match, returns false otherwise. */ -bool v4l_match_dv_timings(const struct v4l2_dv_timings *measured, - const struct v4l2_dv_timings *standard, - unsigned pclock_delta); +bool v4l2_match_dv_timings(const struct v4l2_dv_timings *measured, + const struct v4l2_dv_timings *standard, + unsigned pclock_delta); /** v4l2_print_dv_timings() - log the contents of a dv_timings struct * @dev_prefix:device prefix for each log line. -- cgit v0.10.2 From fe9c2564d80d633d08a2d69fe38598c06a0ddfb2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 08:07:26 -0300 Subject: [media] adv7604/ad9389b/ths8200: decrease min_pixelclock to 25MHz The CEA-861 standard allows for the 640x480 format at 25.175 MHz. Ensure that that's allowed according to the struct v4l2_bt_timings_cap. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 1c6d352..bb74fb6 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -631,7 +631,7 @@ static const struct v4l2_dv_timings_cap ad9389b_timings_cap = { .bt = { .max_width = 1920, .max_height = 1200, - .min_pixelclock = 27000000, + .min_pixelclock = 25000000, .max_pixelclock = 170000000, .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index a1a9d1e..5b54ba1 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1162,7 +1162,7 @@ static int adv7604_dv_timings_cap(struct v4l2_subdev *sd, cap->type = V4L2_DV_BT_656_1120; cap->bt.max_width = 1920; cap->bt.max_height = 1200; - cap->bt.min_pixelclock = 27000000; + cap->bt.min_pixelclock = 25000000; if (DIGITAL_INPUT) cap->bt.max_pixelclock = 225000000; else diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 49041e5..580bd1b 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -49,7 +49,7 @@ static const struct v4l2_dv_timings_cap ths8200_timings_cap = { .bt = { .max_width = 1920, .max_height = 1080, - .min_pixelclock = 27000000, + .min_pixelclock = 25000000, .max_pixelclock = 148500000, .standards = V4L2_DV_BT_STD_CEA861, .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE, -- cgit v0.10.2 From 074ca43f2f99c926a250847ae59a337ebdec6d61 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 08:38:29 -0300 Subject: [media] v4l2-dv-timings: fill in type field The detect_cvt/gtf functions didn't fill in the type field. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 1a9d393..c2f5af7 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -408,6 +408,7 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, h_fp = h_blank - hsync - h_bp; } + fmt->type = V4L2_DV_BT_656_1120; fmt->bt.polarities = polarities; fmt->bt.width = image_width; fmt->bt.height = image_height; @@ -527,6 +528,7 @@ bool v4l2_detect_gtf(unsigned frame_height, h_fp = h_blank / 2 - hsync; + fmt->type = V4L2_DV_BT_656_1120; fmt->bt.polarities = polarities; fmt->bt.width = image_width; fmt->bt.height = image_height; -- cgit v0.10.2 From d1c65ad6a44b0ff79d2f0bf726fa6fd9248991f4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 10:19:54 -0300 Subject: [media] v4l2-dv-timings: export the timings list Some drivers need to be able to access the full list of timings. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index c2f5af7..f515997 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -26,7 +26,7 @@ #include #include -static const struct v4l2_dv_timings timings[] = { +const struct v4l2_dv_timings v4l2_dv_timings_presets[] = { V4L2_DV_BT_CEA_640X480P59_94, V4L2_DV_BT_CEA_720X480I59_94, V4L2_DV_BT_CEA_720X480P59_94, @@ -127,7 +127,9 @@ static const struct v4l2_dv_timings timings[] = { V4L2_DV_BT_DMT_2560X1600P75, V4L2_DV_BT_DMT_2560X1600P85, V4L2_DV_BT_DMT_2560X1600P120_RB, + { } }; +EXPORT_SYMBOL_GPL(v4l2_dv_timings_presets); bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *dvcap) @@ -159,10 +161,10 @@ int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, u32 i, idx; memset(t->reserved, 0, sizeof(t->reserved)); - for (i = idx = 0; i < ARRAY_SIZE(timings); i++) { - if (v4l2_dv_valid_timings(timings + i, cap) && + for (i = idx = 0; v4l2_dv_timings_presets[i].bt.width; i++) { + if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && idx++ == t->index) { - t->timings = timings[i]; + t->timings = v4l2_dv_timings_presets[i]; return 0; } } @@ -179,10 +181,10 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, if (!v4l2_dv_valid_timings(t, cap)) return false; - for (i = 0; i < ARRAY_SIZE(timings); i++) { - if (v4l2_dv_valid_timings(timings + i, cap) && - v4l2_match_dv_timings(t, timings + i, pclock_delta)) { - *t = timings[i]; + for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) { + if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && + v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, pclock_delta)) { + *t = v4l2_dv_timings_presets[i]; return true; } } diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 43f6b67..0fe310b 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -23,6 +23,10 @@ #include +/** v4l2_dv_timings_presets: list of all dv_timings presets. + */ +extern const struct v4l2_dv_timings v4l2_dv_timings_presets[]; + /** v4l2_dv_valid_timings() - are these timings valid? * @t: the v4l2_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. -- cgit v0.10.2 From 70b654945bacd27622ef1c424f054ae04de597e0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 10:23:33 -0300 Subject: [media] v4l2-dv-timings: rename v4l2_dv_valid_timings to v4l2_valid_dv_timings All other functions follow the v4l2__dv_timings pattern, do the same for this function. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index bb74fb6..fc60851 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -648,7 +648,7 @@ static int ad9389b_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); /* quick sanity check */ - if (!v4l2_dv_valid_timings(timings, &ad9389b_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap)) return -EINVAL; /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 580bd1b..6abf0fb 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -378,7 +378,7 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); - if (!v4l2_dv_valid_timings(timings, &ths8200_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap)) return -EINVAL; if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10)) { diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index f515997..a77f201 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -131,7 +131,7 @@ const struct v4l2_dv_timings v4l2_dv_timings_presets[] = { }; EXPORT_SYMBOL_GPL(v4l2_dv_timings_presets); -bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, +bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *dvcap) { const struct v4l2_bt_timings *bt = &t->bt; @@ -153,7 +153,7 @@ bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, return false; return true; } -EXPORT_SYMBOL_GPL(v4l2_dv_valid_timings); +EXPORT_SYMBOL_GPL(v4l2_valid_dv_timings); int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, const struct v4l2_dv_timings_cap *cap) @@ -162,7 +162,7 @@ int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, memset(t->reserved, 0, sizeof(t->reserved)); for (i = idx = 0; v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && idx++ == t->index) { t->timings = v4l2_dv_timings_presets[i]; return 0; @@ -178,11 +178,11 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, { int i; - if (!v4l2_dv_valid_timings(t, cap)) + if (!v4l2_valid_dv_timings(t, cap)) return false; for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, pclock_delta)) { *t = v4l2_dv_timings_presets[i]; return true; diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 0fe310b..bd59df8 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -27,14 +27,14 @@ */ extern const struct v4l2_dv_timings v4l2_dv_timings_presets[]; -/** v4l2_dv_valid_timings() - are these timings valid? +/** v4l2_valid_dv_timings() - are these timings valid? * @t: the v4l2_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. * * Returns true if the given dv_timings struct is supported by the * hardware capabilities, returns false otherwise. */ -bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, +bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap); /** v4l2_enum_dv_timings_cap() - Helper function to enumerate possible DV timings based on capabilities -- cgit v0.10.2 From b8f0fff4279a1b85fa4b6d7d8b538c254edcb4a1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 11:21:50 -0300 Subject: [media] v4l2-dv-timings: add callback to handle exceptions In most cases the v4l2_bt_timings_cap struct has all the information necessary to determine valid timings, but occasionally there are exceptions. Add a callback function to be able to test for those exceptions. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index fc60851..8369786 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -648,12 +648,12 @@ static int ad9389b_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); /* quick sanity check */ - if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap, NULL, NULL)) return -EINVAL; /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings if the format is one of the CEA or DMT timings. */ - v4l2_find_dv_timings_cap(timings, &ad9389b_timings_cap, 0); + v4l2_find_dv_timings_cap(timings, &ad9389b_timings_cap, 0, NULL, NULL); timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS; @@ -691,7 +691,8 @@ static int ad9389b_g_dv_timings(struct v4l2_subdev *sd, static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { - return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap); + return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap, + NULL, NULL); } static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd, diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 6abf0fb..a58a8f6 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -378,10 +378,12 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); - if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap, + NULL, NULL)) return -EINVAL; - if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10)) { + if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10, + NULL, NULL)) { v4l2_dbg(1, debug, sd, "Unsupported format\n"); return -EINVAL; } @@ -411,7 +413,8 @@ static int ths8200_g_dv_timings(struct v4l2_subdev *sd, static int ths8200_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { - return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap); + return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap, + NULL, NULL); } static int ths8200_dv_timings_cap(struct v4l2_subdev *sd, diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index a77f201..ee52b9f4 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -132,7 +132,9 @@ const struct v4l2_dv_timings v4l2_dv_timings_presets[] = { EXPORT_SYMBOL_GPL(v4l2_dv_timings_presets); bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, - const struct v4l2_dv_timings_cap *dvcap) + const struct v4l2_dv_timings_cap *dvcap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle) { const struct v4l2_bt_timings *bt = &t->bt; const struct v4l2_bt_timings_cap *cap = &dvcap->bt; @@ -151,18 +153,21 @@ bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, (bt->interlaced && !(caps & V4L2_DV_BT_CAP_INTERLACED)) || (!bt->interlaced && !(caps & V4L2_DV_BT_CAP_PROGRESSIVE))) return false; - return true; + return fnc == NULL || fnc(t, fnc_handle); } EXPORT_SYMBOL_GPL(v4l2_valid_dv_timings); int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, - const struct v4l2_dv_timings_cap *cap) + const struct v4l2_dv_timings_cap *cap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle) { u32 i, idx; memset(t->reserved, 0, sizeof(t->reserved)); for (i = idx = 0; v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap, + fnc, fnc_handle) && idx++ == t->index) { t->timings = v4l2_dv_timings_presets[i]; return 0; @@ -174,16 +179,20 @@ EXPORT_SYMBOL_GPL(v4l2_enum_dv_timings_cap); bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, - unsigned pclock_delta) + unsigned pclock_delta, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle) { int i; - if (!v4l2_valid_dv_timings(t, cap)) + if (!v4l2_valid_dv_timings(t, cap, fnc, fnc_handle)) return false; for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && - v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, pclock_delta)) { + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap, + fnc, fnc_handle) && + v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, + pclock_delta)) { *t = v4l2_dv_timings_presets[i]; return true; } diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index bd59df8..4becc67 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -27,46 +27,68 @@ */ extern const struct v4l2_dv_timings v4l2_dv_timings_presets[]; +/** v4l2_check_dv_timings_fnc - timings check callback + * @t: the v4l2_dv_timings struct. + * @handle: a handle from the driver. + * + * Returns true if the given timings are valid. + */ +typedef bool v4l2_check_dv_timings_fnc(const struct v4l2_dv_timings *t, void *handle); + /** v4l2_valid_dv_timings() - are these timings valid? * @t: the v4l2_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. + * @fnc: callback to check if this timing is OK. May be NULL. + * @fnc_handle: a handle that is passed on to @fnc. * * Returns true if the given dv_timings struct is supported by the - * hardware capabilities, returns false otherwise. + * hardware capabilities and the callback function (if non-NULL), returns + * false otherwise. */ bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, - const struct v4l2_dv_timings_cap *cap); + const struct v4l2_dv_timings_cap *cap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle); /** v4l2_enum_dv_timings_cap() - Helper function to enumerate possible DV timings based on capabilities * @t: the v4l2_enum_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. + * @fnc: callback to check if this timing is OK. May be NULL. + * @fnc_handle: a handle that is passed on to @fnc. * * This enumerates dv_timings using the full list of possible CEA-861 and DMT * timings, filtering out any timings that are not supported based on the - * hardware capabilities. + * hardware capabilities and the callback function (if non-NULL). * * If a valid timing for the given index is found, it will fill in @t and * return 0, otherwise it returns -EINVAL. */ int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, - const struct v4l2_dv_timings_cap *cap); + const struct v4l2_dv_timings_cap *cap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle); /** v4l2_find_dv_timings_cap() - Find the closest timings struct * @t: the v4l2_enum_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. * @pclock_delta: maximum delta between t->pixelclock and the timing struct * under consideration. + * @fnc: callback to check if a given timings struct is OK. May be NULL. + * @fnc_handle: a handle that is passed on to @fnc. * * This function tries to map the given timings to an entry in the * full list of possible CEA-861 and DMT timings, filtering out any timings - * that are not supported based on the hardware capabilities. + * that are not supported based on the hardware capabilities and the callback + * function (if non-NULL). * * On success it will fill in @t with the found timings and it returns true. * On failure it will return false. */ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, - unsigned pclock_delta); + unsigned pclock_delta, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle); /** v4l2_match_dv_timings() - do two timings match? * @measured: the measured timings data. -- cgit v0.10.2 From 8c0eadb88bc67cee8b83e08f5743bd7c378efdd9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 22 Aug 2013 06:11:17 -0300 Subject: [media] adv7604: set is_private only after successfully creating all controls is_private was set right after creating each control, but the control pointer might be NULL in case of an error. Set it after all controls were successfully created, since that guarantees that all control pointers are non-NULL. 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 5b54ba1..fbfdd2f 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2021,29 +2021,30 @@ static int adv7604_probe(struct i2c_client *client, /* private controls */ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); - state->detect_tx_5v_ctrl->is_private = true; state->rgb_quantization_range_ctrl = v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops, V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0, V4L2_DV_RGB_RANGE_AUTO); - state->rgb_quantization_range_ctrl->is_private = true; /* custom controls */ state->analog_sampling_phase_ctrl = v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL); - state->analog_sampling_phase_ctrl->is_private = true; state->free_run_color_manual_ctrl = v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color_manual, NULL); - state->free_run_color_manual_ctrl->is_private = true; state->free_run_color_ctrl = v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color, NULL); - state->free_run_color_ctrl->is_private = true; sd->ctrl_handler = hdl; if (hdl->error) { err = hdl->error; goto err_hdl; } + state->detect_tx_5v_ctrl->is_private = true; + state->rgb_quantization_range_ctrl->is_private = true; + state->analog_sampling_phase_ctrl->is_private = true; + state->free_run_color_manual_ctrl->is_private = true; + state->free_run_color_ctrl->is_private = true; + if (adv7604_s_detect_tx_5v_ctrl(sd)) { err = -ENODEV; goto err_hdl; -- cgit v0.10.2 From 265d3b55ffdb5e9cf74fd85e2a841b04a82f3355 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 22 Aug 2013 06:15:31 -0300 Subject: [media] ad9389b: set is_private only after successfully creating all controls is_private was set right after creating each control, but the control pointer might be NULL in case of an error. Set it after all controls were successfully created, since that guarantees that all control pointers are non-NULL. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 8369786..bb0c99d 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -1109,27 +1109,27 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * state->hdmi_mode_ctrl = v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops, V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, 0, V4L2_DV_TX_MODE_DVI_D); - state->hdmi_mode_ctrl->is_private = true; state->hotplug_ctrl = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_TX_HOTPLUG, 0, 1, 0, 0); - state->hotplug_ctrl->is_private = true; state->rx_sense_ctrl = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_TX_RXSENSE, 0, 1, 0, 0); - state->rx_sense_ctrl->is_private = true; state->have_edid0_ctrl = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_TX_EDID_PRESENT, 0, 1, 0, 0); - state->have_edid0_ctrl->is_private = true; state->rgb_quantization_range_ctrl = v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops, V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0, V4L2_DV_RGB_RANGE_AUTO); - state->rgb_quantization_range_ctrl->is_private = true; sd->ctrl_handler = hdl; if (hdl->error) { err = hdl->error; goto err_hdl; } + state->hdmi_mode_ctrl->is_private = true; + state->hotplug_ctrl->is_private = true; + state->rx_sense_ctrl->is_private = true; + state->have_edid0_ctrl->is_private = true; + state->rgb_quantization_range_ctrl->is_private = true; state->pad.flags = MEDIA_PAD_FL_SINK; err = media_entity_init(&sd->entity, 1, &state->pad, 0); -- cgit v0.10.2 From b82180dba53e71fbc1b08bc8beca75d1dea5e993 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 22 Aug 2013 12:41:14 -0300 Subject: [media] gspca_ov519: Fix support for the Terratec Terracam USB Pro This is a camera with an ov518+ revision 0 bridge + ov7620ae sensor, which appearently needs different handling then the Trust spacecam 320, which has an ov518+ revision 2 + ov7620ae sensor. The Terracam USB Pro used to write this patch has kindly been provided by Dr. Tilmann Bubeck . Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c index a3958ee..8937d79 100644 --- a/drivers/media/usb/gspca/ov519.c +++ b/drivers/media/usb/gspca/ov519.c @@ -75,6 +75,8 @@ struct sd { struct v4l2_ctrl *brightness; }; + u8 revision; + u8 packet_nr; char bridge; @@ -3080,8 +3082,8 @@ static void ov518_configure(struct gspca_dev *gspca_dev) }; /* First 5 bits of custom ID reg are a revision ID on OV518 */ - PDEBUG(D_PROBE, "Device revision %d", - 0x1f & reg_r(sd, R51x_SYS_CUST_ID)); + sd->revision = reg_r(sd, R51x_SYS_CUST_ID) & 0x1f; + PDEBUG(D_PROBE, "Device revision %d", sd->revision); write_regvals(sd, init_518, ARRAY_SIZE(init_518)); @@ -3657,7 +3659,11 @@ static void ov518_mode_init_regs(struct sd *sd) reg_w(sd, 0x2f, 0x80); /******** Set the framerate ********/ - sd->clockdiv = 1; + if (sd->bridge == BRIDGE_OV518PLUS && sd->revision == 0 && + sd->sensor == SEN_OV7620AE) + sd->clockdiv = 0; + else + sd->clockdiv = 1; /* Mode independent, but framerate dependent, regs */ /* 0x51: Clock divider; Only works on some cams which use 2 crystals */ @@ -3668,12 +3674,24 @@ static void ov518_mode_init_regs(struct sd *sd) if (sd->bridge == BRIDGE_OV518PLUS) { switch (sd->sensor) { case SEN_OV7620AE: - if (sd->gspca_dev.width == 320) { - reg_w(sd, 0x20, 0x00); - reg_w(sd, 0x21, 0x19); - } else { + /* + * HdG: 640x480 needs special handling on device + * revision 2, we check for device revison > 0 to + * avoid regressions, as we don't know the correct + * thing todo for revision 1. + * + * Also this likely means we don't need to + * differentiate between the OV7620 and OV7620AE, + * earlier testing hitting this same problem likely + * happened to be with revision < 2 cams using an + * OV7620 and revision 2 cams using an OV7620AE. + */ + if (sd->revision > 0 && sd->gspca_dev.width == 640) { reg_w(sd, 0x20, 0x60); reg_w(sd, 0x21, 0x1f); + } else { + reg_w(sd, 0x20, 0x00); + reg_w(sd, 0x21, 0x19); } break; case SEN_OV7620: -- cgit v0.10.2 From d334ab08e8e1f6907d44c716f852f49076e957ca Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 26 Jun 2013 10:37:14 -0300 Subject: [media] exynos4-is: Handle suspend/resume of fimc-is-i2c correctly If the same callbacks are used for runtime and system suspend/resume, clocks can get disabled twice, which can lead to negative reference counts and kernel warnings. This patch splits suspend/resume callbacks into separate runtime and system-wide functions, so clock gating is done correctly. Signed-off-by: Tomasz Figa Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c index 617a798..6bc481a 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-i2c.c +++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c @@ -83,21 +83,46 @@ static int fimc_is_i2c_remove(struct platform_device *pdev) return 0; } -static int fimc_is_i2c_suspend(struct device *dev) +#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_PM_SLEEP) +static int fimc_is_i2c_runtime_suspend(struct device *dev) { struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev); + clk_disable_unprepare(isp_i2c->clock); return 0; } -static int fimc_is_i2c_resume(struct device *dev) +static int fimc_is_i2c_runtime_resume(struct device *dev) { struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev); + return clk_prepare_enable(isp_i2c->clock); } +#endif -static UNIVERSAL_DEV_PM_OPS(fimc_is_i2c_pm_ops, fimc_is_i2c_suspend, - fimc_is_i2c_resume, NULL); +#ifdef CONFIG_PM_SLEEP +static int fimc_is_i2c_suspend(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + return 0; + + return fimc_is_i2c_runtime_suspend(dev); +} + +static int fimc_is_i2c_resume(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + return 0; + + return fimc_is_i2c_runtime_resume(dev); +} +#endif + +static struct dev_pm_ops fimc_is_i2c_pm_ops = { + SET_RUNTIME_PM_OPS(fimc_is_i2c_runtime_suspend, + fimc_is_i2c_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(fimc_is_i2c_suspend, fimc_is_i2c_resume) +}; static const struct of_device_id fimc_is_i2c_of_match[] = { { .compatible = FIMC_IS_I2C_COMPATIBLE }, -- cgit v0.10.2 From c1cd2b96e2475437be1f1cd4ec757ef814228531 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 5 Aug 2013 10:35:25 -0300 Subject: [media] exynos4-is: Initialize the ISP subdev sd->owner field Set the subdevs owner module so the exynos4_fimc_is module cannot be unloaded when the FIMC-IS driver is in use. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c index cf520a7..d2e6cba 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -672,6 +672,8 @@ int fimc_isp_subdev_create(struct fimc_isp *isp) mutex_init(&isp->subdev_lock); v4l2_subdev_init(sd, &fimc_is_subdev_ops); + + sd->owner = THIS_MODULE; sd->grp_id = GRP_ID_FIMC_IS; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP"); -- cgit v0.10.2 From fdb4982734e71d06ed3db073cf63f7f5fddc5f4b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 31 Jul 2013 11:08:19 -0300 Subject: [media] exynos4-is: Add missing MODULE_LICENSE for exynos-fimc-is.ko This fixes compilation warning: WARNING: modpost: missing MODULE_LICENSE() in drivers/media/platform/exynos4-is/exynos-fimc-is.o Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 967f6a9..9b7fd1c 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -993,3 +993,4 @@ module_exit(fimc_is_module_exit); MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME); MODULE_AUTHOR("Younghwan Joo "); MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From b74bee1584cf88ed28d15520ff7e74bdc69a7f64 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 9 Aug 2013 14:23:53 -0300 Subject: [media] exynos4-is: Add missing v4l2_device_unregister() call in fimc_md_remove() Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 19f556c..42f121c 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1544,6 +1544,8 @@ static int fimc_md_remove(struct platform_device *pdev) if (!fmd) return 0; + + v4l2_device_unregister(&fmd->v4l2_dev); device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); fimc_md_unregister_entities(fmd); fimc_md_pipelines_free(fmd); -- cgit v0.10.2 From 044c372aeb87946c64f10fe7bbe0f8c4b3222121 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 10:05:17 -0300 Subject: [media] exynos4-is: Simplify sclk_cam clocks handling Use clk_prepare_enable()/clk_disable_unprepare() instead of separately prearing/unparing the clk_cam clocks. This simplifies the code that is now mostly not going to be used, function __fimc_md_set_camclk() is only left for S5PV210 platform which is not yet converted to Device Tree. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 42f121c..ef6642b 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1150,7 +1150,6 @@ static void fimc_md_put_clocks(struct fimc_md *fmd) while (--i >= 0) { if (IS_ERR(fmd->camclk[i].clock)) continue; - clk_unprepare(fmd->camclk[i].clock); clk_put(fmd->camclk[i].clock); fmd->camclk[i].clock = ERR_PTR(-EINVAL); } @@ -1169,7 +1168,7 @@ static int fimc_md_get_clocks(struct fimc_md *fmd) struct device *dev = NULL; char clk_name[32]; struct clk *clock; - int ret, i; + int i, ret = 0; for (i = 0; i < FIMC_MAX_CAMCLKS; i++) fmd->camclk[i].clock = ERR_PTR(-EINVAL); @@ -1187,12 +1186,6 @@ static int fimc_md_get_clocks(struct fimc_md *fmd) ret = PTR_ERR(clock); break; } - ret = clk_prepare(clock); - if (ret < 0) { - clk_put(clock); - fmd->camclk[i].clock = ERR_PTR(-EINVAL); - break; - } fmd->camclk[i].clock = clock; } if (ret) @@ -1249,7 +1242,7 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, ret = pm_runtime_get_sync(fmd->pmf); if (ret < 0) return ret; - ret = clk_enable(camclk->clock); + ret = clk_prepare_enable(camclk->clock); dbg("Enabled camclk %d: f: %lu", si->clk_id, clk_get_rate(camclk->clock)); } @@ -1260,7 +1253,7 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, return 0; if (--camclk->use_count == 0) { - clk_disable(camclk->clock); + clk_disable_unprepare(camclk->clock); pm_runtime_put(fmd->pmf); dbg("Disabled camclk %d", si->clk_id); } -- cgit v0.10.2 From 3263f741e45bd6571a81528a771b863dad449af0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 2 Aug 2013 02:32:13 -0300 Subject: [media] exynos4-is: Annotate unused functions __is_set_init_isp_aa and fimc_is_hw_set_tune currently do not have any callers. However these functions may be used in the future. Hence instead of deleting them, staticize and annotate them with __maybe_unused flag to avoid compiler warnings. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c index a353be0..9bf3ddd 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-param.c +++ b/drivers/media/platform/exynos4-is/fimc-is-param.c @@ -287,7 +287,7 @@ void __is_set_sensor(struct fimc_is *is, int fps) fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT); } -void __is_set_init_isp_aa(struct fimc_is *is) +static void __maybe_unused __is_set_init_isp_aa(struct fimc_is *is) { struct isp_param *isp; diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c index 63c68ec..cf2e13a 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.c +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c @@ -96,7 +96,7 @@ int fimc_is_hw_set_param(struct fimc_is *is) return 0; } -int fimc_is_hw_set_tune(struct fimc_is *is) +static int __maybe_unused fimc_is_hw_set_tune(struct fimc_is *is) { fimc_is_hw_wait_intmsr0_intmsd0(is); -- cgit v0.10.2 From 915b034f81da8e2b6ebcfde8540d2d4ea5d6c133 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Aug 2013 05:36:56 -0300 Subject: [media] exynos4-is: Print error message on timeout There is a stray '!' character so the error message never gets printed. Signed-off-by: Dan Carpenter Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c index cf2e13a..f758e26 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.c +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c @@ -236,7 +236,7 @@ int fimc_is_itf_mode_change(struct fimc_is *is) fimc_is_hw_change_mode(is); ret = fimc_is_wait_event(is, IS_ST_CHANGE_MODE, 1, FIMC_IS_CONFIG_TIMEOUT); - if (!ret < 0) + if (ret < 0) dev_err(&is->pdev->dev, "%s(): mode change (%d) timeout\n", __func__, is->config_index); return ret; -- cgit v0.10.2 From a5b5f3bec2bdc9a61b8f8190a3b3c32c64f19525 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Fri, 19 Jul 2013 07:39:53 -0300 Subject: [media] exynos4-is: Ensure the FIMC gate clock is disabled at driver remove() The patch fixes following warning: [ 9664.460000] WARNING: CPU: 0 PID: 2880 at drivers/clk/clk.c:695 __clk_unprepare+0x8c/0xa4() [ 9664.470000] Modules linked in: m5mols s5k5baf s5p_fimc(-) ipv6 s5p_csis v4l2_mem2mem videobuf2_dma_contig videobuf2_memops exynos4_is_common videobuf2_core [last unloaded: m5mols] [ 9664.485000] CPU: 0 PID: 2880 Comm: rmmod Tainted: G W 3.11.0-rc1-00070-ga94e22f-dirty #1558 [ 9664.495000] [] (unwind_backtrace+0x0/0xf8) from [] (show_stack+0x10/0x14) [ 9664.505000] [] (show_stack+0x10/0x14) from [] (dump_stack+0x6c/0xac) [ 9664.510000] [] (dump_stack+0x6c/0xac) from [] (warn_slowpath_common+0x64/0x88) [ 9664.520000] [] (warn_slowpath_common+0x64/0x88) from [] (warn_slowpath_null+0x1c/0x24) [ 9664.530000] [] (warn_slowpath_null+0x1c/0x24) from [] (__clk_unprepare+0x8c/0xa4) [ 9664.540000] [] (__clk_unprepare+0x8c/0xa4) from [] (clk_unprepare+0x14/0x1c) [ 9664.550000] [] (clk_unprepare+0x14/0x1c) from [] (fimc_clk_put+0x3c/0x5c [s5p_fimc]) [ 9664.560000] [] (fimc_clk_put+0x3c/0x5c [s5p_fimc]) from [] (fimc_remove+0x5c/0x90 [s5p_fimc]) [ 9664.570000] [] (fimc_remove+0x5c/0x90 [s5p_fimc]) from [] (platform_drv_remove+0x18/0x1c) [ 9664.580000] [] (platform_drv_remove+0x18/0x1c) from [] (__device_release_driver+0x70/0xcc) [ 9664.590000] [] (__device_release_driver+0x70/0xcc) from [] (driver_detach+0xac/0xb0) [ 9664.595000] [] (driver_detach+0xac/0xb0) from [] (bus_remove_driver+0x7c/0xc0) [ 9664.605000] [] (bus_remove_driver+0x7c/0xc0) from [] (SyS_delete_module+0x11c/0x204) [ 9664.615000] [] (SyS_delete_module+0x11c/0x204) from [] (ret_fast_syscall+0x0/0x30) [ 9664.625000] ---[ end trace 662c092cce432c8d ]--- Signed-off-by: Andrzej Hajda Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index 6489c51..3d66d88 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -1110,6 +1110,8 @@ static int fimc_remove(struct platform_device *pdev) struct fimc_dev *fimc = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + clk_disable(fimc->clock[CLK_GATE]); pm_runtime_set_suspended(&pdev->dev); fimc_unregister_capture_subdev(fimc); -- cgit v0.10.2 From 5c47776a2d6e36f09c6162196155bd59e70edbfb Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 23 Aug 2013 13:26:10 -0300 Subject: [media] s5p-tv: Include missing v4l2-dv-timings.h header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include the v4l2-dv-timings.h header file which in the s5p-tv driver which was supposed to be updated in commit 2576415846bcbad3c0a6885fc44f95083710 "[media] v4l2: move dv-timings related code to v4l2-dv-timings.c" This fixes following build error: drivers/media/platform/s5p-tv/hdmi_drv.c: In function ‘hdmi_s_dv_timings’: drivers/media/platform/s5p-tv/hdmi_drv.c:628:3: error: implicit declaration of function ‘v4l_match_dv_timings’ Signed-off-by: Sylwester Nawrocki Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 4ad9374..534722c 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "regs-hdmi.h" -- cgit v0.10.2 From d345a5e59b0af5919f4d7f2a0d4c8dcae657e9d1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Aug 2013 05:41:47 -0300 Subject: [media] s3c-camif: forever loop in camif_hw_set_source_format() Because "i" is unsigned then "i-- >= 0" is always true. If we don't find what we are looking for then we loop forever. Signed-off-by: Dan Carpenter Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c index a9e3b16..ebf5b18 100644 --- a/drivers/media/platform/s3c-camif/camif-regs.c +++ b/drivers/media/platform/s3c-camif/camif-regs.c @@ -106,15 +106,15 @@ static const u32 src_pixfmt_map[8][2] = { void camif_hw_set_source_format(struct camif_dev *camif) { struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; - unsigned int i = ARRAY_SIZE(src_pixfmt_map); + int i; u32 cfg; - while (i-- >= 0) { + for (i = ARRAY_SIZE(src_pixfmt_map) - 1; i >= 0; i--) { if (src_pixfmt_map[i][0] == mf->code) break; } - - if (i == 0 && src_pixfmt_map[i][0] != mf->code) { + if (i < 0) { + i = 0; dev_err(camif->dev, "Unsupported pixel code, falling back to %#08x\n", src_pixfmt_map[i][0]); -- cgit v0.10.2 From 174e60adf4bfb849672b3b2afdd3cfd6e65d1d3d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Aug 2013 05:33:06 -0300 Subject: [media] s5k6aa: off by one in s5k6aa_enum_frame_interval() The check is off by one so we could read one space past the end of the array. Signed-off-by: Dan Carpenter Acked-by: Laurent Pinchart Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index 789c02a..629a5cd 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -1003,7 +1003,7 @@ static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, const struct s5k6aa_interval *fi; int ret = 0; - if (fie->index > ARRAY_SIZE(s5k6aa_intervals)) + if (fie->index >= ARRAY_SIZE(s5k6aa_intervals)) return -EINVAL; v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN, -- cgit v0.10.2 From 6a4760ed50e4908cfd597be0d840a0cb990aff7a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Aug 2013 05:33:48 -0300 Subject: [media] ov9650: off by one in ov965x_enum_frame_sizes() The ">" should be ">=" otherwise we read one space beyond the end of the array. Signed-off-by: Dan Carpenter Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index 1dbb811..4da90c6 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1083,7 +1083,7 @@ static int ov965x_enum_frame_sizes(struct v4l2_subdev *sd, { int i = ARRAY_SIZE(ov965x_formats); - if (fse->index > ARRAY_SIZE(ov965x_framesizes)) + if (fse->index >= ARRAY_SIZE(ov965x_framesizes)) return -EINVAL; while (--i) -- cgit v0.10.2 From 803c2df217525495fc440fc9b314b22427dc5db6 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 22 Aug 2013 17:36:36 -0300 Subject: [media] ARM: shmobile: r8a7778: add VIN support Add VIN clocks and platform devices on R8A7778 SoC; add function to register the VIN platform devices. [Sergei: added 'id' parameter check to r8a7778_add_vin_device(), used '*pdata' in *sizeof* operator, and added an empty line there; renamed some variables, annotated 'vin[01]_info' and vin[01]_resources[] as '__initdata'.] Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Acked-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/mach-shmobile/clock-r8a7778.c b/arch/arm/mach-shmobile/clock-r8a7778.c index a0e9eb7..c4bf2d8 100644 --- a/arch/arm/mach-shmobile/clock-r8a7778.c +++ b/arch/arm/mach-shmobile/clock-r8a7778.c @@ -106,6 +106,7 @@ enum { MSTP331, MSTP323, MSTP322, MSTP321, MSTP114, + MSTP110, MSTP109, MSTP100, MSTP030, MSTP029, MSTP028, MSTP027, MSTP026, MSTP025, MSTP024, MSTP023, MSTP022, MSTP021, @@ -119,6 +120,8 @@ static struct clk mstp_clks[MSTP_NR] = { [MSTP322] = SH_CLK_MSTP32(&p_clk, MSTPCR3, 22, 0), /* SDHI1 */ [MSTP321] = SH_CLK_MSTP32(&p_clk, MSTPCR3, 21, 0), /* SDHI2 */ [MSTP114] = SH_CLK_MSTP32(&p_clk, MSTPCR1, 14, 0), /* Ether */ + [MSTP110] = SH_CLK_MSTP32(&s_clk, MSTPCR1, 10, 0), /* VIN0 */ + [MSTP109] = SH_CLK_MSTP32(&s_clk, MSTPCR1, 9, 0), /* VIN1 */ [MSTP100] = SH_CLK_MSTP32(&p_clk, MSTPCR1, 0, 0), /* USB0/1 */ [MSTP030] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 30, 0), /* I2C0 */ [MSTP029] = SH_CLK_MSTP32(&p_clk, MSTPCR0, 29, 0), /* I2C1 */ @@ -146,6 +149,8 @@ static struct clk_lookup lookups[] = { CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP322]), /* SDHI1 */ CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP321]), /* SDHI2 */ CLKDEV_DEV_ID("r8a777x-ether", &mstp_clks[MSTP114]), /* Ether */ + CLKDEV_DEV_ID("r8a7778-vin.0", &mstp_clks[MSTP110]), /* VIN0 */ + CLKDEV_DEV_ID("r8a7778-vin.1", &mstp_clks[MSTP109]), /* VIN1 */ CLKDEV_DEV_ID("ehci-platform", &mstp_clks[MSTP100]), /* USB EHCI port0/1 */ CLKDEV_DEV_ID("ohci-platform", &mstp_clks[MSTP100]), /* USB OHCI port0/1 */ CLKDEV_DEV_ID("i2c-rcar.0", &mstp_clks[MSTP030]), /* I2C0 */ diff --git a/arch/arm/mach-shmobile/include/mach/r8a7778.h b/arch/arm/mach-shmobile/include/mach/r8a7778.h index 851d027..a7c6d15 100644 --- a/arch/arm/mach-shmobile/include/mach/r8a7778.h +++ b/arch/arm/mach-shmobile/include/mach/r8a7778.h @@ -22,6 +22,7 @@ #include #include #include +#include extern void r8a7778_add_standard_devices(void); extern void r8a7778_add_standard_devices_dt(void); @@ -30,6 +31,8 @@ extern void r8a7778_add_usb_phy_device(struct rcar_phy_platform_data *pdata); extern void r8a7778_add_i2c_device(int id); extern void r8a7778_add_hspi_device(int id); extern void r8a7778_add_mmc_device(struct sh_mmcif_plat_data *info); +extern void r8a7778_add_vin_device(int id, + struct rcar_vin_platform_data *pdata); extern void r8a7778_init_late(void); extern void r8a7778_init_delay(void); diff --git a/arch/arm/mach-shmobile/setup-r8a7778.c b/arch/arm/mach-shmobile/setup-r8a7778.c index 80c2039..0174f05 100644 --- a/arch/arm/mach-shmobile/setup-r8a7778.c +++ b/arch/arm/mach-shmobile/setup-r8a7778.c @@ -333,6 +333,40 @@ void __init r8a7778_add_mmc_device(struct sh_mmcif_plat_data *info) info, sizeof(*info)); } +/* VIN */ +#define R8A7778_VIN(idx) \ +static struct resource vin##idx##_resources[] __initdata = { \ + DEFINE_RES_MEM(0xffc50000 + 0x1000 * (idx), 0x1000), \ + DEFINE_RES_IRQ(gic_iid(0x5a)), \ +}; \ + \ +static struct platform_device_info vin##idx##_info __initdata = { \ + .parent = &platform_bus, \ + .name = "r8a7778-vin", \ + .id = idx, \ + .res = vin##idx##_resources, \ + .num_res = ARRAY_SIZE(vin##idx##_resources), \ + .dma_mask = DMA_BIT_MASK(32), \ +} + +R8A7778_VIN(0); +R8A7778_VIN(1); + +static struct platform_device_info *vin_info_table[] __initdata = { + &vin0_info, + &vin1_info, +}; + +void __init r8a7778_add_vin_device(int id, struct rcar_vin_platform_data *pdata) +{ + BUG_ON(id < 0 || id > 1); + + vin_info_table[id]->data = pdata; + vin_info_table[id]->size_data = sizeof(*pdata); + + platform_device_register_full(vin_info_table[id]); +} + void __init r8a7778_add_standard_devices(void) { int i; -- cgit v0.10.2 From 9c43952d0f1e7da943cb697f902e5a0e62abfc63 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 22 Aug 2013 17:38:50 -0300 Subject: [media] ARM: shmobile: BOCK-W: add VIN and ML86V7667 support Add ML86V7667 platform devices on BOCK-W board, configure VIN0/1 pins, and register VIN0/1 devices with the ML86V7667 specific platform data. [Sergei: some macro/comment cleanup; updated the copyrights, removed duplicate 'sh_eth' driver being enabled before registering VIN1 due to a pin conflict, removed superfluous semicolon after iclink[01]_ml86v7667' initializer.] Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Acked-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/mach-shmobile/board-bockw.c b/arch/arm/mach-shmobile/board-bockw.c index d555464..7d44f66 100644 --- a/arch/arm/mach-shmobile/board-bockw.c +++ b/arch/arm/mach-shmobile/board-bockw.c @@ -3,6 +3,7 @@ * * Copyright (C) 2013 Renesas Solutions Corp. * Copyright (C) 2013 Kuninori Morimoto + * Copyright (C) 2013 Cogent Embedded, Inc. * * 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 @@ -28,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -143,6 +145,25 @@ static struct sh_mmcif_plat_data sh_mmcif_plat = { MMC_CAP_NEEDS_POLL, }; +static struct rcar_vin_platform_data vin_platform_data __initdata = { + .flags = RCAR_VIN_BT656, +}; + +/* In the default configuration both decoders reside on I2C bus 0 */ +#define BOCKW_CAMERA(idx) \ +static struct i2c_board_info camera##idx##_info = { \ + I2C_BOARD_INFO("ml86v7667", 0x41 + 2 * (idx)), \ +}; \ + \ +static struct soc_camera_link iclink##idx##_ml86v7667 __initdata = { \ + .bus_id = idx, \ + .i2c_adapter_id = 0, \ + .board_info = &camera##idx##_info, \ +} + +BOCKW_CAMERA(0); +BOCKW_CAMERA(1); + static const struct pinctrl_map bockw_pinctrl_map[] = { /* Ether */ PIN_MAP_MUX_GROUP_DEFAULT("r8a777x-ether", "pfc-r8a7778", @@ -168,6 +189,16 @@ static const struct pinctrl_map bockw_pinctrl_map[] = { /* SDHI0 */ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7778", "sdhi0", "sdhi0"), + /* VIN0 */ + PIN_MAP_MUX_GROUP_DEFAULT("r8a7778-vin.0", "pfc-r8a7778", + "vin0_clk", "vin0"), + PIN_MAP_MUX_GROUP_DEFAULT("r8a7778-vin.0", "pfc-r8a7778", + "vin0_data8", "vin0"), + /* VIN1 */ + PIN_MAP_MUX_GROUP_DEFAULT("r8a7778-vin.1", "pfc-r8a7778", + "vin1_clk", "vin1"), + PIN_MAP_MUX_GROUP_DEFAULT("r8a7778-vin.1", "pfc-r8a7778", + "vin1_data8", "vin1"), }; #define FPGA 0x18200000 @@ -186,6 +217,16 @@ static void __init bockw_init(void) r8a7778_add_i2c_device(0); r8a7778_add_hspi_device(0); r8a7778_add_mmc_device(&sh_mmcif_plat); + r8a7778_add_vin_device(0, &vin_platform_data); + /* VIN1 has a pin conflict with Ether */ + if (!IS_ENABLED(CONFIG_SH_ETH)) + r8a7778_add_vin_device(1, &vin_platform_data); + platform_device_register_data(&platform_bus, "soc-camera-pdrv", 0, + &iclink0_ml86v7667, + sizeof(iclink0_ml86v7667)); + platform_device_register_data(&platform_bus, "soc-camera-pdrv", 1, + &iclink1_ml86v7667, + sizeof(iclink1_ml86v7667)); i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices)); -- cgit v0.10.2 From a3fbba0e39de102bcc65bcc66abeb73d3cf7f633 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 22 Aug 2013 17:40:36 -0300 Subject: [media] ARM: shmobile: BOCK-W: enable VIN and ML86V7667 in defconfig Add the VIN and ML86V7667 drivers to 'bockw_defconfig'. Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Acked-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/configs/bockw_defconfig b/arch/arm/configs/bockw_defconfig index 845f5cd..e7e9494 100644 --- a/arch/arm/configs/bockw_defconfig +++ b/arch/arm/configs/bockw_defconfig @@ -82,6 +82,13 @@ CONFIG_SERIAL_SH_SCI_CONSOLE=y # CONFIG_HWMON is not set CONFIG_I2C=y CONFIG_I2C_RCAR=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +CONFIG_VIDEO_RCAR_VIN=y +# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set +CONFIG_VIDEO_ML86V7667=y CONFIG_SPI=y CONFIG_SPI_SH_HSPI=y CONFIG_USB=y -- cgit v0.10.2 From c21412f5d3717abcc0b1e7ad31f3740415114144 Mon Sep 17 00:00:00 2001 From: Luis Alves Date: Thu, 11 Jul 2013 12:02:44 -0300 Subject: [media] Fixed misleading error when handling IR interrupts Hi, Handling the AV Core/IR interrupts schedules its workqueue but the schedule_work function returns false if @work was already on the kernel-global workqueue and true otherwise. Printing an error message if @work wasn't in the queue is wrong. Regards, Luis Signed-off-by: Luis Alves Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 268654a..9f63d93 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -1941,10 +1941,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) { cx23885_irq_disable(dev, PCI_MSK_AV_CORE); - if (!schedule_work(&dev->cx25840_work)) - printk(KERN_ERR "%s: failed to set up deferred work for" - " AV Core/IR interrupt. Interrupt is disabled" - " and won't be re-enabled\n", dev->name); + schedule_work(&dev->cx25840_work); handled++; } -- cgit v0.10.2 From 6fa1f40905333e10b47fa22609ce5c086eca9b11 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 24 Aug 2013 05:54:56 -0300 Subject: [media] Fix build errors on usbtv when driver is builtin As reported by Fengguang Wu drivers/built-in.o: In function `vb2_ioctl_streamon': >> (.text+0x8d354): undefined reference to `video_devdata' drivers/built-in.o: In function `vb2_ioctl_streamoff': >> (.text+0x8d397): undefined reference to `video_devdata' drivers/built-in.o: In function `vb2_ioctl_expbuf': ... That happens when: CONFIG_VIDEO_DEV=y CONFIG_VIDEO_V4L2=m CONFIG_VIDEO_USBTV=y As the core is module, usbtv should also be compiled as module. Reported-by: Fengguang Wu Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/usb/usbtv/Kconfig b/drivers/media/usb/usbtv/Kconfig index 8864436..7c5b860 100644 --- a/drivers/media/usb/usbtv/Kconfig +++ b/drivers/media/usb/usbtv/Kconfig @@ -1,6 +1,6 @@ config VIDEO_USBTV tristate "USBTV007 video capture support" - depends on VIDEO_DEV + depends on VIDEO_V4L2 select VIDEOBUF2_VMALLOC ---help--- -- cgit v0.10.2 From 6c84b214284e2c28314fe56b2b353b07ddfe9742 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 24 Aug 2013 06:54:59 -0300 Subject: [media] sms: fix randconfig building error As reported by Jim Davis , building with: CONFIG_USB=m CONFIG_SMS_USB_DRV=m CONFIG_SMS_SDIO_DRV=y CONFIG_SMS_SIANO_MDTV=y CONFIG_SMS_SIANO_DEBUGFS=y causes a build error: drivers/built-in.o: In function `smsdvb_debugfs_register': /home/jim/linux/drivers/media/common/siano/smsdvb-debugfs.c:537: undefined reference to `usb_debug_root' make: *** [vmlinux] Error 1 That happens because the siano-mdtv is builtin, while USB is a module. As it makes not much sense to have sms-usb compiled as 'm' and sms-sdio compiled as 'y' (or vice-versa), only allow enabling debugfs if both are either 'y' or 'm'. Reported-by: Jim Davis Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/siano/Kconfig b/drivers/media/common/siano/Kconfig index f3f5ec4..f953d33 100644 --- a/drivers/media/common/siano/Kconfig +++ b/drivers/media/common/siano/Kconfig @@ -23,6 +23,8 @@ config SMS_SIANO_DEBUGFS depends on SMS_SIANO_MDTV depends on DEBUG_FS depends on SMS_USB_DRV + depends on CONFIG_SMS_USB_DRV = CONFIG_SMS_SDIO_DRV + ---help--- Choose Y to enable visualizing a dump of the frontend statistics response packets via debugfs. Currently, works -- cgit v0.10.2 From 1ae0d202a56a18edd468d6fff6f060bfc30df222 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 24 Aug 2013 07:52:25 -0300 Subject: [media] cx88: fix build when VP3054=m and CX88_DVB=y As reported by Jim Davis : randconfig build error with next-20130813, in drivers/media/pci/cx88, when: CONFIG_VIDEO_CX88=y CONFIG_VIDEO_CX88_BLACKBIRD=m CONFIG_VIDEO_CX88_DVB=y CONFIG_VIDEO_CX88_VP3054=m CONFIG_VIDEO_CX88_MPEG=y LD init/built-in.o drivers/built-in.o: In function `cx8802_dvb_remove': cx88-dvb.c:(.text+0x3a9914): undefined reference to `vp3054_i2c_remove' drivers/built-in.o: In function `cx8802_dvb_probe': cx88-dvb.c:(.text+0x3a9c4b): undefined reference to `vp3054_i2c_probe' make: *** [vmlinux] Error 1 That happens because the vp3054 symbols aren't available builtin. So, make it builtin, if CX88_DVB=y, or module otherwise, if this support is selected. Reported-by: Jim Davis Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/pci/cx88/Kconfig b/drivers/media/pci/cx88/Kconfig index bb05eca..a63a9ad 100644 --- a/drivers/media/pci/cx88/Kconfig +++ b/drivers/media/pci/cx88/Kconfig @@ -72,9 +72,9 @@ config VIDEO_CX88_DVB To compile this driver as a module, choose M here: the module will be called cx88-dvb. -config VIDEO_CX88_VP3054 - tristate "VP-3054 Secondary I2C Bus Support" - default m +config VIDEO_CX88_ENABLE_VP3054 + bool "VP-3054 Secondary I2C Bus Support" + default y depends on VIDEO_CX88_DVB && DVB_MT352 ---help--- This adds DVB-T support for cards based on the @@ -82,6 +82,11 @@ config VIDEO_CX88_VP3054 which also require support for the VP-3054 Secondary I2C bus, such at DNTV Live! DVB-T Pro. +config VIDEO_CX88_VP3054 + tristate + depends on VIDEO_CX88_DVB && VIDEO_CX88_ENABLE_VP3054 + default y + config VIDEO_CX88_MPEG tristate depends on VIDEO_CX88_DVB || VIDEO_CX88_BLACKBIRD -- cgit v0.10.2 From e9a7495c5b745683a0f20bea9bc0a5c135f0260c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 24 Aug 2013 08:15:03 -0300 Subject: [media] sound/pci/Kconfig: select RADIO_ADAPTERS if needed As reported by kbuild test robot : warning: (SND_ES1968_RADIO && SND_FM801_TEA575X_BOOL) selects RADIO_TEA575X which has unmet direct dependencies (MEDIA_SUPPORT && RADIO_ADAPTERS && VIDEO_V4L2) That happens because a radio driver is selected, without selecting the RADIO_ADAPTERS menu. Reported-by: kbuild test robot Cc: Takashi Iwai Signed-off-by: Mauro Carvalho Chehab diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 9df80ef..46ed9e8 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -539,7 +539,9 @@ config SND_ES1968_RADIO depends on SND_ES1968 depends on MEDIA_RADIO_SUPPORT depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_ES1968 + select RADIO_ADAPTERS select RADIO_TEA575X + help Say Y here to include support for TEA5757 radio tuner integrated on some MediaForte cards (e.g. SF64-PCE2). @@ -561,6 +563,7 @@ config SND_FM801_TEA575X_BOOL depends on SND_FM801 depends on MEDIA_RADIO_SUPPORT depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801 + select RADIO_ADAPTERS select RADIO_TEA575X help Say Y here to include support for soundcards based on the ForteMedia -- cgit v0.10.2 From cbec6d3ab470565536480d3bd109a7fdb128c3c4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 24 Aug 2013 08:47:51 -0300 Subject: [media] vsp1: Fix a sparse warning As reported by: kbuild test robot : drivers/media/platform/vsp1/vsp1_drv.c:434:21: sparse: cast removes address space of expression 433 vsp1->mmio = devm_ioremap_resource(&pdev->dev, io); > 434 if (IS_ERR((void *)vsp1->mmio)) > 435 return PTR_ERR((void *)vsp1->mmio); There's no need to convert it to void *. Reported-by: kbuild test robot Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 8700842..ff8cd2d 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -434,8 +434,8 @@ static int vsp1_probe(struct platform_device *pdev) /* I/O, IRQ and clock resources */ io = platform_get_resource(pdev, IORESOURCE_MEM, 0); vsp1->mmio = devm_ioremap_resource(&pdev->dev, io); - if (IS_ERR((void *)vsp1->mmio)) - return PTR_ERR((void *)vsp1->mmio); + if (IS_ERR(vsp1->mmio)) + return PTR_ERR(vsp1->mmio); vsp1->clock = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(vsp1->clock)) { -- cgit v0.10.2 From 04e40bdd67afc5ab94d983417f18bb1f20b08eac Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 24 Aug 2013 13:35:14 -0300 Subject: [media] msi3101: Fix compilation on i386 as reported by: kbuild test robot : [linuxtv-media:master 459/499] sdr-msi3101.c:undefined reference to `__umoddi3' Reported-by: kbuild test robot Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index eebe1d0..24c7b70 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -1332,7 +1333,7 @@ static int msi3101_set_tuner(struct msi3101_state *s) int ret, i, len; unsigned int n, m, thresh, frac, vco_step, tmp, f_if1; u32 reg; - u64 f_vco; + u64 f_vco, tmp64; u8 mode, filter_mode, lo_div; const struct msi3101_gain *gain_lut; static const struct { @@ -1436,8 +1437,10 @@ static int msi3101_set_tuner(struct msi3101_state *s) #define F_OUT_STEP 1 #define R_REF 4 f_vco = (f_rf + f_if + f_if1) * lo_div; - n = f_vco / (F_REF * R_REF); - m = f_vco % (F_REF * R_REF); + + 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; -- cgit v0.10.2 From 4714a0255eee45b87634ab28f8a7e66038f645ba Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 22 Aug 2013 17:23:13 -0300 Subject: [media] ARM: shmobile: r8a7779: add VIN support Add VIN clocks and platform devices for R8A7779 SoC; add function to register the VIN platform devices. [Sergei: added 'id' parameter check to r8a7779_add_vin_device(), used '*pdata' in *sizeof* operator there, renamed some variables, annotated vin[0-3]_resources [] and 'vin[0-3]_info' as '__initdata'.] Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Acked-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/mach-shmobile/clock-r8a7779.c b/arch/arm/mach-shmobile/clock-r8a7779.c index 10340f5..bd6ad92 100644 --- a/arch/arm/mach-shmobile/clock-r8a7779.c +++ b/arch/arm/mach-shmobile/clock-r8a7779.c @@ -112,7 +112,9 @@ static struct clk *main_clks[] = { }; enum { MSTP323, MSTP322, MSTP321, MSTP320, + MSTP120, MSTP116, MSTP115, MSTP114, + MSTP110, MSTP109, MSTP108, MSTP103, MSTP101, MSTP100, MSTP030, MSTP029, MSTP028, MSTP027, MSTP026, MSTP025, MSTP024, MSTP023, MSTP022, MSTP021, @@ -125,9 +127,13 @@ static struct clk mstp_clks[MSTP_NR] = { [MSTP322] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 22, 0), /* SDHI1 */ [MSTP321] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 21, 0), /* SDHI2 */ [MSTP320] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 20, 0), /* SDHI3 */ + [MSTP120] = SH_CLK_MSTP32(&clks_clk, MSTPCR1, 20, 0), /* VIN3 */ [MSTP116] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 16, 0), /* PCIe */ [MSTP115] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 15, 0), /* SATA */ [MSTP114] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 14, 0), /* Ether */ + [MSTP110] = SH_CLK_MSTP32(&clks_clk, MSTPCR1, 10, 0), /* VIN0 */ + [MSTP109] = SH_CLK_MSTP32(&clks_clk, MSTPCR1, 9, 0), /* VIN1 */ + [MSTP108] = SH_CLK_MSTP32(&clks_clk, MSTPCR1, 8, 0), /* VIN2 */ [MSTP103] = SH_CLK_MSTP32(&clks_clk, MSTPCR1, 3, 0), /* DU */ [MSTP101] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 1, 0), /* USB2 */ [MSTP100] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 0, 0), /* USB0/1 */ @@ -162,10 +168,14 @@ static struct clk_lookup lookups[] = { CLKDEV_CON_ID("peripheral_clk", &clkp_clk), /* MSTP32 clocks */ + CLKDEV_DEV_ID("r8a7779-vin.3", &mstp_clks[MSTP120]), /* VIN3 */ CLKDEV_DEV_ID("rcar-pcie", &mstp_clks[MSTP116]), /* PCIe */ CLKDEV_DEV_ID("sata_rcar", &mstp_clks[MSTP115]), /* SATA */ CLKDEV_DEV_ID("fc600000.sata", &mstp_clks[MSTP115]), /* SATA w/DT */ CLKDEV_DEV_ID("r8a777x-ether", &mstp_clks[MSTP114]), /* Ether */ + CLKDEV_DEV_ID("r8a7779-vin.0", &mstp_clks[MSTP110]), /* VIN0 */ + CLKDEV_DEV_ID("r8a7779-vin.1", &mstp_clks[MSTP109]), /* VIN1 */ + CLKDEV_DEV_ID("r8a7779-vin.2", &mstp_clks[MSTP108]), /* VIN2 */ CLKDEV_DEV_ID("ehci-platform.1", &mstp_clks[MSTP101]), /* USB EHCI port2 */ CLKDEV_DEV_ID("ohci-platform.1", &mstp_clks[MSTP101]), /* USB OHCI port2 */ CLKDEV_DEV_ID("ehci-platform.0", &mstp_clks[MSTP100]), /* USB EHCI port0/1 */ diff --git a/arch/arm/mach-shmobile/include/mach/r8a7779.h b/arch/arm/mach-shmobile/include/mach/r8a7779.h index fc47073..6d2b641 100644 --- a/arch/arm/mach-shmobile/include/mach/r8a7779.h +++ b/arch/arm/mach-shmobile/include/mach/r8a7779.h @@ -5,6 +5,7 @@ #include #include #include +#include struct platform_device; @@ -35,6 +36,8 @@ extern void r8a7779_add_standard_devices(void); extern void r8a7779_add_standard_devices_dt(void); extern void r8a7779_add_ether_device(struct sh_eth_plat_data *pdata); extern void r8a7779_add_usb_phy_device(struct rcar_phy_platform_data *pdata); +extern void r8a7779_add_vin_device(int idx, + struct rcar_vin_platform_data *pdata); extern void r8a7779_init_late(void); extern void r8a7779_clock_init(void); extern void r8a7779_pinmux_init(void); diff --git a/arch/arm/mach-shmobile/setup-r8a7779.c b/arch/arm/mach-shmobile/setup-r8a7779.c index 3986877..3d89288 100644 --- a/arch/arm/mach-shmobile/setup-r8a7779.c +++ b/arch/arm/mach-shmobile/setup-r8a7779.c @@ -559,6 +559,33 @@ static struct resource ether_resources[] = { }, }; +#define R8A7779_VIN(idx) \ +static struct resource vin##idx##_resources[] __initdata = { \ + DEFINE_RES_MEM(0xffc50000 + 0x1000 * (idx), 0x1000), \ + DEFINE_RES_IRQ(gic_iid(0x5f + (idx))), \ +}; \ + \ +static struct platform_device_info vin##idx##_info __initdata = { \ + .parent = &platform_bus, \ + .name = "r8a7779-vin", \ + .id = idx, \ + .res = vin##idx##_resources, \ + .num_res = ARRAY_SIZE(vin##idx##_resources), \ + .dma_mask = DMA_BIT_MASK(32), \ +} + +R8A7779_VIN(0); +R8A7779_VIN(1); +R8A7779_VIN(2); +R8A7779_VIN(3); + +static struct platform_device_info *vin_info_table[] __initdata = { + &vin0_info, + &vin1_info, + &vin2_info, + &vin3_info, +}; + static struct platform_device *r8a7779_devices_dt[] __initdata = { &scif0_device, &scif1_device, @@ -610,6 +637,16 @@ void __init r8a7779_add_usb_phy_device(struct rcar_phy_platform_data *pdata) pdata, sizeof(*pdata)); } +void __init r8a7779_add_vin_device(int id, struct rcar_vin_platform_data *pdata) +{ + BUG_ON(id < 0 || id > 3); + + vin_info_table[id]->data = pdata; + vin_info_table[id]->size_data = sizeof(*pdata); + + platform_device_register_full(vin_info_table[id]); +} + /* do nothing for !CONFIG_SMP or !CONFIG_HAVE_TWD */ void __init __weak r8a7779_register_twd(void) { } -- cgit v0.10.2 From 7cef5e7fd1ab891bc59c269a1a55b8e8d8ca5bc8 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 22 Aug 2013 17:25:25 -0300 Subject: [media] ARM: shmobile: Marzen: add VIN and ADV7180 support Add ADV7180 platform devices on the Marzen board, configure VIN1/3 pins, and register VIN1/3 devices with the ADV7180 specific platform data. [Sergei: removed superfluous tabulation and inserted empty lines in the macro definition, updated the copyrights, annotated VIN platform data as '__initdata'] Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Acked-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/mach-shmobile/board-marzen.c b/arch/arm/mach-shmobile/board-marzen.c index a7d1010..ca7fb2e 100644 --- a/arch/arm/mach-shmobile/board-marzen.c +++ b/arch/arm/mach-shmobile/board-marzen.c @@ -1,8 +1,9 @@ /* * marzen board support * - * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011, 2013 Renesas Solutions Corp. * Copyright (C) 2011 Magnus Damm + * Copyright (C) 2013 Cogent Embedded, Inc. * * 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 @@ -37,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -178,12 +180,40 @@ static struct platform_device leds_device = { }, }; +static struct rcar_vin_platform_data vin_platform_data __initdata = { + .flags = RCAR_VIN_BT656, +}; + +#define MARZEN_CAMERA(idx) \ +static struct i2c_board_info camera##idx##_info = { \ + I2C_BOARD_INFO("adv7180", 0x20 + (idx)), \ +}; \ + \ +static struct soc_camera_link iclink##idx##_adv7180 = { \ + .bus_id = 1 + 2 * (idx), \ + .i2c_adapter_id = 0, \ + .board_info = &camera##idx##_info, \ +}; \ + \ +static struct platform_device camera##idx##_device = { \ + .name = "soc-camera-pdrv", \ + .id = idx, \ + .dev = { \ + .platform_data = &iclink##idx##_adv7180, \ + }, \ +}; + +MARZEN_CAMERA(0); +MARZEN_CAMERA(1); + static struct platform_device *marzen_devices[] __initdata = { ð_device, &sdhi0_device, &thermal_device, &hspi_device, &leds_device, + &camera0_device, + &camera1_device, }; static const struct pinctrl_map marzen_pinctrl_map[] = { @@ -219,6 +249,16 @@ static const struct pinctrl_map marzen_pinctrl_map[] = { /* USB2 */ PIN_MAP_MUX_GROUP_DEFAULT("ehci-platform.1", "pfc-r8a7779", "usb2", "usb2"), + /* VIN1 */ + PIN_MAP_MUX_GROUP_DEFAULT("r8a7779-vin.1", "pfc-r8a7779", + "vin1_clk", "vin1"), + PIN_MAP_MUX_GROUP_DEFAULT("r8a7779-vin.1", "pfc-r8a7779", + "vin1_data8", "vin1"), + /* VIN3 */ + PIN_MAP_MUX_GROUP_DEFAULT("r8a7779-vin.3", "pfc-r8a7779", + "vin3_clk", "vin3"), + PIN_MAP_MUX_GROUP_DEFAULT("r8a7779-vin.3", "pfc-r8a7779", + "vin3_data8", "vin3"), }; static void __init marzen_init(void) @@ -235,6 +275,8 @@ static void __init marzen_init(void) r8a7779_add_standard_devices(); r8a7779_add_usb_phy_device(&usb_phy_platform_data); + r8a7779_add_vin_device(1, &vin_platform_data); + r8a7779_add_vin_device(3, &vin_platform_data); platform_add_devices(marzen_devices, ARRAY_SIZE(marzen_devices)); } -- cgit v0.10.2 From e0c332c671e71941e0bd4a339972ee4af15df676 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 22 Aug 2013 17:27:08 -0300 Subject: [media] ARM: shmobile: Marzen: enable VIN and ADV7180 in defconfig Add the VIN and ADV7180 drivers to 'marzen_defconfig'. Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Acked-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab diff --git a/arch/arm/configs/marzen_defconfig b/arch/arm/configs/marzen_defconfig index 494e70a..c50e52b 100644 --- a/arch/arm/configs/marzen_defconfig +++ b/arch/arm/configs/marzen_defconfig @@ -84,6 +84,13 @@ CONFIG_GPIO_RCAR=y CONFIG_THERMAL=y CONFIG_RCAR_THERMAL=y CONFIG_SSB=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +CONFIG_VIDEO_RCAR_VIN=y +# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set +CONFIG_VIDEO_ADV7180=y CONFIG_USB=y CONFIG_USB_RCAR_PHY=y CONFIG_MMC=y -- cgit v0.10.2 From a89bcd4c6c2023615a89001b5a11b0bb77eb9491 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 22 Aug 2013 06:14:22 -0300 Subject: [media] adv7842: add new video decoder driver This is a Analog Devices Component/Graphics/SD Digitizer with 2:1 Multiplexed HDMI Receiver. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index b2cd8ca..847b711 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -206,6 +206,18 @@ config VIDEO_ADV7604 To compile this driver as a module, choose M here: the module will be called adv7604. +config VIDEO_ADV7842 + tristate "Analog Devices ADV7842 decoder" + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + ---help--- + Support for the Analog Devices ADV7842 video decoder. + + This is a Analog Devices Component/Graphics/SD Digitizer + with 2:1 Multiplexed HDMI Receiver. + + To compile this driver as a module, choose M here: the + module will be called adv7842. + config VIDEO_BT819 tristate "BT819A VideoStream decoder" depends on VIDEO_V4L2 && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index dc20653..b4cf972 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o +obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_VS6624) += vs6624.o diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c new file mode 100644 index 0000000..d174890 --- /dev/null +++ b/drivers/media/i2c/adv7842.c @@ -0,0 +1,2946 @@ +/* + * adv7842 - Analog Devices ADV7842 video decoder driver + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * 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. + * + */ + +/* + * References (c = chapter, p = page): + * REF_01 - Analog devices, ADV7842, Register Settings Recommendations, + * Revision 2.5, June 2010 + * REF_02 - Analog devices, Register map documentation, Documentation of + * the register maps, Software manual, Rev. F, June 2010 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-2)"); + +MODULE_DESCRIPTION("Analog Devices ADV7842 video decoder driver"); +MODULE_AUTHOR("Hans Verkuil "); +MODULE_AUTHOR("Martin Bugge "); +MODULE_LICENSE("GPL"); + +/* ADV7842 system clock frequency */ +#define ADV7842_fsc (28636360) + +/* +********************************************************************** +* +* Arrays with configuration parameters for the ADV7842 +* +********************************************************************** +*/ + +struct adv7842_state { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler hdl; + enum adv7842_mode mode; + struct v4l2_dv_timings timings; + enum adv7842_vid_std_select vid_std_select; + v4l2_std_id norm; + struct { + u8 edid[256]; + u32 present; + } hdmi_edid; + struct { + u8 edid[256]; + u32 present; + } vga_edid; + struct v4l2_fract aspect_ratio; + u32 rgb_quantization_range; + bool is_cea_format; + struct workqueue_struct *work_queues; + struct delayed_work delayed_work_enable_hotplug; + bool connector_hdmi; + bool hdmi_port_a; + + /* i2c clients */ + struct i2c_client *i2c_sdp_io; + struct i2c_client *i2c_sdp; + struct i2c_client *i2c_cp; + struct i2c_client *i2c_vdp; + struct i2c_client *i2c_afe; + struct i2c_client *i2c_hdmi; + struct i2c_client *i2c_repeater; + struct i2c_client *i2c_edid; + struct i2c_client *i2c_infoframe; + struct i2c_client *i2c_cec; + struct i2c_client *i2c_avlink; + + /* controls */ + struct v4l2_ctrl *detect_tx_5v_ctrl; + struct v4l2_ctrl *analog_sampling_phase_ctrl; + struct v4l2_ctrl *free_run_color_ctrl_manual; + struct v4l2_ctrl *free_run_color_ctrl; + struct v4l2_ctrl *rgb_quantization_range_ctrl; +}; + +/* Unsupported timings. This device cannot support 720p30. */ +static const struct v4l2_dv_timings adv7842_timings_exceptions[] = { + V4L2_DV_BT_CEA_1280X720P30, + { } +}; + +static bool adv7842_check_dv_timings(const struct v4l2_dv_timings *t, void *hdl) +{ + int i; + + for (i = 0; adv7842_timings_exceptions[i].bt.width; i++) + if (v4l2_match_dv_timings(t, adv7842_timings_exceptions + i, 0)) + return false; + return true; +} + +struct adv7842_video_standards { + struct v4l2_dv_timings timings; + u8 vid_std; + u8 v_freq; +}; + +/* sorted by number of lines */ +static const struct adv7842_video_standards adv7842_prim_mode_comp[] = { + /* { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, TODO flickering */ + { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, + { V4L2_DV_BT_CEA_1280X720P50, 0x19, 0x01 }, + { V4L2_DV_BT_CEA_1280X720P60, 0x19, 0x00 }, + { V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 }, + { V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 }, + { V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 }, + { V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 }, + { V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 }, + /* TODO add 1920x1080P60_RB (CVT timing) */ + { }, +}; + +/* sorted by number of lines */ +static const struct adv7842_video_standards adv7842_prim_mode_gr[] = { + { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, + { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, + { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, + { V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 }, + { V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 }, + { V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 }, + { V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 }, + { V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 }, + { V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 }, + { V4L2_DV_BT_DMT_1360X768P60, 0x12, 0x00 }, + { V4L2_DV_BT_DMT_1366X768P60, 0x13, 0x00 }, + { V4L2_DV_BT_DMT_1400X1050P60, 0x14, 0x00 }, + { V4L2_DV_BT_DMT_1400X1050P75, 0x15, 0x00 }, + { V4L2_DV_BT_DMT_1600X1200P60, 0x16, 0x00 }, /* TODO not tested */ + /* TODO add 1600X1200P60_RB (not a DMT timing) */ + { V4L2_DV_BT_DMT_1680X1050P60, 0x18, 0x00 }, + { V4L2_DV_BT_DMT_1920X1200P60_RB, 0x19, 0x00 }, /* TODO not tested */ + { }, +}; + +/* sorted by number of lines */ +static const struct adv7842_video_standards adv7842_prim_mode_hdmi_comp[] = { + { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, + { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, + { V4L2_DV_BT_CEA_1280X720P50, 0x13, 0x01 }, + { V4L2_DV_BT_CEA_1280X720P60, 0x13, 0x00 }, + { V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 }, + { V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 }, + { V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 }, + { V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 }, + { V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 }, + { }, +}; + +/* sorted by number of lines */ +static const struct adv7842_video_standards adv7842_prim_mode_hdmi_gr[] = { + { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, + { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, + { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, + { V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 }, + { V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 }, + { V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 }, + { V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 }, + { V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 }, + { V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 }, + { }, +}; + +/* ----------------------------------------------------------------------- */ + +static inline struct adv7842_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7842_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7842_state, hdl)->sd; +} + +static inline unsigned hblanking(const struct v4l2_bt_timings *t) +{ + return V4L2_DV_BT_BLANKING_WIDTH(t); +} + +static inline unsigned htotal(const struct v4l2_bt_timings *t) +{ + return V4L2_DV_BT_FRAME_WIDTH(t); +} + +static inline unsigned vblanking(const struct v4l2_bt_timings *t) +{ + return V4L2_DV_BT_BLANKING_HEIGHT(t); +} + +static inline unsigned vtotal(const struct v4l2_bt_timings *t) +{ + return V4L2_DV_BT_FRAME_HEIGHT(t); +} + + +/* ----------------------------------------------------------------------- */ + +static s32 adv_smbus_read_byte_data_check(struct i2c_client *client, + u8 command, bool check) +{ + union i2c_smbus_data data; + + if (!i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data)) + return data.byte; + if (check) + v4l_err(client, "error reading %02x, %02x\n", + client->addr, command); + return -EIO; +} + +static s32 adv_smbus_read_byte_data(struct i2c_client *client, u8 command) +{ + int i; + + for (i = 0; i < 3; i++) { + int ret = adv_smbus_read_byte_data_check(client, command, true); + + if (ret >= 0) { + if (i) + v4l_err(client, "read ok after %d retries\n", i); + return ret; + } + } + v4l_err(client, "read failed\n"); + return -EIO; +} + +static s32 adv_smbus_write_byte_data(struct i2c_client *client, + u8 command, u8 value) +{ + union i2c_smbus_data data; + int err; + int i; + + data.byte = value; + for (i = 0; i < 3; i++) { + err = i2c_smbus_xfer(client->adapter, client->addr, + client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data); + if (!err) + break; + } + if (err < 0) + v4l_err(client, "error writing %02x, %02x, %02x\n", + client->addr, command, value); + return err; +} + +static void adv_smbus_write_byte_no_check(struct i2c_client *client, + u8 command, u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + + i2c_smbus_xfer(client->adapter, client->addr, + client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data); +} + +static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client, + u8 command, unsigned length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(data.block + 1, values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); +} + +/* ----------------------------------------------------------------------- */ + +static inline int io_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return adv_smbus_read_byte_data(client, reg); +} + +static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return adv_smbus_write_byte_data(client, reg, val); +} + +static inline int io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return io_write(sd, reg, (io_read(sd, reg) & mask) | val); +} + +static inline int avlink_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_avlink, reg); +} + +static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_avlink, reg, val); +} + +static inline int cec_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_cec, reg); +} + +static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_cec, reg, val); +} + +static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val); +} + +static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_infoframe, reg); +} + +static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_infoframe, reg, val); +} + +static inline int sdp_io_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_sdp_io, reg); +} + +static inline int sdp_io_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_sdp_io, reg, val); +} + +static inline int sdp_io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return sdp_io_write(sd, reg, (sdp_io_read(sd, reg) & mask) | val); +} + +static inline int sdp_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_sdp, reg); +} + +static inline int sdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_sdp, reg, val); +} + +static inline int sdp_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return sdp_write(sd, reg, (sdp_read(sd, reg) & mask) | val); +} + +static inline int afe_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_afe, reg); +} + +static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_afe, reg, val); +} + +static inline int afe_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return afe_write(sd, reg, (afe_read(sd, reg) & mask) | val); +} + +static inline int rep_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_repeater, reg); +} + +static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_repeater, reg, val); +} + +static inline int rep_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return rep_write(sd, reg, (rep_read(sd, reg) & mask) | val); +} + +static inline int edid_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_edid, reg); +} + +static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_edid, reg, val); +} + +static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_hdmi, reg); +} + +static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val); +} + +static inline int cp_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_cp, reg); +} + +static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_cp, reg, val); +} + +static inline int cp_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return cp_write(sd, reg, (cp_read(sd, reg) & mask) | val); +} + +static inline int vdp_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_vdp, reg); +} + +static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_vdp, reg, val); +} + +static void main_reset(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + adv_smbus_write_byte_no_check(client, 0xff, 0x80); + + mdelay(2); +} + +/* ----------------------------------------------------------------------- */ + +static inline bool is_digital_input(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + + return state->mode == ADV7842_MODE_HDMI; +} + +static const struct v4l2_dv_timings_cap adv7842_timings_cap_analog = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .max_width = 1920, + .max_height = 1200, + .min_pixelclock = 25000000, + .max_pixelclock = 170000000, + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM, + }, +}; + +static const struct v4l2_dv_timings_cap adv7842_timings_cap_digital = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .max_width = 1920, + .max_height = 1200, + .min_pixelclock = 25000000, + .max_pixelclock = 225000000, + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM, + }, +}; + +static inline const struct v4l2_dv_timings_cap * +adv7842_get_dv_timings_cap(struct v4l2_subdev *sd) +{ + return is_digital_input(sd) ? &adv7842_timings_cap_digital : + &adv7842_timings_cap_analog; +} + +/* ----------------------------------------------------------------------- */ + +static void adv7842_delayed_work_enable_hotplug(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct adv7842_state *state = container_of(dwork, + struct adv7842_state, delayed_work_enable_hotplug); + struct v4l2_subdev *sd = &state->sd; + int present = state->hdmi_edid.present; + u8 mask = 0; + + v4l2_dbg(2, debug, sd, "%s: enable hotplug on ports: 0x%x\n", + __func__, present); + + if (present & 0x1) + mask |= 0x20; /* port A */ + if (present & 0x2) + mask |= 0x10; /* port B */ + io_write_and_or(sd, 0x20, 0xcf, mask); +} + +static int edid_write_vga_segment(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7842_state *state = to_state(sd); + const u8 *val = state->vga_edid.edid; + int err = 0; + int i; + + v4l2_dbg(2, debug, sd, "%s: write EDID on VGA port\n", __func__); + + /* HPA disable on port A and B */ + io_write_and_or(sd, 0x20, 0xcf, 0x00); + + /* Disable I2C access to internal EDID ram from VGA DDC port */ + rep_write_and_or(sd, 0x7f, 0x7f, 0x00); + + /* edid segment pointer '1' for VGA port */ + rep_write_and_or(sd, 0x77, 0xef, 0x10); + + for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX) + err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, + I2C_SMBUS_BLOCK_MAX, val + i); + if (err) + return err; + + /* Calculates the checksums and enables I2C access + * to internal EDID ram from VGA DDC port. + */ + rep_write_and_or(sd, 0x7f, 0x7f, 0x80); + + for (i = 0; i < 1000; i++) { + if (rep_read(sd, 0x79) & 0x20) + break; + mdelay(1); + } + if (i == 1000) { + v4l_err(client, "error enabling edid on VGA port\n"); + return -EIO; + } + + /* enable hotplug after 200 ms */ + queue_delayed_work(state->work_queues, + &state->delayed_work_enable_hotplug, HZ / 5); + + return 0; +} + +static int edid_spa_location(const u8 *edid) +{ + u8 d; + + /* + * TODO, improve and update for other CEA extensions + * currently only for 1 segment (256 bytes), + * i.e. 1 extension block and CEA revision 3. + */ + if ((edid[0x7e] != 1) || + (edid[0x80] != 0x02) || + (edid[0x81] != 0x03)) { + return -EINVAL; + } + /* + * search Vendor Specific Data Block (tag 3) + */ + d = edid[0x82] & 0x7f; + if (d > 4) { + int i = 0x84; + int end = 0x80 + d; + do { + u8 tag = edid[i]>>5; + u8 len = edid[i] & 0x1f; + + if ((tag == 3) && (len >= 5)) + return i + 4; + i += len + 1; + } while (i < end); + } + return -EINVAL; +} + +static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7842_state *state = to_state(sd); + const u8 *val = state->hdmi_edid.edid; + u8 cur_mask = rep_read(sd, 0x77) & 0x0c; + u8 mask = port == 0 ? 0x4 : 0x8; + int spa_loc = edid_spa_location(val); + int err = 0; + int i; + + v4l2_dbg(2, debug, sd, "%s: write EDID on port %d (spa at 0x%x)\n", + __func__, port, spa_loc); + + /* HPA disable on port A and B */ + io_write_and_or(sd, 0x20, 0xcf, 0x00); + + /* Disable I2C access to internal EDID ram from HDMI DDC ports */ + rep_write_and_or(sd, 0x77, 0xf3, 0x00); + + /* edid segment pointer '0' for HDMI ports */ + rep_write_and_or(sd, 0x77, 0xef, 0x00); + + for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX) + err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, + I2C_SMBUS_BLOCK_MAX, val + i); + if (err) + return err; + + if (spa_loc > 0) { + if (port == 0) { + /* port A SPA */ + rep_write(sd, 0x72, val[spa_loc]); + rep_write(sd, 0x73, val[spa_loc + 1]); + } else { + /* port B SPA */ + rep_write(sd, 0x74, val[spa_loc]); + rep_write(sd, 0x75, val[spa_loc + 1]); + } + rep_write(sd, 0x76, spa_loc); + } else { + /* default register values for SPA */ + if (port == 0) { + /* port A SPA */ + rep_write(sd, 0x72, 0); + rep_write(sd, 0x73, 0); + } else { + /* port B SPA */ + rep_write(sd, 0x74, 0); + rep_write(sd, 0x75, 0); + } + rep_write(sd, 0x76, 0xc0); + } + rep_write_and_or(sd, 0x77, 0xbf, 0x00); + + /* Calculates the checksums and enables I2C access to internal + * EDID ram from HDMI DDC ports + */ + rep_write_and_or(sd, 0x77, 0xf3, mask | cur_mask); + + for (i = 0; i < 1000; i++) { + if (rep_read(sd, 0x7d) & mask) + break; + mdelay(1); + } + if (i == 1000) { + v4l_err(client, "error enabling edid on port %d\n", port); + return -EIO; + } + + /* enable hotplug after 200 ms */ + queue_delayed_work(state->work_queues, + &state->delayed_work_enable_hotplug, HZ / 5); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static void adv7842_inv_register(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "0x000-0x0ff: IO Map\n"); + v4l2_info(sd, "0x100-0x1ff: AVLink Map\n"); + v4l2_info(sd, "0x200-0x2ff: CEC Map\n"); + v4l2_info(sd, "0x300-0x3ff: InfoFrame Map\n"); + v4l2_info(sd, "0x400-0x4ff: SDP_IO Map\n"); + v4l2_info(sd, "0x500-0x5ff: SDP Map\n"); + v4l2_info(sd, "0x600-0x6ff: AFE Map\n"); + v4l2_info(sd, "0x700-0x7ff: Repeater Map\n"); + v4l2_info(sd, "0x800-0x8ff: EDID Map\n"); + v4l2_info(sd, "0x900-0x9ff: HDMI Map\n"); + v4l2_info(sd, "0xa00-0xaff: CP Map\n"); + v4l2_info(sd, "0xb00-0xbff: VDP Map\n"); +} + +static int adv7842_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + reg->size = 1; + switch (reg->reg >> 8) { + case 0: + reg->val = io_read(sd, reg->reg & 0xff); + break; + case 1: + reg->val = avlink_read(sd, reg->reg & 0xff); + break; + case 2: + reg->val = cec_read(sd, reg->reg & 0xff); + break; + case 3: + reg->val = infoframe_read(sd, reg->reg & 0xff); + break; + case 4: + reg->val = sdp_io_read(sd, reg->reg & 0xff); + break; + case 5: + reg->val = sdp_read(sd, reg->reg & 0xff); + break; + case 6: + reg->val = afe_read(sd, reg->reg & 0xff); + break; + case 7: + reg->val = rep_read(sd, reg->reg & 0xff); + break; + case 8: + reg->val = edid_read(sd, reg->reg & 0xff); + break; + case 9: + reg->val = hdmi_read(sd, reg->reg & 0xff); + break; + case 0xa: + reg->val = cp_read(sd, reg->reg & 0xff); + break; + case 0xb: + reg->val = vdp_read(sd, reg->reg & 0xff); + break; + default: + v4l2_info(sd, "Register %03llx not supported\n", reg->reg); + adv7842_inv_register(sd); + break; + } + return 0; +} + +static int adv7842_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + u8 val = reg->val & 0xff; + + switch (reg->reg >> 8) { + case 0: + io_write(sd, reg->reg & 0xff, val); + break; + case 1: + avlink_write(sd, reg->reg & 0xff, val); + break; + case 2: + cec_write(sd, reg->reg & 0xff, val); + break; + case 3: + infoframe_write(sd, reg->reg & 0xff, val); + break; + case 4: + sdp_io_write(sd, reg->reg & 0xff, val); + break; + case 5: + sdp_write(sd, reg->reg & 0xff, val); + break; + case 6: + afe_write(sd, reg->reg & 0xff, val); + break; + case 7: + rep_write(sd, reg->reg & 0xff, val); + break; + case 8: + edid_write(sd, reg->reg & 0xff, val); + break; + case 9: + hdmi_write(sd, reg->reg & 0xff, val); + break; + case 0xa: + cp_write(sd, reg->reg & 0xff, val); + break; + case 0xb: + vdp_write(sd, reg->reg & 0xff, val); + break; + default: + v4l2_info(sd, "Register %03llx not supported\n", reg->reg); + adv7842_inv_register(sd); + break; + } + return 0; +} +#endif + +static int adv7842_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + int prev = v4l2_ctrl_g_ctrl(state->detect_tx_5v_ctrl); + u8 reg_io_6f = io_read(sd, 0x6f); + int val = 0; + + if (reg_io_6f & 0x02) + val |= 1; /* port A */ + if (reg_io_6f & 0x01) + val |= 2; /* port B */ + + v4l2_dbg(1, debug, sd, "%s: 0x%x -> 0x%x\n", __func__, prev, val); + + if (val != prev) + return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, val); + return 0; +} + +static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, + u8 prim_mode, + const struct adv7842_video_standards *predef_vid_timings, + const struct v4l2_dv_timings *timings) +{ + int i; + + for (i = 0; predef_vid_timings[i].timings.bt.width; i++) { + if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings, + is_digital_input(sd) ? 250000 : 1000000)) + continue; + /* video std */ + io_write(sd, 0x00, predef_vid_timings[i].vid_std); + /* v_freq and prim mode */ + io_write(sd, 0x01, (predef_vid_timings[i].v_freq << 4) + prim_mode); + return 0; + } + + return -1; +} + +static int configure_predefined_video_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7842_state *state = to_state(sd); + int err; + + v4l2_dbg(1, debug, sd, "%s\n", __func__); + + /* reset to default values */ + io_write(sd, 0x16, 0x43); + io_write(sd, 0x17, 0x5a); + /* disable embedded syncs for auto graphics mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x00); + cp_write(sd, 0x26, 0x00); + cp_write(sd, 0x27, 0x00); + cp_write(sd, 0x28, 0x00); + cp_write(sd, 0x29, 0x00); + cp_write(sd, 0x8f, 0x00); + cp_write(sd, 0x90, 0x00); + cp_write(sd, 0xa5, 0x00); + cp_write(sd, 0xa6, 0x00); + cp_write(sd, 0xa7, 0x00); + cp_write(sd, 0xab, 0x00); + cp_write(sd, 0xac, 0x00); + + switch (state->mode) { + case ADV7842_MODE_COMP: + case ADV7842_MODE_RGB: + err = find_and_set_predefined_video_timings(sd, + 0x01, adv7842_prim_mode_comp, timings); + if (err) + err = find_and_set_predefined_video_timings(sd, + 0x02, adv7842_prim_mode_gr, timings); + break; + case ADV7842_MODE_HDMI: + err = find_and_set_predefined_video_timings(sd, + 0x05, adv7842_prim_mode_hdmi_comp, timings); + if (err) + err = find_and_set_predefined_video_timings(sd, + 0x06, adv7842_prim_mode_hdmi_gr, timings); + break; + default: + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); + err = -1; + break; + } + + + return err; +} + +static void configure_custom_video_timings(struct v4l2_subdev *sd, + const struct v4l2_bt_timings *bt) +{ + struct adv7842_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u32 width = htotal(bt); + u32 height = vtotal(bt); + u16 cp_start_sav = bt->hsync + bt->hbackporch - 4; + u16 cp_start_eav = width - bt->hfrontporch; + u16 cp_start_vbi = height - bt->vfrontporch + 1; + u16 cp_end_vbi = bt->vsync + bt->vbackporch + 1; + u16 ch1_fr_ll = (((u32)bt->pixelclock / 100) > 0) ? + ((width * (ADV7842_fsc / 100)) / ((u32)bt->pixelclock / 100)) : 0; + const u8 pll[2] = { + 0xc0 | ((width >> 8) & 0x1f), + width & 0xff + }; + + v4l2_dbg(2, debug, sd, "%s\n", __func__); + + switch (state->mode) { + case ADV7842_MODE_COMP: + case ADV7842_MODE_RGB: + /* auto graphics */ + io_write(sd, 0x00, 0x07); /* video std */ + io_write(sd, 0x01, 0x02); /* prim mode */ + /* enable embedded syncs for auto graphics mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x10); + + /* Should only be set in auto-graphics mode [REF_02, p. 91-92] */ + /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */ + /* IO-map reg. 0x16 and 0x17 should be written in sequence */ + if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll)) { + v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); + break; + } + + /* active video - horizontal timing */ + cp_write(sd, 0x26, (cp_start_sav >> 8) & 0xf); + cp_write(sd, 0x27, (cp_start_sav & 0xff)); + cp_write(sd, 0x28, (cp_start_eav >> 8) & 0xf); + cp_write(sd, 0x29, (cp_start_eav & 0xff)); + + /* active video - vertical timing */ + cp_write(sd, 0xa5, (cp_start_vbi >> 4) & 0xff); + cp_write(sd, 0xa6, ((cp_start_vbi & 0xf) << 4) | + ((cp_end_vbi >> 8) & 0xf)); + cp_write(sd, 0xa7, cp_end_vbi & 0xff); + break; + case ADV7842_MODE_HDMI: + /* set default prim_mode/vid_std for HDMI + accoring to [REF_03, c. 4.2] */ + io_write(sd, 0x00, 0x02); /* video std */ + io_write(sd, 0x01, 0x06); /* prim mode */ + break; + default: + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); + break; + } + + cp_write(sd, 0x8f, (ch1_fr_ll >> 8) & 0x7); + cp_write(sd, 0x90, ch1_fr_ll & 0xff); + cp_write(sd, 0xab, (height >> 4) & 0xff); + cp_write(sd, 0xac, (height & 0x0f) << 4); +} + +static void set_rgb_quantization_range(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + + switch (state->rgb_quantization_range) { + case V4L2_DV_RGB_RANGE_AUTO: + /* automatic */ + if (is_digital_input(sd) && !(hdmi_read(sd, 0x05) & 0x80)) { + /* receiving DVI-D signal */ + + /* ADV7842 selects RGB limited range regardless of + input format (CE/IT) in automatic mode */ + if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { + /* RGB limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x00); + + } else { + /* RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + } + } else { + /* receiving HDMI or analog signal, set automode */ + io_write_and_or(sd, 0x02, 0x0f, 0xf0); + } + break; + case V4L2_DV_RGB_RANGE_LIMITED: + /* RGB limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x00); + break; + case V4L2_DV_RGB_RANGE_FULL: + /* RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + break; + } +} + +static int adv7842_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct adv7842_state *state = to_state(sd); + + /* TODO SDP ctrls + contrast/brightness/hue/free run is acting a bit strange, + not sure if sdp csc is correct. + */ + switch (ctrl->id) { + /* standard ctrls */ + case V4L2_CID_BRIGHTNESS: + cp_write(sd, 0x3c, ctrl->val); + sdp_write(sd, 0x14, ctrl->val); + /* ignore lsb sdp 0x17[3:2] */ + return 0; + case V4L2_CID_CONTRAST: + cp_write(sd, 0x3a, ctrl->val); + sdp_write(sd, 0x13, ctrl->val); + /* ignore lsb sdp 0x17[1:0] */ + return 0; + case V4L2_CID_SATURATION: + cp_write(sd, 0x3b, ctrl->val); + sdp_write(sd, 0x15, ctrl->val); + /* ignore lsb sdp 0x17[5:4] */ + return 0; + case V4L2_CID_HUE: + cp_write(sd, 0x3d, ctrl->val); + sdp_write(sd, 0x16, ctrl->val); + /* ignore lsb sdp 0x17[7:6] */ + return 0; + /* custom ctrls */ + case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE: + afe_write(sd, 0xc8, ctrl->val); + return 0; + case V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL: + cp_write_and_or(sd, 0xbf, ~0x04, (ctrl->val << 2)); + sdp_write_and_or(sd, 0xdd, ~0x04, (ctrl->val << 2)); + return 0; + case V4L2_CID_ADV_RX_FREE_RUN_COLOR: { + u8 R = (ctrl->val & 0xff0000) >> 16; + u8 G = (ctrl->val & 0x00ff00) >> 8; + u8 B = (ctrl->val & 0x0000ff); + /* RGB -> YUV, numerical approximation */ + int Y = 66 * R + 129 * G + 25 * B; + int U = -38 * R - 74 * G + 112 * B; + int V = 112 * R - 94 * G - 18 * B; + + /* Scale down to 8 bits with rounding */ + Y = (Y + 128) >> 8; + U = (U + 128) >> 8; + V = (V + 128) >> 8; + /* make U,V positive */ + Y += 16; + U += 128; + V += 128; + + v4l2_dbg(1, debug, sd, "R %x, G %x, B %x\n", R, G, B); + v4l2_dbg(1, debug, sd, "Y %x, U %x, V %x\n", Y, U, V); + + /* CP */ + cp_write(sd, 0xc1, R); + cp_write(sd, 0xc0, G); + cp_write(sd, 0xc2, B); + /* SDP */ + sdp_write(sd, 0xde, Y); + sdp_write(sd, 0xdf, (V & 0xf0) | ((U >> 4) & 0x0f)); + return 0; + } + case V4L2_CID_DV_RX_RGB_RANGE: + state->rgb_quantization_range = ctrl->val; + set_rgb_quantization_range(sd); + return 0; + } + return -EINVAL; +} + +static inline bool no_power(struct v4l2_subdev *sd) +{ + return io_read(sd, 0x0c) & 0x24; +} + +static inline bool no_cp_signal(struct v4l2_subdev *sd) +{ + return ((cp_read(sd, 0xb5) & 0xd0) != 0xd0) || !(cp_read(sd, 0xb1) & 0x80); +} + +static inline bool is_hdmi(struct v4l2_subdev *sd) +{ + return hdmi_read(sd, 0x05) & 0x80; +} + +static int adv7842_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct adv7842_state *state = to_state(sd); + + *status = 0; + + if (io_read(sd, 0x0c) & 0x24) + *status |= V4L2_IN_ST_NO_POWER; + + if (state->mode == ADV7842_MODE_SDP) { + /* status from SDP block */ + if (!(sdp_read(sd, 0x5A) & 0x01)) + *status |= V4L2_IN_ST_NO_SIGNAL; + + v4l2_dbg(1, debug, sd, "%s: SDP status = 0x%x\n", + __func__, *status); + return 0; + } + /* status from CP block */ + if ((cp_read(sd, 0xb5) & 0xd0) != 0xd0 || + !(cp_read(sd, 0xb1) & 0x80)) + /* TODO channel 2 */ + *status |= V4L2_IN_ST_NO_SIGNAL; + + if (is_digital_input(sd) && ((io_read(sd, 0x74) & 0x03) != 0x03)) + *status |= V4L2_IN_ST_NO_SIGNAL; + + v4l2_dbg(1, debug, sd, "%s: CP status = 0x%x\n", + __func__, *status); + + return 0; +} + +struct stdi_readback { + u16 bl, lcf, lcvs; + u8 hs_pol, vs_pol; + bool interlaced; +}; + +static int stdi2dv_timings(struct v4l2_subdev *sd, + struct stdi_readback *stdi, + struct v4l2_dv_timings *timings) +{ + struct adv7842_state *state = to_state(sd); + u32 hfreq = (ADV7842_fsc * 8) / stdi->bl; + u32 pix_clk; + int i; + + for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { + const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; + + if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i], + adv7842_get_dv_timings_cap(sd), + adv7842_check_dv_timings, NULL)) + continue; + if (vtotal(bt) != stdi->lcf + 1) + continue; + if (bt->vsync != stdi->lcvs) + continue; + + pix_clk = hfreq * htotal(bt); + + if ((pix_clk < bt->pixelclock + 1000000) && + (pix_clk > bt->pixelclock - 1000000)) { + *timings = v4l2_dv_timings_presets[i]; + return 0; + } + } + + if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, + (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | + (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), + timings)) + return 0; + if (v4l2_detect_gtf(stdi->lcf + 1, hfreq, stdi->lcvs, + (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | + (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), + state->aspect_ratio, timings)) + return 0; + + v4l2_dbg(2, debug, sd, + "%s: No format candidate found for lcvs = %d, lcf=%d, bl = %d, %chsync, %cvsync\n", + __func__, stdi->lcvs, stdi->lcf, stdi->bl, + stdi->hs_pol, stdi->vs_pol); + return -1; +} + +static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) +{ + u32 status; + + adv7842_g_input_status(sd, &status); + if (status & V4L2_IN_ST_NO_SIGNAL) { + v4l2_dbg(2, debug, sd, "%s: no signal\n", __func__); + return -ENOLINK; + } + + stdi->bl = ((cp_read(sd, 0xb1) & 0x3f) << 8) | cp_read(sd, 0xb2); + stdi->lcf = ((cp_read(sd, 0xb3) & 0x7) << 8) | cp_read(sd, 0xb4); + stdi->lcvs = cp_read(sd, 0xb3) >> 3; + + if ((cp_read(sd, 0xb5) & 0x80) && ((cp_read(sd, 0xb5) & 0x03) == 0x01)) { + stdi->hs_pol = ((cp_read(sd, 0xb5) & 0x10) ? + ((cp_read(sd, 0xb5) & 0x08) ? '+' : '-') : 'x'); + stdi->vs_pol = ((cp_read(sd, 0xb5) & 0x40) ? + ((cp_read(sd, 0xb5) & 0x20) ? '+' : '-') : 'x'); + } else { + stdi->hs_pol = 'x'; + stdi->vs_pol = 'x'; + } + stdi->interlaced = (cp_read(sd, 0xb1) & 0x40) ? true : false; + + if (stdi->lcf < 239 || stdi->bl < 8 || stdi->bl == 0x3fff) { + v4l2_dbg(2, debug, sd, "%s: invalid signal\n", __func__); + return -ENOLINK; + } + + v4l2_dbg(2, debug, sd, + "%s: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %chsync, %cvsync, %s\n", + __func__, stdi->lcf, stdi->bl, stdi->lcvs, + stdi->hs_pol, stdi->vs_pol, + stdi->interlaced ? "interlaced" : "progressive"); + + return 0; +} + +static int adv7842_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + return v4l2_enum_dv_timings_cap(timings, + adv7842_get_dv_timings_cap(sd), adv7842_check_dv_timings, NULL); +} + +static int adv7842_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + *cap = *adv7842_get_dv_timings_cap(sd); + return 0; +} + +/* Fill the optional fields .standards and .flags in struct v4l2_dv_timings + if the format is listed in adv7604_timings[] */ +static void adv7842_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + v4l2_find_dv_timings_cap(timings, adv7842_get_dv_timings_cap(sd), + is_digital_input(sd) ? 250000 : 1000000, + adv7842_check_dv_timings, NULL); +} + +static int adv7842_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7842_state *state = to_state(sd); + struct v4l2_bt_timings *bt = &timings->bt; + struct stdi_readback stdi = { 0 }; + + /* SDP block */ + if (state->mode == ADV7842_MODE_SDP) + return -ENODATA; + + /* read STDI */ + if (read_stdi(sd, &stdi)) { + v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); + return -ENOLINK; + } + bt->interlaced = stdi.interlaced ? + V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; + bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | + ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); + bt->vsync = stdi.lcvs; + + if (is_digital_input(sd)) { + bool lock = hdmi_read(sd, 0x04) & 0x02; + bool interlaced = hdmi_read(sd, 0x0b) & 0x20; + unsigned w = (hdmi_read(sd, 0x07) & 0x1f) * 256 + hdmi_read(sd, 0x08); + unsigned h = (hdmi_read(sd, 0x09) & 0x1f) * 256 + hdmi_read(sd, 0x0a); + unsigned w_total = (hdmi_read(sd, 0x1e) & 0x3f) * 256 + + hdmi_read(sd, 0x1f); + unsigned h_total = ((hdmi_read(sd, 0x26) & 0x3f) * 256 + + hdmi_read(sd, 0x27)) / 2; + unsigned freq = (((hdmi_read(sd, 0x51) << 1) + + (hdmi_read(sd, 0x52) >> 7)) * 1000000) + + ((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128; + int i; + + if (is_hdmi(sd)) { + /* adjust for deep color mode */ + freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0)>>6) * 2 + 8); + } + + /* No lock? */ + if (!lock) { + v4l2_dbg(1, debug, sd, "%s: no lock on TMDS signal\n", __func__); + return -ENOLCK; + } + /* Interlaced? */ + if (interlaced) { + v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__); + return -ERANGE; + } + + for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { + const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; + + if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i], + adv7842_get_dv_timings_cap(sd), + adv7842_check_dv_timings, NULL)) + continue; + if (w_total != htotal(bt) || h_total != vtotal(bt)) + continue; + + if (w != bt->width || h != bt->height) + continue; + + if (abs(freq - bt->pixelclock) > 1000000) + continue; + *timings = v4l2_dv_timings_presets[i]; + return 0; + } + + timings->type = V4L2_DV_BT_656_1120; + + bt->width = w; + bt->height = h; + bt->interlaced = (hdmi_read(sd, 0x0b) & 0x20) ? + V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; + bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? + V4L2_DV_VSYNC_POS_POL : 0) | ((hdmi_read(sd, 0x05) & 0x20) ? + V4L2_DV_HSYNC_POS_POL : 0); + bt->pixelclock = (((hdmi_read(sd, 0x51) << 1) + + (hdmi_read(sd, 0x52) >> 7)) * 1000000) + + ((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128; + bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x1f) * 256 + + hdmi_read(sd, 0x21); + bt->hsync = (hdmi_read(sd, 0x22) & 0x1f) * 256 + + hdmi_read(sd, 0x23); + bt->hbackporch = (hdmi_read(sd, 0x24) & 0x1f) * 256 + + hdmi_read(sd, 0x25); + bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x3f) * 256 + + hdmi_read(sd, 0x2b)) / 2; + bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x3f) * 256 + + hdmi_read(sd, 0x2d)) / 2; + bt->vsync = ((hdmi_read(sd, 0x2e) & 0x3f) * 256 + + hdmi_read(sd, 0x2f)) / 2; + bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x3f) * 256 + + hdmi_read(sd, 0x31)) / 2; + bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x3f) * 256 + + hdmi_read(sd, 0x33)) / 2; + bt->il_vbackporch = ((hdmi_read(sd, 0x34) & 0x3f) * 256 + + hdmi_read(sd, 0x35)) / 2; + + bt->standards = 0; + bt->flags = 0; + } else { + /* Interlaced? */ + if (stdi.interlaced) { + v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__); + return -ERANGE; + } + + if (stdi2dv_timings(sd, &stdi, timings)) { + v4l2_dbg(1, debug, sd, "%s: format not supported\n", __func__); + return -ERANGE; + } + } + + if (debug > 1) + v4l2_print_dv_timings(sd->name, "adv7842_query_dv_timings: ", + timings, true); + return 0; +} + +static int adv7842_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7842_state *state = to_state(sd); + struct v4l2_bt_timings *bt; + int err; + + if (state->mode == ADV7842_MODE_SDP) + return -ENODATA; + + bt = &timings->bt; + + if (!v4l2_valid_dv_timings(timings, adv7842_get_dv_timings_cap(sd), + adv7842_check_dv_timings, NULL)) + return -ERANGE; + + adv7842_fill_optional_dv_timings_fields(sd, timings); + + state->timings = *timings; + + cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10); + + /* Use prim_mode and vid_std when available */ + err = configure_predefined_video_timings(sd, timings); + if (err) { + /* custom settings when the video format + does not have prim_mode/vid_std */ + configure_custom_video_timings(sd, bt); + } + + set_rgb_quantization_range(sd); + + + if (debug > 1) + v4l2_print_dv_timings(sd->name, "adv7842_s_dv_timings: ", + timings, true); + return 0; +} + +static int adv7842_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7842_state *state = to_state(sd); + + if (state->mode == ADV7842_MODE_SDP) + return -ENODATA; + *timings = state->timings; + return 0; +} + +static void enable_input(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + switch (state->mode) { + case ADV7842_MODE_SDP: + case ADV7842_MODE_COMP: + case ADV7842_MODE_RGB: + /* enable */ + io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ + break; + case ADV7842_MODE_HDMI: + /* enable */ + hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */ + hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */ + io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */ + break; + default: + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); + break; + } +} + +static void disable_input(struct v4l2_subdev *sd) +{ + /* disable */ + io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */ + hdmi_write(sd, 0x1a, 0x1a); /* Mute audio */ + hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */ +} + +static void sdp_csc_coeff(struct v4l2_subdev *sd, + const struct adv7842_sdp_csc_coeff *c) +{ + /* csc auto/manual */ + sdp_io_write_and_or(sd, 0xe0, 0xbf, c->manual ? 0x00 : 0x40); + + if (!c->manual) + return; + + /* csc scaling */ + sdp_io_write_and_or(sd, 0xe0, 0x7f, c->scaling == 2 ? 0x80 : 0x00); + + /* A coeff */ + sdp_io_write_and_or(sd, 0xe0, 0xe0, c->A1 >> 8); + sdp_io_write(sd, 0xe1, c->A1); + sdp_io_write_and_or(sd, 0xe2, 0xe0, c->A2 >> 8); + sdp_io_write(sd, 0xe3, c->A2); + sdp_io_write_and_or(sd, 0xe4, 0xe0, c->A3 >> 8); + sdp_io_write(sd, 0xe5, c->A3); + + /* A scale */ + sdp_io_write_and_or(sd, 0xe6, 0x80, c->A4 >> 8); + sdp_io_write(sd, 0xe7, c->A4); + + /* B coeff */ + sdp_io_write_and_or(sd, 0xe8, 0xe0, c->B1 >> 8); + sdp_io_write(sd, 0xe9, c->B1); + sdp_io_write_and_or(sd, 0xea, 0xe0, c->B2 >> 8); + sdp_io_write(sd, 0xeb, c->B2); + sdp_io_write_and_or(sd, 0xec, 0xe0, c->B3 >> 8); + sdp_io_write(sd, 0xed, c->B3); + + /* B scale */ + sdp_io_write_and_or(sd, 0xee, 0x80, c->B4 >> 8); + sdp_io_write(sd, 0xef, c->B4); + + /* C coeff */ + sdp_io_write_and_or(sd, 0xf0, 0xe0, c->C1 >> 8); + sdp_io_write(sd, 0xf1, c->C1); + sdp_io_write_and_or(sd, 0xf2, 0xe0, c->C2 >> 8); + sdp_io_write(sd, 0xf3, c->C2); + sdp_io_write_and_or(sd, 0xf4, 0xe0, c->C3 >> 8); + sdp_io_write(sd, 0xf5, c->C3); + + /* C scale */ + sdp_io_write_and_or(sd, 0xf6, 0x80, c->C4 >> 8); + sdp_io_write(sd, 0xf7, c->C4); +} + +static void select_input(struct v4l2_subdev *sd, + enum adv7842_vid_std_select vid_std_select) +{ + struct adv7842_state *state = to_state(sd); + + switch (state->mode) { + case ADV7842_MODE_SDP: + io_write(sd, 0x00, vid_std_select); /* video std: CVBS or YC mode */ + io_write(sd, 0x01, 0); /* prim mode */ + /* enable embedded syncs for auto graphics mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x10); + + afe_write(sd, 0x00, 0x00); /* power up ADC */ + afe_write(sd, 0xc8, 0x00); /* phase control */ + + io_write(sd, 0x19, 0x83); /* LLC DLL phase */ + io_write(sd, 0x33, 0x40); /* LLC DLL enable */ + + io_write(sd, 0xdd, 0x90); /* Manual 2x output clock */ + /* script says register 0xde, which don't exist in manual */ + + /* Manual analog input muxing mode, CVBS (6.4)*/ + afe_write_and_or(sd, 0x02, 0x7f, 0x80); + if (vid_std_select == ADV7842_SDP_VID_STD_CVBS_SD_4x1) { + afe_write(sd, 0x03, 0xa0); /* ADC0 to AIN10 (CVBS), ADC1 N/C*/ + afe_write(sd, 0x04, 0x00); /* ADC2 N/C,ADC3 N/C*/ + } else { + afe_write(sd, 0x03, 0xa0); /* ADC0 to AIN10 (CVBS), ADC1 N/C*/ + afe_write(sd, 0x04, 0xc0); /* ADC2 to AIN12, ADC3 N/C*/ + } + afe_write(sd, 0x0c, 0x1f); /* ADI recommend write */ + afe_write(sd, 0x12, 0x63); /* ADI recommend write */ + + sdp_io_write(sd, 0xb2, 0x60); /* Disable AV codes */ + sdp_io_write(sd, 0xc8, 0xe3); /* Disable Ancillary data */ + + /* SDP recommended settings */ + sdp_write(sd, 0x00, 0x3F); /* Autodetect PAL NTSC (not SECAM) */ + sdp_write(sd, 0x01, 0x00); /* Pedestal Off */ + + sdp_write(sd, 0x03, 0xE4); /* Manual VCR Gain Luma 0x40B */ + sdp_write(sd, 0x04, 0x0B); /* Manual Luma setting */ + sdp_write(sd, 0x05, 0xC3); /* Manual Chroma setting 0x3FE */ + sdp_write(sd, 0x06, 0xFE); /* Manual Chroma setting */ + sdp_write(sd, 0x12, 0x0D); /* Frame TBC,I_P, 3D comb enabled */ + sdp_write(sd, 0xA7, 0x00); /* ADI Recommended Write */ + sdp_io_write(sd, 0xB0, 0x00); /* Disable H and v blanking */ + + /* deinterlacer enabled and 3D comb */ + sdp_write_and_or(sd, 0x12, 0xf6, 0x09); + + sdp_write(sd, 0xdd, 0x08); /* free run auto */ + + break; + + case ADV7842_MODE_COMP: + case ADV7842_MODE_RGB: + /* Automatic analog input muxing mode */ + afe_write_and_or(sd, 0x02, 0x7f, 0x00); + /* set mode and select free run resolution */ + io_write(sd, 0x00, vid_std_select); /* video std */ + io_write(sd, 0x01, 0x02); /* prim mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x10); /* enable embedded syncs + for auto graphics mode */ + + afe_write(sd, 0x00, 0x00); /* power up ADC */ + afe_write(sd, 0xc8, 0x00); /* phase control */ + + /* set ADI recommended settings for digitizer */ + /* "ADV7842 Register Settings Recommendations + * (rev. 1.8, November 2010)" p. 9. */ + afe_write(sd, 0x0c, 0x1f); /* ADC Range improvement */ + afe_write(sd, 0x12, 0x63); /* ADC Range improvement */ + + /* set to default gain for RGB */ + cp_write(sd, 0x73, 0x10); + cp_write(sd, 0x74, 0x04); + cp_write(sd, 0x75, 0x01); + cp_write(sd, 0x76, 0x00); + + cp_write(sd, 0x3e, 0x04); /* CP core pre-gain control */ + cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ + cp_write(sd, 0x40, 0x5c); /* CP core pre-gain control. Graphics mode */ + break; + + case ADV7842_MODE_HDMI: + /* Automatic analog input muxing mode */ + afe_write_and_or(sd, 0x02, 0x7f, 0x00); + /* set mode and select free run resolution */ + if (state->hdmi_port_a) + hdmi_write(sd, 0x00, 0x02); /* select port A */ + else + hdmi_write(sd, 0x00, 0x03); /* select port B */ + io_write(sd, 0x00, vid_std_select); /* video std */ + io_write(sd, 0x01, 5); /* prim mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x00); /* disable embedded syncs + for auto graphics mode */ + + /* set ADI recommended settings for HDMI: */ + /* "ADV7842 Register Settings Recommendations + * (rev. 1.8, November 2010)" p. 3. */ + hdmi_write(sd, 0xc0, 0x00); + hdmi_write(sd, 0x0d, 0x34); /* ADI recommended write */ + hdmi_write(sd, 0x3d, 0x10); /* ADI recommended write */ + hdmi_write(sd, 0x44, 0x85); /* TMDS PLL optimization */ + hdmi_write(sd, 0x46, 0x1f); /* ADI recommended write */ + hdmi_write(sd, 0x57, 0xb6); /* TMDS PLL optimization */ + hdmi_write(sd, 0x58, 0x03); /* TMDS PLL optimization */ + hdmi_write(sd, 0x60, 0x88); /* TMDS PLL optimization */ + hdmi_write(sd, 0x61, 0x88); /* TMDS PLL optimization */ + hdmi_write(sd, 0x6c, 0x18); /* Disable ISRC clearing bit, + Improve robustness */ + hdmi_write(sd, 0x75, 0x10); /* DDC drive strength */ + hdmi_write(sd, 0x85, 0x1f); /* equaliser */ + hdmi_write(sd, 0x87, 0x70); /* ADI recommended write */ + hdmi_write(sd, 0x89, 0x04); /* equaliser */ + hdmi_write(sd, 0x8a, 0x1e); /* equaliser */ + hdmi_write(sd, 0x93, 0x04); /* equaliser */ + hdmi_write(sd, 0x94, 0x1e); /* equaliser */ + hdmi_write(sd, 0x99, 0xa1); /* ADI recommended write */ + hdmi_write(sd, 0x9b, 0x09); /* ADI recommended write */ + hdmi_write(sd, 0x9d, 0x02); /* equaliser */ + + afe_write(sd, 0x00, 0xff); /* power down ADC */ + afe_write(sd, 0xc8, 0x40); /* phase control */ + + /* set to default gain for HDMI */ + cp_write(sd, 0x73, 0x10); + cp_write(sd, 0x74, 0x04); + cp_write(sd, 0x75, 0x01); + cp_write(sd, 0x76, 0x00); + + /* reset ADI recommended settings for digitizer */ + /* "ADV7842 Register Settings Recommendations + * (rev. 2.5, June 2010)" p. 17. */ + afe_write(sd, 0x12, 0xfb); /* ADC noise shaping filter controls */ + afe_write(sd, 0x0c, 0x0d); /* CP core gain controls */ + cp_write(sd, 0x3e, 0x80); /* CP core pre-gain control, + enable color control */ + /* CP coast control */ + cp_write(sd, 0xc3, 0x33); /* Component mode */ + + /* color space conversion, autodetect color space */ + io_write_and_or(sd, 0x02, 0x0f, 0xf0); + break; + + default: + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); + break; + } +} + +static int adv7842_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7842_state *state = to_state(sd); + + v4l2_dbg(2, debug, sd, "%s: input %d\n", __func__, input); + + switch (input) { + case ADV7842_SELECT_HDMI_PORT_A: + /* TODO select HDMI_COMP or HDMI_GR */ + state->mode = ADV7842_MODE_HDMI; + state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P; + state->hdmi_port_a = true; + break; + case ADV7842_SELECT_HDMI_PORT_B: + /* TODO select HDMI_COMP or HDMI_GR */ + state->mode = ADV7842_MODE_HDMI; + state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P; + state->hdmi_port_a = false; + break; + case ADV7842_SELECT_VGA_COMP: + v4l2_info(sd, "%s: VGA component: todo\n", __func__); + case ADV7842_SELECT_VGA_RGB: + state->mode = ADV7842_MODE_RGB; + state->vid_std_select = ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE; + break; + case ADV7842_SELECT_SDP_CVBS: + state->mode = ADV7842_MODE_SDP; + state->vid_std_select = ADV7842_SDP_VID_STD_CVBS_SD_4x1; + break; + case ADV7842_SELECT_SDP_YC: + state->mode = ADV7842_MODE_SDP; + state->vid_std_select = ADV7842_SDP_VID_STD_YC_SD4_x1; + break; + default: + return -EINVAL; + } + + disable_input(sd); + select_input(sd, state->vid_std_select); + enable_input(sd); + + v4l2_subdev_notify(sd, ADV7842_FMT_CHANGE, NULL); + + return 0; +} + +static int adv7842_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index) + return -EINVAL; + /* Good enough for now */ + *code = V4L2_MBUS_FMT_FIXED; + return 0; +} + +static int adv7842_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7842_state *state = to_state(sd); + + fmt->width = state->timings.bt.width; + fmt->height = state->timings.bt.height; + fmt->code = V4L2_MBUS_FMT_FIXED; + fmt->field = V4L2_FIELD_NONE; + + if (state->mode == ADV7842_MODE_SDP) { + /* SPD block */ + if (!(sdp_read(sd, 0x5A) & 0x01)) + return -EINVAL; + fmt->width = 720; + /* valid signal */ + if (state->norm & V4L2_STD_525_60) + fmt->height = 480; + else + fmt->height = 576; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; + } + + if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { + fmt->colorspace = (state->timings.bt.height <= 576) ? + V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; + } + return 0; +} + +static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable) +{ + if (enable) { + /* Enable SSPD, STDI and CP locked/unlocked interrupts */ + io_write(sd, 0x46, 0x9c); + /* ESDP_50HZ_DET interrupt */ + io_write(sd, 0x5a, 0x10); + /* Enable CABLE_DET_A/B_ST (+5v) interrupt */ + io_write(sd, 0x73, 0x03); + /* Enable V_LOCKED and DE_REGEN_LCK interrupts */ + io_write(sd, 0x78, 0x03); + /* Enable SDP Standard Detection Change and SDP Video Detected */ + io_write(sd, 0xa0, 0x09); + } else { + io_write(sd, 0x46, 0x0); + io_write(sd, 0x5a, 0x0); + io_write(sd, 0x73, 0x0); + io_write(sd, 0x78, 0x0); + io_write(sd, 0xa0, 0x0); + } +} + +static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + struct adv7842_state *state = to_state(sd); + u8 fmt_change_cp, fmt_change_digital, fmt_change_sdp; + u8 irq_status[5]; + u8 irq_cfg = io_read(sd, 0x40); + + /* disable irq-pin output */ + io_write(sd, 0x40, irq_cfg | 0x3); + + /* read status */ + irq_status[0] = io_read(sd, 0x43); + irq_status[1] = io_read(sd, 0x57); + irq_status[2] = io_read(sd, 0x70); + irq_status[3] = io_read(sd, 0x75); + irq_status[4] = io_read(sd, 0x9d); + + /* and clear */ + if (irq_status[0]) + io_write(sd, 0x44, irq_status[0]); + if (irq_status[1]) + io_write(sd, 0x58, irq_status[1]); + if (irq_status[2]) + io_write(sd, 0x71, irq_status[2]); + if (irq_status[3]) + io_write(sd, 0x76, irq_status[3]); + if (irq_status[4]) + io_write(sd, 0x9e, irq_status[4]); + + v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x\n", __func__, + irq_status[0], irq_status[1], irq_status[2], + irq_status[3], irq_status[4]); + + /* format change CP */ + fmt_change_cp = irq_status[0] & 0x9c; + + /* format change SDP */ + if (state->mode == ADV7842_MODE_SDP) + fmt_change_sdp = (irq_status[1] & 0x30) | (irq_status[4] & 0x09); + else + fmt_change_sdp = 0; + + /* digital format CP */ + if (is_digital_input(sd)) + fmt_change_digital = irq_status[3] & 0x03; + else + fmt_change_digital = 0; + + /* notify */ + if (fmt_change_cp || fmt_change_digital || fmt_change_sdp) { + v4l2_dbg(1, debug, sd, + "%s: fmt_change_cp = 0x%x, fmt_change_digital = 0x%x, fmt_change_sdp = 0x%x\n", + __func__, fmt_change_cp, fmt_change_digital, + fmt_change_sdp); + v4l2_subdev_notify(sd, ADV7842_FMT_CHANGE, NULL); + } + + /* 5v cable detect */ + if (irq_status[2]) + adv7842_s_detect_tx_5v_ctrl(sd); + + if (handled) + *handled = true; + + /* re-enable irq-pin output */ + io_write(sd, 0x40, irq_cfg); + + return 0; +} + +static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e) +{ + struct adv7842_state *state = to_state(sd); + int err = 0; + + if (e->pad > 2) + return -EINVAL; + if (e->start_block != 0) + return -EINVAL; + if (e->blocks > 2) + return -E2BIG; + if (!e->edid) + return -EINVAL; + + /* todo, per edid */ + state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15], + e->edid[0x16]); + + if (e->pad == 2) { + memset(&state->vga_edid.edid, 0, 256); + state->vga_edid.present = e->blocks ? 0x1 : 0x0; + memcpy(&state->vga_edid.edid, e->edid, 128 * e->blocks); + err = edid_write_vga_segment(sd); + } else { + u32 mask = 0x1<pad; + memset(&state->hdmi_edid.edid, 0, 256); + if (e->blocks) + state->hdmi_edid.present |= mask; + else + state->hdmi_edid.present &= ~mask; + memcpy(&state->hdmi_edid.edid, e->edid, 128*e->blocks); + err = edid_write_hdmi_segment(sd, e->pad); + } + if (err < 0) + v4l2_err(sd, "error %d writing edid on port %d\n", err, e->pad); + return err; +} + +/*********** avi info frame CEA-861-E **************/ +/* TODO move to common library */ + +struct avi_info_frame { + uint8_t f17; + uint8_t y10; + uint8_t a0; + uint8_t b10; + uint8_t s10; + uint8_t c10; + uint8_t m10; + uint8_t r3210; + uint8_t itc; + uint8_t ec210; + uint8_t q10; + uint8_t sc10; + uint8_t f47; + uint8_t vic; + uint8_t yq10; + uint8_t cn10; + uint8_t pr3210; + uint16_t etb; + uint16_t sbb; + uint16_t elb; + uint16_t srb; +}; + +static const char *y10_txt[4] = { + "RGB", + "YCbCr 4:2:2", + "YCbCr 4:4:4", + "Future", +}; + +static const char *c10_txt[4] = { + "No Data", + "SMPTE 170M", + "ITU-R 709", + "Extended Colorimetry information valied", +}; + +static const char *itc_txt[2] = { + "No Data", + "IT content", +}; + +static const char *ec210_txt[8] = { + "xvYCC601", + "xvYCC709", + "sYCC601", + "AdobeYCC601", + "AdobeRGB", + "5 reserved", + "6 reserved", + "7 reserved", +}; + +static const char *q10_txt[4] = { + "Default", + "Limited Range", + "Full Range", + "Reserved", +}; + +static void parse_avi_infoframe(struct v4l2_subdev *sd, uint8_t *buf, + struct avi_info_frame *avi) +{ + avi->f17 = (buf[1] >> 7) & 0x1; + avi->y10 = (buf[1] >> 5) & 0x3; + avi->a0 = (buf[1] >> 4) & 0x1; + avi->b10 = (buf[1] >> 2) & 0x3; + avi->s10 = buf[1] & 0x3; + avi->c10 = (buf[2] >> 6) & 0x3; + avi->m10 = (buf[2] >> 4) & 0x3; + avi->r3210 = buf[2] & 0xf; + avi->itc = (buf[3] >> 7) & 0x1; + avi->ec210 = (buf[3] >> 4) & 0x7; + avi->q10 = (buf[3] >> 2) & 0x3; + avi->sc10 = buf[3] & 0x3; + avi->f47 = (buf[4] >> 7) & 0x1; + avi->vic = buf[4] & 0x7f; + avi->yq10 = (buf[5] >> 6) & 0x3; + avi->cn10 = (buf[5] >> 4) & 0x3; + avi->pr3210 = buf[5] & 0xf; + avi->etb = buf[6] + 256*buf[7]; + avi->sbb = buf[8] + 256*buf[9]; + avi->elb = buf[10] + 256*buf[11]; + avi->srb = buf[12] + 256*buf[13]; +} + +static void print_avi_infoframe(struct v4l2_subdev *sd) +{ + int i; + uint8_t buf[14]; + uint8_t avi_inf_len; + struct avi_info_frame avi; + + if (!(hdmi_read(sd, 0x05) & 0x80)) { + v4l2_info(sd, "receive DVI-D signal (AVI infoframe not supported)\n"); + return; + } + if (!(io_read(sd, 0x60) & 0x01)) { + v4l2_info(sd, "AVI infoframe not received\n"); + return; + } + + if (io_read(sd, 0x88) & 0x10) { + /* Note: the ADV7842 calculated incorrect checksums for InfoFrames + with a length of 14 or 15. See the ADV7842 Register Settings + Recommendations document for more details. */ + v4l2_info(sd, "AVI infoframe checksum error\n"); + return; + } + + avi_inf_len = infoframe_read(sd, 0xe2); + v4l2_info(sd, "AVI infoframe version %d (%d byte)\n", + infoframe_read(sd, 0xe1), avi_inf_len); + + if (infoframe_read(sd, 0xe1) != 0x02) + return; + + for (i = 0; i < 14; i++) + buf[i] = infoframe_read(sd, i); + + v4l2_info(sd, "\t%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]); + + parse_avi_infoframe(sd, buf, &avi); + + if (avi.vic) + v4l2_info(sd, "\tVIC: %d\n", avi.vic); + if (avi.itc) + v4l2_info(sd, "\t%s\n", itc_txt[avi.itc]); + + if (avi.y10) + v4l2_info(sd, "\t%s %s\n", y10_txt[avi.y10], !avi.c10 ? "" : + (avi.c10 == 0x3 ? ec210_txt[avi.ec210] : c10_txt[avi.c10])); + else + v4l2_info(sd, "\t%s %s\n", y10_txt[avi.y10], q10_txt[avi.q10]); +} + +static const char * const prim_mode_txt[] = { + "SDP", + "Component", + "Graphics", + "Reserved", + "CVBS & HDMI AUDIO", + "HDMI-Comp", + "HDMI-GR", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", +}; + +static int adv7842_sdp_log_status(struct v4l2_subdev *sd) +{ + /* SDP (Standard definition processor) block */ + uint8_t sdp_signal_detected = sdp_read(sd, 0x5A) & 0x01; + + v4l2_info(sd, "Chip powered %s\n", no_power(sd) ? "off" : "on"); + v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x\n", + io_read(sd, 0x01) & 0x0f, io_read(sd, 0x00) & 0x3f); + + v4l2_info(sd, "SDP: free run: %s\n", + (sdp_read(sd, 0x56) & 0x01) ? "on" : "off"); + v4l2_info(sd, "SDP: %s\n", sdp_signal_detected ? + "valid SD/PR signal detected" : "invalid/no signal"); + if (sdp_signal_detected) { + static const char * const sdp_std_txt[] = { + "NTSC-M/J", + "1?", + "NTSC-443", + "60HzSECAM", + "PAL-M", + "5?", + "PAL-60", + "7?", "8?", "9?", "a?", "b?", + "PAL-CombN", + "d?", + "PAL-BGHID", + "SECAM" + }; + v4l2_info(sd, "SDP: standard %s\n", + sdp_std_txt[sdp_read(sd, 0x52) & 0x0f]); + v4l2_info(sd, "SDP: %s\n", + (sdp_read(sd, 0x59) & 0x08) ? "50Hz" : "60Hz"); + v4l2_info(sd, "SDP: %s\n", + (sdp_read(sd, 0x57) & 0x08) ? "Interlaced" : "Progressive"); + v4l2_info(sd, "SDP: deinterlacer %s\n", + (sdp_read(sd, 0x12) & 0x08) ? "enabled" : "disabled"); + v4l2_info(sd, "SDP: csc %s mode\n", + (sdp_io_read(sd, 0xe0) & 0x40) ? "auto" : "manual"); + } + return 0; +} + +static int adv7842_cp_log_status(struct v4l2_subdev *sd) +{ + /* CP block */ + struct adv7842_state *state = to_state(sd); + struct v4l2_dv_timings timings; + uint8_t reg_io_0x02 = io_read(sd, 0x02); + uint8_t reg_io_0x21 = io_read(sd, 0x21); + uint8_t reg_rep_0x77 = rep_read(sd, 0x77); + uint8_t reg_rep_0x7d = rep_read(sd, 0x7d); + bool audio_pll_locked = hdmi_read(sd, 0x04) & 0x01; + bool audio_sample_packet_detect = hdmi_read(sd, 0x18) & 0x01; + bool audio_mute = io_read(sd, 0x65) & 0x40; + + static const char * const csc_coeff_sel_rb[16] = { + "bypassed", "YPbPr601 -> RGB", "reserved", "YPbPr709 -> RGB", + "reserved", "RGB -> YPbPr601", "reserved", "RGB -> YPbPr709", + "reserved", "YPbPr709 -> YPbPr601", "YPbPr601 -> YPbPr709", + "reserved", "reserved", "reserved", "reserved", "manual" + }; + static const char * const input_color_space_txt[16] = { + "RGB limited range (16-235)", "RGB full range (0-255)", + "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", + "XvYCC Bt.601", "XvYCC Bt.709", + "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)", + "invalid", "invalid", "invalid", "invalid", "invalid", + "invalid", "invalid", "automatic" + }; + static const char * const rgb_quantization_range_txt[] = { + "Automatic", + "RGB limited range (16-235)", + "RGB full range (0-255)", + }; + static const char * const deep_color_mode_txt[4] = { + "8-bits per channel", + "10-bits per channel", + "12-bits per channel", + "16-bits per channel (not supported)" + }; + + v4l2_info(sd, "-----Chip status-----\n"); + v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); + v4l2_info(sd, "Connector type: %s\n", state->connector_hdmi ? + "HDMI" : (is_digital_input(sd) ? "DVI-D" : "DVI-A")); + v4l2_info(sd, "HDMI/DVI-D port selected: %s\n", + state->hdmi_port_a ? "A" : "B"); + v4l2_info(sd, "EDID A %s, B %s\n", + ((reg_rep_0x7d & 0x04) && (reg_rep_0x77 & 0x04)) ? + "enabled" : "disabled", + ((reg_rep_0x7d & 0x08) && (reg_rep_0x77 & 0x08)) ? + "enabled" : "disabled"); + v4l2_info(sd, "HPD A %s, B %s\n", + reg_io_0x21 & 0x02 ? "enabled" : "disabled", + reg_io_0x21 & 0x01 ? "enabled" : "disabled"); + v4l2_info(sd, "CEC %s\n", !!(cec_read(sd, 0x2a) & 0x01) ? + "enabled" : "disabled"); + + v4l2_info(sd, "-----Signal status-----\n"); + if (state->hdmi_port_a) { + v4l2_info(sd, "Cable detected (+5V power): %s\n", + io_read(sd, 0x6f) & 0x02 ? "true" : "false"); + v4l2_info(sd, "TMDS signal detected: %s\n", + (io_read(sd, 0x6a) & 0x02) ? "true" : "false"); + v4l2_info(sd, "TMDS signal locked: %s\n", + (io_read(sd, 0x6a) & 0x20) ? "true" : "false"); + } else { + v4l2_info(sd, "Cable detected (+5V power):%s\n", + io_read(sd, 0x6f) & 0x01 ? "true" : "false"); + v4l2_info(sd, "TMDS signal detected: %s\n", + (io_read(sd, 0x6a) & 0x01) ? "true" : "false"); + v4l2_info(sd, "TMDS signal locked: %s\n", + (io_read(sd, 0x6a) & 0x10) ? "true" : "false"); + } + v4l2_info(sd, "CP free run: %s\n", + (!!(cp_read(sd, 0xff) & 0x10) ? "on" : "off")); + v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x, v_freq = 0x%x\n", + io_read(sd, 0x01) & 0x0f, io_read(sd, 0x00) & 0x3f, + (io_read(sd, 0x01) & 0x70) >> 4); + + v4l2_info(sd, "-----Video Timings-----\n"); + if (no_cp_signal(sd)) { + v4l2_info(sd, "STDI: not locked\n"); + } else { + uint32_t bl = ((cp_read(sd, 0xb1) & 0x3f) << 8) | cp_read(sd, 0xb2); + uint32_t lcf = ((cp_read(sd, 0xb3) & 0x7) << 8) | cp_read(sd, 0xb4); + uint32_t lcvs = cp_read(sd, 0xb3) >> 3; + uint32_t fcl = ((cp_read(sd, 0xb8) & 0x1f) << 8) | cp_read(sd, 0xb9); + char hs_pol = ((cp_read(sd, 0xb5) & 0x10) ? + ((cp_read(sd, 0xb5) & 0x08) ? '+' : '-') : 'x'); + char vs_pol = ((cp_read(sd, 0xb5) & 0x40) ? + ((cp_read(sd, 0xb5) & 0x20) ? '+' : '-') : 'x'); + v4l2_info(sd, + "STDI: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, fcl = %d, %s, %chsync, %cvsync\n", + lcf, bl, lcvs, fcl, + (cp_read(sd, 0xb1) & 0x40) ? + "interlaced" : "progressive", + hs_pol, vs_pol); + } + if (adv7842_query_dv_timings(sd, &timings)) + v4l2_info(sd, "No video detected\n"); + else + v4l2_print_dv_timings(sd->name, "Detected format: ", + &timings, true); + v4l2_print_dv_timings(sd->name, "Configured format: ", + &state->timings, true); + + if (no_cp_signal(sd)) + return 0; + + v4l2_info(sd, "-----Color space-----\n"); + v4l2_info(sd, "RGB quantization range ctrl: %s\n", + rgb_quantization_range_txt[state->rgb_quantization_range]); + v4l2_info(sd, "Input color space: %s\n", + input_color_space_txt[reg_io_0x02 >> 4]); + v4l2_info(sd, "Output color space: %s %s, saturator %s\n", + (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr", + (reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)", + ((reg_io_0x02 & 0x04) ^ (reg_io_0x02 & 0x01)) ? + "enabled" : "disabled"); + v4l2_info(sd, "Color space conversion: %s\n", + csc_coeff_sel_rb[cp_read(sd, 0xf4) >> 4]); + + if (!is_digital_input(sd)) + return 0; + + v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); + v4l2_info(sd, "HDCP encrypted content: %s\n", + (hdmi_read(sd, 0x05) & 0x40) ? "true" : "false"); + v4l2_info(sd, "HDCP keys read: %s%s\n", + (hdmi_read(sd, 0x04) & 0x20) ? "yes" : "no", + (hdmi_read(sd, 0x04) & 0x10) ? "ERROR" : ""); + if (!is_hdmi(sd)) + return 0; + + v4l2_info(sd, "Audio: pll %s, samples %s, %s\n", + audio_pll_locked ? "locked" : "not locked", + audio_sample_packet_detect ? "detected" : "not detected", + audio_mute ? "muted" : "enabled"); + if (audio_pll_locked && audio_sample_packet_detect) { + v4l2_info(sd, "Audio format: %s\n", + (hdmi_read(sd, 0x07) & 0x40) ? "multi-channel" : "stereo"); + } + v4l2_info(sd, "Audio CTS: %u\n", (hdmi_read(sd, 0x5b) << 12) + + (hdmi_read(sd, 0x5c) << 8) + + (hdmi_read(sd, 0x5d) & 0xf0)); + v4l2_info(sd, "Audio N: %u\n", ((hdmi_read(sd, 0x5d) & 0x0f) << 16) + + (hdmi_read(sd, 0x5e) << 8) + + hdmi_read(sd, 0x5f)); + v4l2_info(sd, "AV Mute: %s\n", + (hdmi_read(sd, 0x04) & 0x40) ? "on" : "off"); + v4l2_info(sd, "Deep color mode: %s\n", + deep_color_mode_txt[hdmi_read(sd, 0x0b) >> 6]); + + print_avi_infoframe(sd); + return 0; +} + +static int adv7842_log_status(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + + if (state->mode == ADV7842_MODE_SDP) + return adv7842_sdp_log_status(sd); + return adv7842_cp_log_status(sd); +} + +static int adv7842_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7842_state *state = to_state(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + if (state->mode != ADV7842_MODE_SDP) + return -ENODATA; + + if (!(sdp_read(sd, 0x5A) & 0x01)) { + *std = 0; + v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); + return 0; + } + + switch (sdp_read(sd, 0x52) & 0x0f) { + case 0: + /* NTSC-M/J */ + *std &= V4L2_STD_NTSC; + break; + case 2: + /* NTSC-443 */ + *std &= V4L2_STD_NTSC_443; + break; + case 3: + /* 60HzSECAM */ + *std &= V4L2_STD_SECAM; + break; + case 4: + /* PAL-M */ + *std &= V4L2_STD_PAL_M; + break; + case 6: + /* PAL-60 */ + *std &= V4L2_STD_PAL_60; + break; + case 0xc: + /* PAL-CombN */ + *std &= V4L2_STD_PAL_Nc; + break; + case 0xe: + /* PAL-BGHID */ + *std &= V4L2_STD_PAL; + break; + case 0xf: + /* SECAM */ + *std &= V4L2_STD_SECAM; + break; + default: + *std &= V4L2_STD_ALL; + break; + } + return 0; +} + +static int adv7842_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct adv7842_state *state = to_state(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + if (state->mode != ADV7842_MODE_SDP) + return -ENODATA; + + if (norm & V4L2_STD_ALL) { + state->norm = norm; + return 0; + } + return -EINVAL; +} + +static int adv7842_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + struct adv7842_state *state = to_state(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + if (state->mode != ADV7842_MODE_SDP) + return -ENODATA; + + *norm = state->norm; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int adv7842_core_init(struct v4l2_subdev *sd, + const struct adv7842_platform_data *pdata) +{ + hdmi_write(sd, 0x48, + (pdata->disable_pwrdnb ? 0x80 : 0) | + (pdata->disable_cable_det_rst ? 0x40 : 0)); + + disable_input(sd); + + /* power */ + io_write(sd, 0x0c, 0x42); /* Power up part and power down VDP */ + io_write(sd, 0x15, 0x80); /* Power up pads */ + + /* video format */ + io_write(sd, 0x02, + pdata->inp_color_space << 4 | + pdata->alt_gamma << 3 | + pdata->op_656_range << 2 | + pdata->rgb_out << 1 | + pdata->alt_data_sat << 0); + io_write(sd, 0x03, pdata->op_format_sel); + io_write_and_or(sd, 0x04, 0x1f, pdata->op_ch_sel << 5); + io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 | + pdata->insert_av_codes << 2 | + pdata->replicate_av_codes << 1 | + pdata->invert_cbcr << 0); + + /* Drive strength */ + io_write_and_or(sd, 0x14, 0xc0, pdata->drive_strength.data<<4 | + pdata->drive_strength.clock<<2 | + pdata->drive_strength.sync); + + /* HDMI free run */ + cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); + + /* TODO from platform data */ + cp_write(sd, 0x69, 0x14); /* Enable CP CSC */ + io_write(sd, 0x06, 0xa6); /* positive VS and HS and DE */ + cp_write(sd, 0xf3, 0xdc); /* Low threshold to enter/exit free run mode */ + afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */ + + afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ + io_write_and_or(sd, 0x30, ~(1 << 4), pdata->output_bus_lsb_to_msb << 4); + + sdp_csc_coeff(sd, &pdata->sdp_csc_coeff); + + if (pdata->sdp_io_sync.adjust) { + const struct adv7842_sdp_io_sync_adjustment *s = &pdata->sdp_io_sync; + sdp_io_write(sd, 0x94, (s->hs_beg>>8) & 0xf); + sdp_io_write(sd, 0x95, s->hs_beg & 0xff); + sdp_io_write(sd, 0x96, (s->hs_width>>8) & 0xf); + sdp_io_write(sd, 0x97, s->hs_width & 0xff); + sdp_io_write(sd, 0x98, (s->de_beg>>8) & 0xf); + sdp_io_write(sd, 0x99, s->de_beg & 0xff); + sdp_io_write(sd, 0x9a, (s->de_end>>8) & 0xf); + sdp_io_write(sd, 0x9b, s->de_end & 0xff); + } + + /* todo, improve settings for sdram */ + if (pdata->sd_ram_size >= 128) { + sdp_write(sd, 0x12, 0x0d); /* Frame TBC,3D comb enabled */ + if (pdata->sd_ram_ddr) { + /* SDP setup for the AD eval board */ + sdp_io_write(sd, 0x6f, 0x00); /* DDR mode */ + sdp_io_write(sd, 0x75, 0x0a); /* 128 MB memory size */ + sdp_io_write(sd, 0x7a, 0xa5); /* Timing Adjustment */ + sdp_io_write(sd, 0x7b, 0x8f); /* Timing Adjustment */ + sdp_io_write(sd, 0x60, 0x01); /* SDRAM reset */ + } else { + sdp_io_write(sd, 0x75, 0x0a); /* 64 MB memory size ?*/ + sdp_io_write(sd, 0x74, 0x00); /* must be zero for sdr sdram */ + sdp_io_write(sd, 0x79, 0x33); /* CAS latency to 3, + depends on memory */ + sdp_io_write(sd, 0x6f, 0x01); /* SDR mode */ + sdp_io_write(sd, 0x7a, 0xa5); /* Timing Adjustment */ + sdp_io_write(sd, 0x7b, 0x8f); /* Timing Adjustment */ + sdp_io_write(sd, 0x60, 0x01); /* SDRAM reset */ + } + } else { + /* + * Manual UG-214, rev 0 is bit confusing on this bit + * but a '1' disables any signal if the Ram is active. + */ + sdp_io_write(sd, 0x29, 0x10); /* Tristate memory interface */ + } + + select_input(sd, pdata->vid_std_select); + + enable_input(sd); + + /* disable I2C access to internal EDID ram from HDMI DDC ports */ + rep_write_and_or(sd, 0x77, 0xf3, 0x00); + + hdmi_write(sd, 0x69, 0xa3); /* HPA manual */ + /* HPA disable on port A and B */ + io_write_and_or(sd, 0x20, 0xcf, 0x00); + + /* LLC */ + /* Set phase to 16. TODO: get this from platform_data */ + io_write(sd, 0x19, 0x90); + io_write(sd, 0x33, 0x40); + + /* interrupts */ + io_write(sd, 0x40, 0xe2); /* Configure INT1 */ + + adv7842_irq_enable(sd, true); + + return v4l2_ctrl_handler_setup(sd->ctrl_handler); +} + +/* ----------------------------------------------------------------------- */ + +static int adv7842_ddr_ram_test(struct v4l2_subdev *sd) +{ + /* + * From ADV784x external Memory test.pdf + * + * Reset must just been performed before running test. + * Recommended to reset after test. + */ + int i; + int pass = 0; + int fail = 0; + int complete = 0; + + io_write(sd, 0x00, 0x01); /* Program SDP 4x1 */ + io_write(sd, 0x01, 0x00); /* Program SDP mode */ + afe_write(sd, 0x80, 0x92); /* SDP Recommeneded Write */ + afe_write(sd, 0x9B, 0x01); /* SDP Recommeneded Write ADV7844ES1 */ + afe_write(sd, 0x9C, 0x60); /* SDP Recommeneded Write ADV7844ES1 */ + afe_write(sd, 0x9E, 0x02); /* SDP Recommeneded Write ADV7844ES1 */ + afe_write(sd, 0xA0, 0x0B); /* SDP Recommeneded Write ADV7844ES1 */ + afe_write(sd, 0xC3, 0x02); /* Memory BIST Initialisation */ + io_write(sd, 0x0C, 0x40); /* Power up ADV7844 */ + io_write(sd, 0x15, 0xBA); /* Enable outputs */ + sdp_write(sd, 0x12, 0x00); /* Disable 3D comb, Frame TBC & 3DNR */ + io_write(sd, 0xFF, 0x04); /* Reset memory controller */ + + mdelay(5); + + sdp_write(sd, 0x12, 0x00); /* Disable 3D Comb, Frame TBC & 3DNR */ + sdp_io_write(sd, 0x2A, 0x01); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x7c, 0x19); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x80, 0x87); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x81, 0x4a); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x82, 0x2c); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x83, 0x0e); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x84, 0x94); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x85, 0x62); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x7d, 0x00); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x7e, 0x1a); /* Memory BIST Initialisation */ + + mdelay(5); + + sdp_io_write(sd, 0xd9, 0xd5); /* Enable BIST Test */ + sdp_write(sd, 0x12, 0x05); /* Enable FRAME TBC & 3D COMB */ + + mdelay(20); + + for (i = 0; i < 10; i++) { + u8 result = sdp_io_read(sd, 0xdb); + if (result & 0x10) { + complete++; + if (result & 0x20) + fail++; + else + pass++; + } + mdelay(20); + } + + v4l2_dbg(1, debug, sd, + "Ram Test: completed %d of %d: pass %d, fail %d\n", + complete, i, pass, fail); + + if (!complete || fail) + return -EIO; + return 0; +} + +static void adv7842_rewrite_i2c_addresses(struct v4l2_subdev *sd, + struct adv7842_platform_data *pdata) +{ + io_write(sd, 0xf1, pdata->i2c_sdp << 1); + io_write(sd, 0xf2, pdata->i2c_sdp_io << 1); + io_write(sd, 0xf3, pdata->i2c_avlink << 1); + io_write(sd, 0xf4, pdata->i2c_cec << 1); + io_write(sd, 0xf5, pdata->i2c_infoframe << 1); + + io_write(sd, 0xf8, pdata->i2c_afe << 1); + io_write(sd, 0xf9, pdata->i2c_repeater << 1); + io_write(sd, 0xfa, pdata->i2c_edid << 1); + io_write(sd, 0xfb, pdata->i2c_hdmi << 1); + + io_write(sd, 0xfd, pdata->i2c_cp << 1); + io_write(sd, 0xfe, pdata->i2c_vdp << 1); +} + +static int adv7842_command_ram_test(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7842_state *state = to_state(sd); + struct adv7842_platform_data *pdata = client->dev.platform_data; + int ret = 0; + + if (!pdata) + return -ENODEV; + + if (!pdata->sd_ram_size || !pdata->sd_ram_ddr) { + v4l2_info(sd, "no sdram or no ddr sdram\n"); + return -EINVAL; + } + + main_reset(sd); + + adv7842_rewrite_i2c_addresses(sd, pdata); + + /* run ram test */ + ret = adv7842_ddr_ram_test(sd); + + main_reset(sd); + + adv7842_rewrite_i2c_addresses(sd, pdata); + + /* and re-init chip and state */ + adv7842_core_init(sd, pdata); + + disable_input(sd); + + select_input(sd, state->vid_std_select); + + enable_input(sd); + + adv7842_s_dv_timings(sd, &state->timings); + + edid_write_vga_segment(sd); + edid_write_hdmi_segment(sd, 0); + edid_write_hdmi_segment(sd, 1); + + return ret; +} + +static long adv7842_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + switch (cmd) { + case ADV7842_CMD_RAM_TEST: + return adv7842_command_ram_test(sd); + } + return -ENOTTY; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops adv7842_ctrl_ops = { + .s_ctrl = adv7842_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7842_core_ops = { + .log_status = adv7842_log_status, + .g_std = adv7842_g_std, + .s_std = adv7842_s_std, + .ioctl = adv7842_ioctl, + .interrupt_service_routine = adv7842_isr, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = adv7842_g_register, + .s_register = adv7842_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops adv7842_video_ops = { + .s_routing = adv7842_s_routing, + .querystd = adv7842_querystd, + .g_input_status = adv7842_g_input_status, + .s_dv_timings = adv7842_s_dv_timings, + .g_dv_timings = adv7842_g_dv_timings, + .query_dv_timings = adv7842_query_dv_timings, + .enum_dv_timings = adv7842_enum_dv_timings, + .dv_timings_cap = adv7842_dv_timings_cap, + .enum_mbus_fmt = adv7842_enum_mbus_fmt, + .g_mbus_fmt = adv7842_g_mbus_fmt, + .try_mbus_fmt = adv7842_g_mbus_fmt, + .s_mbus_fmt = adv7842_g_mbus_fmt, +}; + +static const struct v4l2_subdev_pad_ops adv7842_pad_ops = { + .set_edid = adv7842_set_edid, +}; + +static const struct v4l2_subdev_ops adv7842_ops = { + .core = &adv7842_core_ops, + .video = &adv7842_video_ops, + .pad = &adv7842_pad_ops, +}; + +/* -------------------------- custom ctrls ---------------------------------- */ + +static const struct v4l2_ctrl_config adv7842_ctrl_analog_sampling_phase = { + .ops = &adv7842_ctrl_ops, + .id = V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE, + .name = "Analog Sampling Phase", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 0x1f, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config adv7842_ctrl_free_run_color_manual = { + .ops = &adv7842_ctrl_ops, + .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL, + .name = "Free Running Color, Manual", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, + .def = 1, +}; + +static const struct v4l2_ctrl_config adv7842_ctrl_free_run_color = { + .ops = &adv7842_ctrl_ops, + .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR, + .name = "Free Running Color", + .type = V4L2_CTRL_TYPE_INTEGER, + .max = 0xffffff, + .step = 0x1, +}; + + +static void adv7842_unregister_clients(struct adv7842_state *state) +{ + if (state->i2c_avlink) + i2c_unregister_device(state->i2c_avlink); + if (state->i2c_cec) + i2c_unregister_device(state->i2c_cec); + if (state->i2c_infoframe) + i2c_unregister_device(state->i2c_infoframe); + if (state->i2c_sdp_io) + i2c_unregister_device(state->i2c_sdp_io); + if (state->i2c_sdp) + i2c_unregister_device(state->i2c_sdp); + if (state->i2c_afe) + i2c_unregister_device(state->i2c_afe); + if (state->i2c_repeater) + i2c_unregister_device(state->i2c_repeater); + if (state->i2c_edid) + i2c_unregister_device(state->i2c_edid); + if (state->i2c_hdmi) + i2c_unregister_device(state->i2c_hdmi); + if (state->i2c_cp) + i2c_unregister_device(state->i2c_cp); + if (state->i2c_vdp) + i2c_unregister_device(state->i2c_vdp); +} + +static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, + u8 addr, u8 io_reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + io_write(sd, io_reg, addr << 1); + return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); +} + +static int adv7842_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7842_state *state; + struct adv7842_platform_data *pdata = client->dev.platform_data; + struct v4l2_ctrl_handler *hdl; + struct v4l2_subdev *sd; + u16 rev; + int err; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_dbg(1, debug, client, "detecting adv7842 client on address 0x%x\n", + client->addr << 1); + + if (!pdata) { + v4l_err(client, "No platform data!\n"); + return -ENODEV; + } + + state = devm_kzalloc(&client->dev, sizeof(struct adv7842_state), GFP_KERNEL); + if (!state) { + v4l_err(client, "Could not allocate adv7842_state memory!\n"); + return -ENOMEM; + } + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &adv7842_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + state->connector_hdmi = pdata->connector_hdmi; + state->mode = pdata->mode; + + state->hdmi_port_a = true; + + /* i2c access to adv7842? */ + rev = adv_smbus_read_byte_data_check(client, 0xea, false) << 8 | + adv_smbus_read_byte_data_check(client, 0xeb, false); + if (rev != 0x2012) { + v4l2_info(sd, "got rev=0x%04x on first read attempt\n", rev); + rev = adv_smbus_read_byte_data_check(client, 0xea, false) << 8 | + adv_smbus_read_byte_data_check(client, 0xeb, false); + } + if (rev != 0x2012) { + v4l2_info(sd, "not an adv7842 on address 0x%x (rev=0x%04x)\n", + client->addr << 1, rev); + return -ENODEV; + } + + if (pdata->chip_reset) + main_reset(sd); + + /* control handlers */ + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 6); + + /* add in ascending ID order */ + v4l2_ctrl_new_std(hdl, &adv7842_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &adv7842_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &adv7842_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &adv7842_ctrl_ops, + V4L2_CID_HUE, 0, 128, 1, 0); + + /* custom controls */ + state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_DV_RX_POWER_PRESENT, 0, 3, 0, 0); + state->analog_sampling_phase_ctrl = v4l2_ctrl_new_custom(hdl, + &adv7842_ctrl_analog_sampling_phase, NULL); + state->free_run_color_ctrl_manual = v4l2_ctrl_new_custom(hdl, + &adv7842_ctrl_free_run_color_manual, NULL); + state->free_run_color_ctrl = v4l2_ctrl_new_custom(hdl, + &adv7842_ctrl_free_run_color, NULL); + state->rgb_quantization_range_ctrl = + v4l2_ctrl_new_std_menu(hdl, &adv7842_ctrl_ops, + V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, + 0, V4L2_DV_RGB_RANGE_AUTO); + sd->ctrl_handler = hdl; + if (hdl->error) { + err = hdl->error; + goto err_hdl; + } + state->detect_tx_5v_ctrl->is_private = true; + state->rgb_quantization_range_ctrl->is_private = true; + state->analog_sampling_phase_ctrl->is_private = true; + state->free_run_color_ctrl_manual->is_private = true; + state->free_run_color_ctrl->is_private = true; + + if (adv7842_s_detect_tx_5v_ctrl(sd)) { + err = -ENODEV; + goto err_hdl; + } + + state->i2c_avlink = adv7842_dummy_client(sd, pdata->i2c_avlink, 0xf3); + state->i2c_cec = adv7842_dummy_client(sd, pdata->i2c_cec, 0xf4); + state->i2c_infoframe = adv7842_dummy_client(sd, pdata->i2c_infoframe, 0xf5); + state->i2c_sdp_io = adv7842_dummy_client(sd, pdata->i2c_sdp_io, 0xf2); + state->i2c_sdp = adv7842_dummy_client(sd, pdata->i2c_sdp, 0xf1); + state->i2c_afe = adv7842_dummy_client(sd, pdata->i2c_afe, 0xf8); + state->i2c_repeater = adv7842_dummy_client(sd, pdata->i2c_repeater, 0xf9); + state->i2c_edid = adv7842_dummy_client(sd, pdata->i2c_edid, 0xfa); + state->i2c_hdmi = adv7842_dummy_client(sd, pdata->i2c_hdmi, 0xfb); + state->i2c_cp = adv7842_dummy_client(sd, pdata->i2c_cp, 0xfd); + state->i2c_vdp = adv7842_dummy_client(sd, pdata->i2c_vdp, 0xfe); + if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe || + !state->i2c_sdp_io || !state->i2c_sdp || !state->i2c_afe || + !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi || + !state->i2c_cp || !state->i2c_vdp) { + err = -ENOMEM; + v4l2_err(sd, "failed to create all i2c clients\n"); + goto err_i2c; + } + + /* work queues */ + state->work_queues = create_singlethread_workqueue(client->name); + if (!state->work_queues) { + v4l2_err(sd, "Could not create work queue\n"); + err = -ENOMEM; + goto err_i2c; + } + + INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, + adv7842_delayed_work_enable_hotplug); + + state->pad.flags = MEDIA_PAD_FL_SOURCE; + err = media_entity_init(&sd->entity, 1, &state->pad, 0); + if (err) + goto err_work_queues; + + err = adv7842_core_init(sd, pdata); + if (err) + goto err_entity; + + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + return 0; + +err_entity: + media_entity_cleanup(&sd->entity); +err_work_queues: + cancel_delayed_work(&state->delayed_work_enable_hotplug); + destroy_workqueue(state->work_queues); +err_i2c: + adv7842_unregister_clients(state); +err_hdl: + v4l2_ctrl_handler_free(hdl); + return err; +} + +/* ----------------------------------------------------------------------- */ + +static int adv7842_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7842_state *state = to_state(sd); + + adv7842_irq_enable(sd, false); + + cancel_delayed_work(&state->delayed_work_enable_hotplug); + destroy_workqueue(state->work_queues); + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + adv7842_unregister_clients(to_state(sd)); + v4l2_ctrl_handler_free(sd->ctrl_handler); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_device_id adv7842_id[] = { + { "adv7842", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7842_id); + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver adv7842_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7842", + }, + .probe = adv7842_probe, + .remove = adv7842_remove, + .id_table = adv7842_id, +}; + +module_i2c_driver(adv7842_driver); diff --git a/include/media/adv7842.h b/include/media/adv7842.h new file mode 100644 index 0000000..c02201d --- /dev/null +++ b/include/media/adv7842.h @@ -0,0 +1,226 @@ +/* + * adv7842 - Analog Devices ADV7842 video decoder driver + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * 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. + * + */ + +#ifndef _ADV7842_ +#define _ADV7842_ + +/* Analog input muxing modes (AFE register 0x02, [2:0]) */ +enum adv7842_ain_sel { + ADV7842_AIN1_2_3_NC_SYNC_1_2 = 0, + ADV7842_AIN4_5_6_NC_SYNC_2_1 = 1, + ADV7842_AIN7_8_9_NC_SYNC_3_1 = 2, + ADV7842_AIN10_11_12_NC_SYNC_4_1 = 3, + ADV7842_AIN9_4_5_6_SYNC_2_1 = 4, +}; + +/* Bus rotation and reordering (IO register 0x04, [7:5]) */ +enum adv7842_op_ch_sel { + ADV7842_OP_CH_SEL_GBR = 0, + ADV7842_OP_CH_SEL_GRB = 1, + ADV7842_OP_CH_SEL_BGR = 2, + ADV7842_OP_CH_SEL_RGB = 3, + ADV7842_OP_CH_SEL_BRG = 4, + ADV7842_OP_CH_SEL_RBG = 5, +}; + +/* Mode of operation */ +enum adv7842_mode { + ADV7842_MODE_SDP, + ADV7842_MODE_COMP, + ADV7842_MODE_RGB, + ADV7842_MODE_HDMI +}; + +/* Video standard select (IO register 0x00, [5:0]) */ +enum adv7842_vid_std_select { + /* SDP */ + ADV7842_SDP_VID_STD_CVBS_SD_4x1 = 0x01, + ADV7842_SDP_VID_STD_YC_SD4_x1 = 0x09, + /* RGB */ + ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE = 0x07, + /* HDMI GR */ + ADV7842_HDMI_GR_VID_STD_AUTO_GRAPH_MODE = 0x02, + /* HDMI COMP */ + ADV7842_HDMI_COMP_VID_STD_HD_1250P = 0x1e, +}; + +/* Input Color Space (IO register 0x02, [7:4]) */ +enum adv7842_inp_color_space { + ADV7842_INP_COLOR_SPACE_LIM_RGB = 0, + ADV7842_INP_COLOR_SPACE_FULL_RGB = 1, + ADV7842_INP_COLOR_SPACE_LIM_YCbCr_601 = 2, + ADV7842_INP_COLOR_SPACE_LIM_YCbCr_709 = 3, + ADV7842_INP_COLOR_SPACE_XVYCC_601 = 4, + ADV7842_INP_COLOR_SPACE_XVYCC_709 = 5, + ADV7842_INP_COLOR_SPACE_FULL_YCbCr_601 = 6, + ADV7842_INP_COLOR_SPACE_FULL_YCbCr_709 = 7, + ADV7842_INP_COLOR_SPACE_AUTO = 0xf, +}; + +/* Select output format (IO register 0x03, [7:0]) */ +enum adv7842_op_format_sel { + ADV7842_OP_FORMAT_SEL_SDR_ITU656_8 = 0x00, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_10 = 0x01, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_12_MODE0 = 0x02, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_12_MODE1 = 0x06, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_12_MODE2 = 0x0a, + ADV7842_OP_FORMAT_SEL_DDR_422_8 = 0x20, + ADV7842_OP_FORMAT_SEL_DDR_422_10 = 0x21, + ADV7842_OP_FORMAT_SEL_DDR_422_12_MODE0 = 0x22, + ADV7842_OP_FORMAT_SEL_DDR_422_12_MODE1 = 0x23, + ADV7842_OP_FORMAT_SEL_DDR_422_12_MODE2 = 0x24, + ADV7842_OP_FORMAT_SEL_SDR_444_24 = 0x40, + ADV7842_OP_FORMAT_SEL_SDR_444_30 = 0x41, + ADV7842_OP_FORMAT_SEL_SDR_444_36_MODE0 = 0x42, + ADV7842_OP_FORMAT_SEL_DDR_444_24 = 0x60, + ADV7842_OP_FORMAT_SEL_DDR_444_30 = 0x61, + ADV7842_OP_FORMAT_SEL_DDR_444_36 = 0x62, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_16 = 0x80, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_20 = 0x81, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_24_MODE0 = 0x82, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_24_MODE1 = 0x86, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_24_MODE2 = 0x8a, +}; + +enum adv7842_select_input { + ADV7842_SELECT_HDMI_PORT_A, + ADV7842_SELECT_HDMI_PORT_B, + ADV7842_SELECT_VGA_RGB, + ADV7842_SELECT_VGA_COMP, + ADV7842_SELECT_SDP_CVBS, + ADV7842_SELECT_SDP_YC, +}; + +struct adv7842_sdp_csc_coeff { + bool manual; + uint16_t scaling; + uint16_t A1; + uint16_t A2; + uint16_t A3; + uint16_t A4; + uint16_t B1; + uint16_t B2; + uint16_t B3; + uint16_t B4; + uint16_t C1; + uint16_t C2; + uint16_t C3; + uint16_t C4; +}; + +struct adv7842_sdp_io_sync_adjustment { + bool adjust; + uint16_t hs_beg; + uint16_t hs_width; + uint16_t de_beg; + uint16_t de_end; +}; + +/* Platform dependent definition */ +struct adv7842_platform_data { + /* connector - HDMI or DVI? */ + unsigned connector_hdmi:1; + + /* chip reset during probe */ + unsigned chip_reset:1; + + /* DIS_PWRDNB: 1 if the PWRDNB pin is unused and unconnected */ + unsigned disable_pwrdnb:1; + + /* DIS_CABLE_DET_RST: 1 if the 5V pins are unused and unconnected */ + unsigned disable_cable_det_rst:1; + + /* Analog input muxing mode */ + enum adv7842_ain_sel ain_sel; + + /* Bus rotation and reordering */ + enum adv7842_op_ch_sel op_ch_sel; + + /* Default mode */ + enum adv7842_mode mode; + + /* Video standard */ + enum adv7842_vid_std_select vid_std_select; + + /* Input Color Space */ + enum adv7842_inp_color_space inp_color_space; + + /* Select output format */ + enum adv7842_op_format_sel op_format_sel; + + /* IO register 0x02 */ + unsigned alt_gamma:1; + unsigned op_656_range:1; + unsigned rgb_out:1; + unsigned alt_data_sat:1; + + /* IO register 0x05 */ + unsigned blank_data:1; + unsigned insert_av_codes:1; + unsigned replicate_av_codes:1; + unsigned invert_cbcr:1; + + /* IO register 0x30 */ + unsigned output_bus_lsb_to_msb:1; + + /* IO register 0x14 */ + struct { + unsigned data:2; + unsigned clock:2; + unsigned sync:2; + } drive_strength; + + /* External RAM for 3-D comb or frame synchronizer */ + unsigned sd_ram_size; /* ram size in MB */ + unsigned sd_ram_ddr:1; /* ddr or sdr sdram */ + + /* Free run */ + unsigned hdmi_free_run_mode; + + struct adv7842_sdp_csc_coeff sdp_csc_coeff; + + struct adv7842_sdp_io_sync_adjustment sdp_io_sync; + + /* i2c addresses */ + u8 i2c_sdp_io; + u8 i2c_sdp; + u8 i2c_cp; + u8 i2c_vdp; + u8 i2c_afe; + u8 i2c_hdmi; + u8 i2c_repeater; + u8 i2c_edid; + u8 i2c_infoframe; + u8 i2c_cec; + u8 i2c_avlink; +}; + +#define V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE (V4L2_CID_DV_CLASS_BASE + 0x1000) +#define V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL (V4L2_CID_DV_CLASS_BASE + 0x1001) +#define V4L2_CID_ADV_RX_FREE_RUN_COLOR (V4L2_CID_DV_CLASS_BASE + 0x1002) + +/* notify events */ +#define ADV7842_FMT_CHANGE 1 + +/* custom ioctl, used to test the external RAM that's used by the + * deinterlacer. */ +#define ADV7842_CMD_RAM_TEST _IO('V', BASE_VIDIOC_PRIVATE) + +#endif -- cgit v0.10.2 From 5a544cce2177fe361ba539db9ddaf1eff4e73f81 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Aug 2013 09:12:36 -0300 Subject: [media] adv7511: add new video encoder This is an Analog Devices HDMI transmitter. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 847b711..d18be19 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -429,6 +429,17 @@ config VIDEO_ADV7393 To compile this driver as a module, choose M here: the module will be called adv7393. +config VIDEO_ADV7511 + tristate "Analog Devices ADV7511 encoder" + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + ---help--- + Support for the Analog Devices ADV7511 video encoder. + + This is a Analog Devices HDMI transmitter. + + To compile this driver as a module, choose M here: the + module will be called adv7511. + config VIDEO_AD9389B tristate "Analog Devices AD9389B encoder" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index b4cf972..9f462df 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o +obj-$(CONFIG_VIDEO_ADV7511) += adv7511.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_VS6624) += vs6624.o obj-$(CONFIG_VIDEO_BT819) += bt819.o diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c new file mode 100644 index 0000000..7a576097 --- /dev/null +++ b/drivers/media/i2c/adv7511.c @@ -0,0 +1,1198 @@ +/* + * Analog Devices ADV7511 HDMI Transmitter Device Driver + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-2)"); + +MODULE_DESCRIPTION("Analog Devices ADV7511 HDMI Transmitter Device Driver"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + +#define MASK_ADV7511_EDID_RDY_INT 0x04 +#define MASK_ADV7511_MSEN_INT 0x40 +#define MASK_ADV7511_HPD_INT 0x80 + +#define MASK_ADV7511_HPD_DETECT 0x40 +#define MASK_ADV7511_MSEN_DETECT 0x20 +#define MASK_ADV7511_EDID_RDY 0x10 + +#define EDID_MAX_RETRIES (8) +#define EDID_DELAY 250 +#define EDID_MAX_SEGM 8 + +#define ADV7511_MAX_WIDTH 1920 +#define ADV7511_MAX_HEIGHT 1200 +#define ADV7511_MIN_PIXELCLOCK 20000000 +#define ADV7511_MAX_PIXELCLOCK 225000000 + +/* +********************************************************************** +* +* Arrays with configuration parameters for the ADV7511 +* +********************************************************************** +*/ + +struct i2c_reg_value { + unsigned char reg; + unsigned char value; +}; + +struct adv7511_state_edid { + /* total number of blocks */ + u32 blocks; + /* Number of segments read */ + u32 segments; + uint8_t data[EDID_MAX_SEGM * 256]; + /* Number of EDID read retries left */ + unsigned read_retries; + bool complete; +}; + +struct adv7511_state { + struct adv7511_platform_data pdata; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler hdl; + int chip_revision; + uint8_t i2c_edid_addr; + uint8_t i2c_cec_addr; + /* Is the adv7511 powered on? */ + bool power_on; + /* Did we receive hotplug and rx-sense signals? */ + bool have_monitor; + /* timings from s_dv_timings */ + struct v4l2_dv_timings dv_timings; + /* controls */ + struct v4l2_ctrl *hdmi_mode_ctrl; + struct v4l2_ctrl *hotplug_ctrl; + struct v4l2_ctrl *rx_sense_ctrl; + struct v4l2_ctrl *have_edid0_ctrl; + struct v4l2_ctrl *rgb_quantization_range_ctrl; + struct i2c_client *i2c_edid; + struct adv7511_state_edid edid; + /* Running counter of the number of detected EDIDs (for debugging) */ + unsigned edid_detect_counter; + struct workqueue_struct *work_queue; + struct delayed_work edid_handler; /* work entry */ +}; + +static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd); +static bool adv7511_check_edid_status(struct v4l2_subdev *sd); +static void adv7511_setup(struct v4l2_subdev *sd); +static int adv7511_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq); +static int adv7511_s_clock_freq(struct v4l2_subdev *sd, u32 freq); + + +static const struct v4l2_dv_timings_cap adv7511_timings_cap = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .max_width = ADV7511_MAX_WIDTH, + .max_height = ADV7511_MAX_HEIGHT, + .min_pixelclock = ADV7511_MIN_PIXELCLOCK, + .max_pixelclock = ADV7511_MAX_PIXELCLOCK, + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM, + }, +}; + +static inline struct adv7511_state *get_adv7511_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7511_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7511_state, hdl)->sd; +} + +/* ------------------------ I2C ----------------------------------------------- */ + +static s32 adv_smbus_read_byte_data_check(struct i2c_client *client, + u8 command, bool check) +{ + union i2c_smbus_data data; + + if (!i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data)) + return data.byte; + if (check) + v4l_err(client, "error reading %02x, %02x\n", + client->addr, command); + return -1; +} + +static s32 adv_smbus_read_byte_data(struct i2c_client *client, u8 command) +{ + int i; + for (i = 0; i < 3; i++) { + int ret = adv_smbus_read_byte_data_check(client, command, true); + if (ret >= 0) { + if (i) + v4l_err(client, "read ok after %d retries\n", i); + return ret; + } + } + v4l_err(client, "read failed\n"); + return -1; +} + +static int adv7511_rd(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return adv_smbus_read_byte_data(client, reg); +} + +static int adv7511_wr(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + int i; + + for (i = 0; i < 3; i++) { + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret == 0) + return 0; + } + v4l2_err(sd, "%s: i2c write error\n", __func__); + return ret; +} + +/* To set specific bits in the register, a clear-mask is given (to be AND-ed), + and then the value-mask (to be OR-ed). */ +static inline void adv7511_wr_and_or(struct v4l2_subdev *sd, u8 reg, uint8_t clr_mask, uint8_t val_mask) +{ + adv7511_wr(sd, reg, (adv7511_rd(sd, reg) & clr_mask) | val_mask); +} + +static int adv_smbus_read_i2c_block_data(struct i2c_client *client, + u8 command, unsigned length, u8 *values) +{ + union i2c_smbus_data data; + int ret; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + + ret = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); + memcpy(values, data.block + 1, length); + return ret; +} + +static inline void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf) +{ + struct adv7511_state *state = get_adv7511_state(sd); + int i; + int err = 0; + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) + err = adv_smbus_read_i2c_block_data(state->i2c_edid, i, + I2C_SMBUS_BLOCK_MAX, buf + i); + if (err) + v4l2_err(sd, "%s: i2c read error\n", __func__); +} + +static inline bool adv7511_have_hotplug(struct v4l2_subdev *sd) +{ + return adv7511_rd(sd, 0x42) & MASK_ADV7511_HPD_DETECT; +} + +static inline bool adv7511_have_rx_sense(struct v4l2_subdev *sd) +{ + return adv7511_rd(sd, 0x42) & MASK_ADV7511_MSEN_DETECT; +} + +static void adv7511_csc_conversion_mode(struct v4l2_subdev *sd, uint8_t mode) +{ + adv7511_wr_and_or(sd, 0x18, 0x9f, (mode & 0x3)<<5); +} + +static void adv7511_csc_coeff(struct v4l2_subdev *sd, + u16 A1, u16 A2, u16 A3, u16 A4, + u16 B1, u16 B2, u16 B3, u16 B4, + u16 C1, u16 C2, u16 C3, u16 C4) +{ + /* A */ + adv7511_wr_and_or(sd, 0x18, 0xe0, A1>>8); + adv7511_wr(sd, 0x19, A1); + adv7511_wr_and_or(sd, 0x1A, 0xe0, A2>>8); + adv7511_wr(sd, 0x1B, A2); + adv7511_wr_and_or(sd, 0x1c, 0xe0, A3>>8); + adv7511_wr(sd, 0x1d, A3); + adv7511_wr_and_or(sd, 0x1e, 0xe0, A4>>8); + adv7511_wr(sd, 0x1f, A4); + + /* B */ + adv7511_wr_and_or(sd, 0x20, 0xe0, B1>>8); + adv7511_wr(sd, 0x21, B1); + adv7511_wr_and_or(sd, 0x22, 0xe0, B2>>8); + adv7511_wr(sd, 0x23, B2); + adv7511_wr_and_or(sd, 0x24, 0xe0, B3>>8); + adv7511_wr(sd, 0x25, B3); + adv7511_wr_and_or(sd, 0x26, 0xe0, B4>>8); + adv7511_wr(sd, 0x27, B4); + + /* C */ + adv7511_wr_and_or(sd, 0x28, 0xe0, C1>>8); + adv7511_wr(sd, 0x29, C1); + adv7511_wr_and_or(sd, 0x2A, 0xe0, C2>>8); + adv7511_wr(sd, 0x2B, C2); + adv7511_wr_and_or(sd, 0x2C, 0xe0, C3>>8); + adv7511_wr(sd, 0x2D, C3); + adv7511_wr_and_or(sd, 0x2E, 0xe0, C4>>8); + adv7511_wr(sd, 0x2F, C4); +} + +static void adv7511_csc_rgb_full2limit(struct v4l2_subdev *sd, bool enable) +{ + if (enable) { + uint8_t csc_mode = 0; + adv7511_csc_conversion_mode(sd, csc_mode); + adv7511_csc_coeff(sd, + 4096-564, 0, 0, 256, + 0, 4096-564, 0, 256, + 0, 0, 4096-564, 256); + /* enable CSC */ + adv7511_wr_and_or(sd, 0x18, 0x7f, 0x80); + /* AVI infoframe: Limited range RGB (16-235) */ + adv7511_wr_and_or(sd, 0x57, 0xf3, 0x04); + } else { + /* disable CSC */ + adv7511_wr_and_or(sd, 0x18, 0x7f, 0x0); + /* AVI infoframe: Full range RGB (0-255) */ + adv7511_wr_and_or(sd, 0x57, 0xf3, 0x08); + } +} + +static void adv7511_set_IT_content_AVI_InfoFrame(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) { + /* CEA format, not IT */ + adv7511_wr_and_or(sd, 0x57, 0x7f, 0x00); + } else { + /* IT format */ + adv7511_wr_and_or(sd, 0x57, 0x7f, 0x80); + } +} + +static int adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl) +{ + switch (ctrl->val) { + default: + return -EINVAL; + break; + case V4L2_DV_RGB_RANGE_AUTO: { + /* automatic */ + struct adv7511_state *state = get_adv7511_state(sd); + + if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) { + /* cea format, RGB limited range (16-235) */ + adv7511_csc_rgb_full2limit(sd, true); + } else { + /* not cea format, RGB full range (0-255) */ + adv7511_csc_rgb_full2limit(sd, false); + } + } + break; + case V4L2_DV_RGB_RANGE_LIMITED: + /* RGB limited range (16-235) */ + adv7511_csc_rgb_full2limit(sd, true); + break; + case V4L2_DV_RGB_RANGE_FULL: + /* RGB full range (0-255) */ + adv7511_csc_rgb_full2limit(sd, false); + break; + } + return 0; +} + +/* ------------------------------ CTRL OPS ------------------------------ */ + +static int adv7511_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct adv7511_state *state = get_adv7511_state(sd); + + v4l2_dbg(1, debug, sd, "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val); + + if (state->hdmi_mode_ctrl == ctrl) { + /* Set HDMI or DVI-D */ + adv7511_wr_and_or(sd, 0xaf, 0xfd, ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00); + return 0; + } + if (state->rgb_quantization_range_ctrl == ctrl) + return adv7511_set_rgb_quantization_mode(sd, ctrl); + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops adv7511_ctrl_ops = { + .s_ctrl = adv7511_s_ctrl, +}; + +/* ---------------------------- CORE OPS ------------------------------------------- */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static void adv7511_inv_register(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "0x000-0x0ff: Main Map\n"); +} + +static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + reg->size = 1; + switch (reg->reg >> 8) { + case 0: + reg->val = adv7511_rd(sd, reg->reg & 0xff); + break; + default: + v4l2_info(sd, "Register %03llx not supported\n", reg->reg); + adv7511_inv_register(sd); + break; + } + return 0; +} + +static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) +{ + switch (reg->reg >> 8) { + case 0: + adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff); + break; + default: + v4l2_info(sd, "Register %03llx not supported\n", reg->reg); + adv7511_inv_register(sd); + break; + } + return 0; +} +#endif + +static int adv7511_log_status(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + struct adv7511_state_edid *edid = &state->edid; + + static const char * const states[] = { + "in reset", + "reading EDID", + "idle", + "initializing HDCP", + "HDCP enabled", + "initializing HDCP repeater", + "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" + }; + static const char * const errors[] = { + "no error", + "bad receiver BKSV", + "Ri mismatch", + "Pj mismatch", + "i2c error", + "timed out", + "max repeater cascade exceeded", + "hash check failed", + "too many devices", + "9", "A", "B", "C", "D", "E", "F" + }; + + v4l2_info(sd, "power %s\n", state->power_on ? "on" : "off"); + v4l2_info(sd, "%s hotplug, %s Rx Sense, %s EDID (%d block(s))\n", + (adv7511_rd(sd, 0x42) & MASK_ADV7511_HPD_DETECT) ? "detected" : "no", + (adv7511_rd(sd, 0x42) & MASK_ADV7511_MSEN_DETECT) ? "detected" : "no", + edid->segments ? "found" : "no", + edid->blocks); + v4l2_info(sd, "%s output %s\n", + (adv7511_rd(sd, 0xaf) & 0x02) ? + "HDMI" : "DVI-D", + (adv7511_rd(sd, 0xa1) & 0x3c) ? + "disabled" : "enabled"); + v4l2_info(sd, "state: %s, error: %s, detect count: %u, msk/irq: %02x/%02x\n", + states[adv7511_rd(sd, 0xc8) & 0xf], + errors[adv7511_rd(sd, 0xc8) >> 4], state->edid_detect_counter, + adv7511_rd(sd, 0x94), adv7511_rd(sd, 0x96)); + v4l2_info(sd, "RGB quantization: %s range\n", adv7511_rd(sd, 0x18) & 0x80 ? "limited" : "full"); + if (state->dv_timings.type == V4L2_DV_BT_656_1120) + v4l2_print_dv_timings(sd->name, "timings: ", + &state->dv_timings, false); + else + v4l2_info(sd, "no timings set\n"); + v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr); + v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr); + return 0; +} + +/* Power up/down adv7511 */ +static int adv7511_s_power(struct v4l2_subdev *sd, int on) +{ + struct adv7511_state *state = get_adv7511_state(sd); + const int retries = 20; + int i; + + v4l2_dbg(1, debug, sd, "%s: power %s\n", __func__, on ? "on" : "off"); + + state->power_on = on; + + if (!on) { + /* Power down */ + adv7511_wr_and_or(sd, 0x41, 0xbf, 0x40); + return true; + } + + /* Power up */ + /* The adv7511 does not always come up immediately. + Retry multiple times. */ + for (i = 0; i < retries; i++) { + adv7511_wr_and_or(sd, 0x41, 0xbf, 0x0); + if ((adv7511_rd(sd, 0x41) & 0x40) == 0) + break; + adv7511_wr_and_or(sd, 0x41, 0xbf, 0x40); + msleep(10); + } + if (i == retries) { + v4l2_dbg(1, debug, sd, "%s: failed to powerup the adv7511!\n", __func__); + adv7511_s_power(sd, 0); + return false; + } + if (i > 1) + v4l2_dbg(1, debug, sd, "%s: needed %d retries to powerup the adv7511\n", __func__, i); + + /* Reserved registers that must be set */ + adv7511_wr(sd, 0x98, 0x03); + adv7511_wr_and_or(sd, 0x9a, 0xfe, 0x70); + adv7511_wr(sd, 0x9c, 0x30); + adv7511_wr_and_or(sd, 0x9d, 0xfc, 0x01); + adv7511_wr(sd, 0xa2, 0xa4); + adv7511_wr(sd, 0xa3, 0xa4); + adv7511_wr(sd, 0xe0, 0xd0); + adv7511_wr(sd, 0xf9, 0x00); + + adv7511_wr(sd, 0x43, state->i2c_edid_addr); + + /* Set number of attempts to read the EDID */ + adv7511_wr(sd, 0xc9, 0xf); + return true; +} + +/* Enable interrupts */ +static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) +{ + uint8_t irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT; + uint8_t irqs_rd; + int retries = 100; + + v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable"); + + /* The datasheet says that the EDID ready interrupt should be + disabled if there is no hotplug. */ + if (!enable) + irqs = 0; + else if (adv7511_have_hotplug(sd)) + irqs |= MASK_ADV7511_EDID_RDY_INT; + + /* + * This i2c write can fail (approx. 1 in 1000 writes). But it + * is essential that this register is correct, so retry it + * multiple times. + * + * Note that the i2c write does not report an error, but the readback + * clearly shows the wrong value. + */ + do { + adv7511_wr(sd, 0x94, irqs); + irqs_rd = adv7511_rd(sd, 0x94); + } while (retries-- && irqs_rd != irqs); + + if (irqs_rd == irqs) + return; + v4l2_err(sd, "Could not set interrupts: hw failure?\n"); +} + +/* Interrupt handler */ +static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + uint8_t irq_status; + + /* disable interrupts to prevent a race condition */ + adv7511_set_isr(sd, false); + irq_status = adv7511_rd(sd, 0x96); + /* clear detected interrupts */ + adv7511_wr(sd, 0x96, irq_status); + + v4l2_dbg(1, debug, sd, "%s: irq 0x%x\n", __func__, irq_status); + + if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT)) + adv7511_check_monitor_present_status(sd); + if (irq_status & MASK_ADV7511_EDID_RDY_INT) + adv7511_check_edid_status(sd); + + /* enable interrupts */ + adv7511_set_isr(sd, true); + + if (handled) + *handled = true; + return 0; +} + +static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + if (edid->pad != 0) + return -EINVAL; + if ((edid->blocks == 0) || (edid->blocks > 256)) + return -EINVAL; + if (!edid->edid) + return -EINVAL; + if (!state->edid.segments) { + v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n"); + return -ENODATA; + } + if (edid->start_block >= state->edid.segments * 2) + return -E2BIG; + if ((edid->blocks + edid->start_block) >= state->edid.segments * 2) + edid->blocks = state->edid.segments * 2 - edid->start_block; + + memcpy(edid->edid, &state->edid.data[edid->start_block * 128], + 128 * edid->blocks); + return 0; +} + +static const struct v4l2_subdev_pad_ops adv7511_pad_ops = { + .get_edid = adv7511_get_edid, +}; + +static const struct v4l2_subdev_core_ops adv7511_core_ops = { + .log_status = adv7511_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = adv7511_g_register, + .s_register = adv7511_s_register, +#endif + .s_power = adv7511_s_power, + .interrupt_service_routine = adv7511_isr, +}; + +/* ------------------------------ VIDEO OPS ------------------------------ */ + +/* Enable/disable adv7511 output */ +static int adv7511_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); + adv7511_wr_and_or(sd, 0xa1, ~0x3c, (enable ? 0 : 0x3c)); + if (enable) { + adv7511_check_monitor_present_status(sd); + } else { + adv7511_s_power(sd, 0); + state->have_monitor = false; + } + return 0; +} + +static int adv7511_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + /* quick sanity check */ + if (!v4l2_valid_dv_timings(timings, &adv7511_timings_cap, NULL, NULL)) + return -EINVAL; + + /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings + if the format is one of the CEA or DMT timings. */ + v4l2_find_dv_timings_cap(timings, &adv7511_timings_cap, 0, NULL, NULL); + + timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS; + + /* save timings */ + state->dv_timings = *timings; + + /* update quantization range based on new dv_timings */ + adv7511_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl); + + /* update AVI infoframe */ + adv7511_set_IT_content_AVI_InfoFrame(sd); + + return 0; +} + +static int adv7511_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + if (!timings) + return -EINVAL; + + *timings = state->dv_timings; + + return 0; +} + +static int adv7511_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + return v4l2_enum_dv_timings_cap(timings, &adv7511_timings_cap, NULL, NULL); +} + +static int adv7511_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + *cap = adv7511_timings_cap; + return 0; +} + +static const struct v4l2_subdev_video_ops adv7511_video_ops = { + .s_stream = adv7511_s_stream, + .s_dv_timings = adv7511_s_dv_timings, + .g_dv_timings = adv7511_g_dv_timings, + .enum_dv_timings = adv7511_enum_dv_timings, + .dv_timings_cap = adv7511_dv_timings_cap, +}; + +/* ------------------------------ AUDIO OPS ------------------------------ */ +static int adv7511_s_audio_stream(struct v4l2_subdev *sd, int enable) +{ + v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); + + if (enable) + adv7511_wr_and_or(sd, 0x4b, 0x3f, 0x80); + else + adv7511_wr_and_or(sd, 0x4b, 0x3f, 0x40); + + return 0; +} + +static int adv7511_s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + u32 N; + + switch (freq) { + case 32000: N = 4096; break; + case 44100: N = 6272; break; + case 48000: N = 6144; break; + case 88200: N = 12544; break; + case 96000: N = 12288; break; + case 176400: N = 25088; break; + case 192000: N = 24576; break; + default: + return -EINVAL; + } + + /* Set N (used with CTS to regenerate the audio clock) */ + adv7511_wr(sd, 0x01, (N >> 16) & 0xf); + adv7511_wr(sd, 0x02, (N >> 8) & 0xff); + adv7511_wr(sd, 0x03, N & 0xff); + + return 0; +} + +static int adv7511_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + u32 i2s_sf; + + switch (freq) { + case 32000: i2s_sf = 0x30; break; + case 44100: i2s_sf = 0x00; break; + case 48000: i2s_sf = 0x20; break; + case 88200: i2s_sf = 0x80; break; + case 96000: i2s_sf = 0xa0; break; + case 176400: i2s_sf = 0xc0; break; + case 192000: i2s_sf = 0xe0; break; + default: + return -EINVAL; + } + + /* Set sampling frequency for I2S audio to 48 kHz */ + adv7511_wr_and_or(sd, 0x15, 0xf, i2s_sf); + + return 0; +} + +static int adv7511_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) +{ + /* Only 2 channels in use for application */ + adv7511_wr_and_or(sd, 0x73, 0xf8, 0x1); + /* Speaker mapping */ + adv7511_wr(sd, 0x76, 0x00); + + /* 16 bit audio word length */ + adv7511_wr_and_or(sd, 0x14, 0xf0, 0x02); + + return 0; +} + +static const struct v4l2_subdev_audio_ops adv7511_audio_ops = { + .s_stream = adv7511_s_audio_stream, + .s_clock_freq = adv7511_s_clock_freq, + .s_i2s_clock_freq = adv7511_s_i2s_clock_freq, + .s_routing = adv7511_s_routing, +}; + +/* --------------------- SUBDEV OPS --------------------------------------- */ + +static const struct v4l2_subdev_ops adv7511_ops = { + .core = &adv7511_core_ops, + .pad = &adv7511_pad_ops, + .video = &adv7511_video_ops, + .audio = &adv7511_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ +static void adv7511_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, int segment, uint8_t *buf) +{ + if (debug >= lvl) { + int i, j; + v4l2_dbg(lvl, debug, sd, "edid segment %d\n", segment); + for (i = 0; i < 256; i += 16) { + u8 b[128]; + u8 *bp = b; + if (i == 128) + v4l2_dbg(lvl, debug, sd, "\n"); + for (j = i; j < i + 16; j++) { + sprintf(bp, "0x%02x, ", buf[j]); + bp += 6; + } + bp[0] = '\0'; + v4l2_dbg(lvl, debug, sd, "%s\n", b); + } + } +} + +static void adv7511_edid_handler(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct adv7511_state *state = container_of(dwork, struct adv7511_state, edid_handler); + struct v4l2_subdev *sd = &state->sd; + struct adv7511_edid_detect ed; + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + if (adv7511_check_edid_status(sd)) { + /* Return if we received the EDID. */ + return; + } + + if (adv7511_have_hotplug(sd)) { + /* We must retry reading the EDID several times, it is possible + * that initially the EDID couldn't be read due to i2c errors + * (DVI connectors are particularly prone to this problem). */ + if (state->edid.read_retries) { + state->edid.read_retries--; + v4l2_dbg(1, debug, sd, "%s: edid read failed\n", __func__); + state->have_monitor = false; + adv7511_s_power(sd, false); + adv7511_s_power(sd, true); + queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); + return; + } + } + + /* We failed to read the EDID, so send an event for this. */ + ed.present = false; + ed.segment = adv7511_rd(sd, 0xc4); + v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); + v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__); +} + +static void adv7511_audio_setup(struct v4l2_subdev *sd) +{ + v4l2_dbg(1, debug, sd, "%s\n", __func__); + + adv7511_s_i2s_clock_freq(sd, 48000); + adv7511_s_clock_freq(sd, 48000); + adv7511_s_routing(sd, 0, 0, 0); +} + +/* Configure hdmi transmitter. */ +static void adv7511_setup(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + v4l2_dbg(1, debug, sd, "%s\n", __func__); + + /* Input format: RGB 4:4:4 */ + adv7511_wr_and_or(sd, 0x15, 0xf0, 0x0); + /* Output format: RGB 4:4:4 */ + adv7511_wr_and_or(sd, 0x16, 0x7f, 0x0); + /* 1st order interpolation 4:2:2 -> 4:4:4 up conversion, Aspect ratio: 16:9 */ + adv7511_wr_and_or(sd, 0x17, 0xf9, 0x06); + /* Disable pixel repetition */ + adv7511_wr_and_or(sd, 0x3b, 0x9f, 0x0); + /* Disable CSC */ + adv7511_wr_and_or(sd, 0x18, 0x7f, 0x0); + /* Output format: RGB 4:4:4, Active Format Information is valid, + * underscanned */ + adv7511_wr_and_or(sd, 0x55, 0x9c, 0x12); + /* AVI Info frame packet enable, Audio Info frame disable */ + adv7511_wr_and_or(sd, 0x44, 0xe7, 0x10); + /* Colorimetry, Active format aspect ratio: same as picure. */ + adv7511_wr(sd, 0x56, 0xa8); + /* No encryption */ + adv7511_wr_and_or(sd, 0xaf, 0xed, 0x0); + + /* Positive clk edge capture for input video clock */ + adv7511_wr_and_or(sd, 0xba, 0x1f, 0x60); + + adv7511_audio_setup(sd); + + v4l2_ctrl_handler_setup(&state->hdl); +} + +static void adv7511_notify_monitor_detect(struct v4l2_subdev *sd) +{ + struct adv7511_monitor_detect mdt; + struct adv7511_state *state = get_adv7511_state(sd); + + mdt.present = state->have_monitor; + v4l2_subdev_notify(sd, ADV7511_MONITOR_DETECT, (void *)&mdt); +} + +static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + /* read hotplug and rx-sense state */ + uint8_t status = adv7511_rd(sd, 0x42); + + v4l2_dbg(1, debug, sd, "%s: status: 0x%x%s%s\n", + __func__, + status, + status & MASK_ADV7511_HPD_DETECT ? ", hotplug" : "", + status & MASK_ADV7511_MSEN_DETECT ? ", rx-sense" : ""); + + /* update read only ctrls */ + v4l2_ctrl_s_ctrl(state->hotplug_ctrl, adv7511_have_hotplug(sd) ? 0x1 : 0x0); + v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, adv7511_have_rx_sense(sd) ? 0x1 : 0x0); + v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); + + if ((status & MASK_ADV7511_HPD_DETECT) && ((status & MASK_ADV7511_MSEN_DETECT) || state->edid.segments)) { + v4l2_dbg(1, debug, sd, "%s: hotplug and (rx-sense or edid)\n", __func__); + if (!state->have_monitor) { + v4l2_dbg(1, debug, sd, "%s: monitor detected\n", __func__); + state->have_monitor = true; + adv7511_set_isr(sd, true); + if (!adv7511_s_power(sd, true)) { + v4l2_dbg(1, debug, sd, "%s: monitor detected, powerup failed\n", __func__); + return; + } + adv7511_setup(sd); + adv7511_notify_monitor_detect(sd); + state->edid.read_retries = EDID_MAX_RETRIES; + queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); + } + } else if (status & MASK_ADV7511_HPD_DETECT) { + v4l2_dbg(1, debug, sd, "%s: hotplug detected\n", __func__); + state->edid.read_retries = EDID_MAX_RETRIES; + queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); + } else if (!(status & MASK_ADV7511_HPD_DETECT)) { + v4l2_dbg(1, debug, sd, "%s: hotplug not detected\n", __func__); + if (state->have_monitor) { + v4l2_dbg(1, debug, sd, "%s: monitor not detected\n", __func__); + state->have_monitor = false; + adv7511_notify_monitor_detect(sd); + } + adv7511_s_power(sd, false); + memset(&state->edid, 0, sizeof(struct adv7511_state_edid)); + } +} + +static bool edid_block_verify_crc(uint8_t *edid_block) +{ + int i; + uint8_t sum = 0; + + for (i = 0; i < 128; i++) + sum += *(edid_block + i); + return (sum == 0); +} + +static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment) +{ + struct adv7511_state *state = get_adv7511_state(sd); + u32 blocks = state->edid.blocks; + uint8_t *data = state->edid.data; + + if (edid_block_verify_crc(&data[segment * 256])) { + if ((segment + 1) * 2 <= blocks) + return edid_block_verify_crc(&data[segment * 256 + 128]); + return true; + } + return false; +} + +static bool adv7511_check_edid_status(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + uint8_t edidRdy = adv7511_rd(sd, 0xc5); + + v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n", + __func__, EDID_MAX_RETRIES - state->edid.read_retries); + + if (state->edid.complete) + return true; + + if (edidRdy & MASK_ADV7511_EDID_RDY) { + int segment = adv7511_rd(sd, 0xc4); + struct adv7511_edid_detect ed; + + if (segment >= EDID_MAX_SEGM) { + v4l2_err(sd, "edid segment number too big\n"); + return false; + } + v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment); + adv7511_edid_rd(sd, 256, &state->edid.data[segment * 256]); + adv7511_dbg_dump_edid(2, debug, sd, segment, &state->edid.data[segment * 256]); + if (segment == 0) { + state->edid.blocks = state->edid.data[0x7e] + 1; + v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", __func__, state->edid.blocks); + } + if (!edid_segment_verify_crc(sd, segment)) { + /* edid crc error, force reread of edid segment */ + v4l2_dbg(1, debug, sd, "%s: edid crc error\n", __func__); + state->have_monitor = false; + adv7511_s_power(sd, false); + adv7511_s_power(sd, true); + return false; + } + /* one more segment read ok */ + state->edid.segments = segment + 1; + if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) { + /* Request next EDID segment */ + v4l2_dbg(1, debug, sd, "%s: request segment %d\n", __func__, state->edid.segments); + adv7511_wr(sd, 0xc9, 0xf); + adv7511_wr(sd, 0xc4, state->edid.segments); + state->edid.read_retries = EDID_MAX_RETRIES; + queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); + return false; + } + + v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments); + state->edid.complete = true; + + /* report when we have all segments + but report only for segment 0 + */ + ed.present = true; + ed.segment = 0; + state->edid_detect_counter++; + v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); + v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); + return ed.present; + } + + return false; +} + +/* ----------------------------------------------------------------------- */ +/* Setup ADV7511 */ +static void adv7511_init_setup(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + struct adv7511_state_edid *edid = &state->edid; + + v4l2_dbg(1, debug, sd, "%s\n", __func__); + + /* clear all interrupts */ + adv7511_wr(sd, 0x96, 0xff); + memset(edid, 0, sizeof(struct adv7511_state_edid)); + state->have_monitor = false; + adv7511_set_isr(sd, false); + adv7511_s_stream(sd, false); + adv7511_s_audio_stream(sd, false); +} + +static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct adv7511_state *state; + struct adv7511_platform_data *pdata = client->dev.platform_data; + struct v4l2_ctrl_handler *hdl; + struct v4l2_subdev *sd; + u8 chip_id[2]; + int err = -EIO; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + state = devm_kzalloc(&client->dev, sizeof(struct adv7511_state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + /* Platform data */ + if (!pdata) { + v4l_err(client, "No platform data!\n"); + return -ENODEV; + } + memcpy(&state->pdata, pdata, sizeof(state->pdata)); + + sd = &state->sd; + + v4l2_dbg(1, debug, sd, "detecting adv7511 client on address 0x%x\n", + client->addr << 1); + + v4l2_i2c_subdev_init(sd, client, &adv7511_ops); + + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 10); + /* add in ascending ID order */ + state->hdmi_mode_ctrl = v4l2_ctrl_new_std_menu(hdl, &adv7511_ctrl_ops, + V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, + 0, V4L2_DV_TX_MODE_DVI_D); + state->hotplug_ctrl = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_DV_TX_HOTPLUG, 0, 1, 0, 0); + state->rx_sense_ctrl = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_DV_TX_RXSENSE, 0, 1, 0, 0); + state->have_edid0_ctrl = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_DV_TX_EDID_PRESENT, 0, 1, 0, 0); + state->rgb_quantization_range_ctrl = + v4l2_ctrl_new_std_menu(hdl, &adv7511_ctrl_ops, + V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, + 0, V4L2_DV_RGB_RANGE_AUTO); + sd->ctrl_handler = hdl; + if (hdl->error) { + err = hdl->error; + goto err_hdl; + } + state->hdmi_mode_ctrl->is_private = true; + state->hotplug_ctrl->is_private = true; + state->rx_sense_ctrl->is_private = true; + state->have_edid0_ctrl->is_private = true; + state->rgb_quantization_range_ctrl->is_private = true; + + state->pad.flags = MEDIA_PAD_FL_SINK; + err = media_entity_init(&sd->entity, 1, &state->pad, 0); + if (err) + goto err_hdl; + + /* EDID and CEC i2c addr */ + state->i2c_edid_addr = state->pdata.i2c_edid << 1; + state->i2c_cec_addr = state->pdata.i2c_cec << 1; + + state->chip_revision = adv7511_rd(sd, 0x0); + chip_id[0] = adv7511_rd(sd, 0xf5); + chip_id[1] = adv7511_rd(sd, 0xf6); + if (chip_id[0] != 0x75 || chip_id[1] != 0x11) { + v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0], chip_id[1]); + err = -EIO; + goto err_entity; + } + + state->i2c_edid = i2c_new_dummy(client->adapter, state->i2c_edid_addr >> 1); + if (state->i2c_edid == NULL) { + v4l2_err(sd, "failed to register edid i2c client\n"); + goto err_entity; + } + + adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */ + state->work_queue = create_singlethread_workqueue(sd->name); + if (state->work_queue == NULL) { + v4l2_err(sd, "could not create workqueue\n"); + goto err_unreg_cec; + } + + INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler); + + adv7511_init_setup(sd); + adv7511_set_isr(sd, true); + adv7511_check_monitor_present_status(sd); + + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + return 0; + +err_unreg_cec: + i2c_unregister_device(state->i2c_edid); +err_entity: + media_entity_cleanup(&sd->entity); +err_hdl: + v4l2_ctrl_handler_free(&state->hdl); + return err; +} + +/* ----------------------------------------------------------------------- */ + +static int adv7511_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7511_state *state = get_adv7511_state(sd); + + state->chip_revision = -1; + + v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + + adv7511_init_setup(sd); + cancel_delayed_work(&state->edid_handler); + i2c_unregister_device(state->i2c_edid); + destroy_workqueue(state->work_queue); + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_device_id adv7511_id[] = { + { "adv7511", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7511_id); + +static struct i2c_driver adv7511_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7511", + }, + .probe = adv7511_probe, + .remove = adv7511_remove, + .id_table = adv7511_id, +}; + +module_i2c_driver(adv7511_driver); diff --git a/include/media/adv7511.h b/include/media/adv7511.h new file mode 100644 index 0000000..bb78bed --- /dev/null +++ b/include/media/adv7511.h @@ -0,0 +1,48 @@ +/* + * Analog Devices ADV7511 HDMI Transmitter Device Driver + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * 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. + */ + +#ifndef ADV7511_H +#define ADV7511_H + +/* notify events */ +#define ADV7511_MONITOR_DETECT 0 +#define ADV7511_EDID_DETECT 1 + + +struct adv7511_monitor_detect { + int present; +}; + +struct adv7511_edid_detect { + int present; + int segment; +}; + +struct adv7511_cec_arg { + void *arg; + u32 f_flags; +}; + +struct adv7511_platform_data { + uint8_t i2c_edid; + uint8_t i2c_cec; + uint32_t cec_clk; +}; + +#endif -- cgit v0.10.2 From c40ddfa34addb80e2adf42751dd8a010cc4c5403 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 12 Aug 2013 10:02:48 -0300 Subject: [media] MAINTAINERS: add entries for adv7511 and adv7842 Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 9b12947..b2618ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -580,12 +580,24 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/ad9389b* +ANALOG DEVICES INC ADV7511 DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/adv7511* + ANALOG DEVICES INC ADV7604 DRIVER M: Hans Verkuil L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/adv7604* +ANALOG DEVICES INC ADV7842 DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/adv7842* + ANALOG DEVICES INC ASOC CODEC DRIVERS M: Lars-Peter Clausen L: device-drivers-devel@blackfin.uclinux.org -- cgit v0.10.2 From 9d3e976bb1ea736b6154fafac4957b85360dad93 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 22 Aug 2013 22:59:44 -0300 Subject: [media] davinci: vpif_display: fix error return code in vpif_probe() Fix to return -ENODEV in the subdevice register error handling case instead of 0, as done elsewhere in this function. Introduce by commit 4b8a531e6bb0686203e9cf82a54dfe189de7d5c2. ([media] media: davinci: vpif: display: add V4L2-async support) Signed-off-by: Wei Yongjun Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index f2b4ae1..c31bcf1 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1818,6 +1818,7 @@ static __init int vpif_probe(struct platform_device *pdev) NULL); if (!vpif_obj.sd[i]) { vpif_err("Error registering v4l2 subdevice\n"); + err = -ENODEV; goto probe_subdev_out; } -- cgit v0.10.2 From 43054ecced8ae77c805470447d72da4fdc276e02 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 22 Aug 2013 23:00:01 -0300 Subject: [media] davinci: vpif_capture: fix error return code in vpif_probe() Fix to return -ENODEV in the subdevice register error handling case instead of 0, as done elsewhere in this function. Introduced by commit 873229e4fdf34196aa5d707957c59ba54c25eaba ([media] media: davinci: vpif: capture: add V4L2-async support) Signed-off-by: Wei Yongjun Acked-by: Lad, Prabhakar 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 b79280f..1089834 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -2154,6 +2154,7 @@ static __init int vpif_probe(struct platform_device *pdev) if (!vpif_obj.sd[i]) { vpif_err("Error registering v4l2 subdevice\n"); + err = -ENOMEM; goto probe_subdev_out; } v4l2_info(&vpif_obj.v4l2_dev, -- cgit v0.10.2 From 10d79b995be5399be0a59a72859ac4bfdf066299 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 22 Aug 2013 14:11:47 -0300 Subject: [media] v4l: vsp1: Initialize media device bus_info field Fill bus_info with the VSP1 platform device name Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index ff8cd2d..aa8b9b2 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -134,6 +134,8 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) mdev->dev = vsp1->dev; strlcpy(mdev->model, "VSP1", sizeof(mdev->model)); + snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", + dev_name(mdev->dev)); ret = media_device_register(mdev); if (ret < 0) { dev_err(vsp1->dev, "media device registration failed (%d)\n", -- cgit v0.10.2 From f2226a33bf9a431f616b435aa10a9ad4fea1d042 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 22 Aug 2013 14:29:46 -0300 Subject: [media] v4l: vsp1: Add support for RT clock The VSPR and VSPS instances use two clocks, the VSP1 system clock and the VSP1 realtime clock. Both of them need to be enabled to access the VSP1 registers. Add support for an optional RT clock and enable/disable it along with the system clock. 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 11ac94b..d6c6ecd 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -42,6 +42,7 @@ struct vsp1_device { void __iomem *mmio; struct clk *clock; + struct clk *rt_clock; struct mutex lock; int ref_count; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index aa8b9b2..1c9e771 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -290,6 +290,33 @@ static int vsp1_device_init(struct vsp1_device *vsp1) return 0; } +static int vsp1_clocks_enable(struct vsp1_device *vsp1) +{ + int ret; + + ret = clk_prepare_enable(vsp1->clock); + if (ret < 0) + return ret; + + if (IS_ERR(vsp1->rt_clock)) + return 0; + + ret = clk_prepare_enable(vsp1->rt_clock); + if (ret < 0) { + clk_disable_unprepare(vsp1->clock); + return ret; + } + + return 0; +} + +static void vsp1_clocks_disable(struct vsp1_device *vsp1) +{ + if (!IS_ERR(vsp1->rt_clock)) + clk_disable_unprepare(vsp1->rt_clock); + clk_disable_unprepare(vsp1->clock); +} + /* * vsp1_device_get - Acquire the VSP1 device * @@ -307,7 +334,7 @@ struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1) if (vsp1->ref_count > 0) goto done; - ret = clk_prepare_enable(vsp1->clock); + ret = vsp1_clocks_enable(vsp1); if (ret < 0) { __vsp1 = NULL; goto done; @@ -315,7 +342,7 @@ struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1) ret = vsp1_device_init(vsp1); if (ret < 0) { - clk_disable_unprepare(vsp1->clock); + vsp1_clocks_disable(vsp1); __vsp1 = NULL; goto done; } @@ -339,7 +366,7 @@ void vsp1_device_put(struct vsp1_device *vsp1) mutex_lock(&vsp1->lock); if (--vsp1->ref_count == 0) - clk_disable_unprepare(vsp1->clock); + vsp1_clocks_disable(vsp1); mutex_unlock(&vsp1->lock); } @@ -358,7 +385,7 @@ static int vsp1_pm_suspend(struct device *dev) if (vsp1->ref_count == 0) return 0; - clk_disable_unprepare(vsp1->clock); + vsp1_clocks_disable(vsp1); return 0; } @@ -371,7 +398,7 @@ static int vsp1_pm_resume(struct device *dev) if (vsp1->ref_count) return 0; - return clk_prepare_enable(vsp1->clock); + return vsp1_clocks_enable(vsp1); } #endif @@ -445,6 +472,9 @@ static int vsp1_probe(struct platform_device *pdev) return PTR_ERR(vsp1->clock); } + /* The RT clock is optional */ + vsp1->rt_clock = devm_clk_get(&pdev->dev, "rt"); + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq) { dev_err(&pdev->dev, "missing IRQ\n"); -- cgit v0.10.2 From 26a20eb09d44dc064c4f5d1f024bd501c09edb4b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 22 Aug 2013 19:51:01 -0300 Subject: [media] v4l: vsp1: Fix mutex double lock at streamon time A mutex_lock() was left when the driver was converted to use the vb2 ioctl helpers, resulting in a deadlock at streamon time. Fix it. 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 f51f842..714c53e 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -839,8 +839,6 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) struct vsp1_pipeline *pipe; int ret; - mutex_lock(&video->lock); - if (video->queue.owner && video->queue.owner != file->private_data) return -EBUSY; -- cgit v0.10.2 From 19c2813c2882c7ecc6e1e8813f54ace495cfbe9a Mon Sep 17 00:00:00 2001 From: John Sheu Date: Tue, 3 Sep 2013 08:26:42 -0300 Subject: [media] s5p-mfc: Fix input/output format reporting The video encode/decode paths have duplicated logic between VIDIOC_TRY_FMT and VIDIOC_S_FMT that should be de-duped. Also, video decode reports V4L2_PIX_FMT_NV12MT_16X16 output format, regardless of what the actual output has been set at. Fix this. Signed-off-by: John Sheu Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index e2f2212..5c764f9 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -344,7 +344,7 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_mp->num_planes = 2; /* Set pixelformat to the format in which MFC outputs the decoded frame */ - pix_mp->pixelformat = V4L2_PIX_FMT_NV12MT; + pix_mp->pixelformat = ctx->dst_fmt->fourcc; pix_mp->plane_fmt[0].bytesperline = ctx->buf_width; pix_mp->plane_fmt[0].sizeimage = ctx->luma_size; pix_mp->plane_fmt[1].bytesperline = ctx->buf_width; @@ -382,10 +382,20 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("Unsupported format for source.\n"); return -EINVAL; } + if (fmt->codec_mode == S5P_FIMV_CODEC_NONE) { + mfc_err("Unknown codec\n"); + return -EINVAL; + } if (!IS_MFCV6_PLUS(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) { mfc_err("Not supported format.\n"); return -EINVAL; } + if (!IS_MFCV6_PLUS(dev)) { + if (fmt->fourcc == V4L2_PIX_FMT_VP8) { + mfc_err("Not supported format.\n"); + return -EINVAL; + } + } } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { fmt = find_format(f, MFC_FMT_RAW); if (!fmt) { @@ -412,7 +422,6 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) struct s5p_mfc_dev *dev = video_drvdata(file); struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); int ret = 0; - struct s5p_mfc_fmt *fmt; struct v4l2_pix_format_mplane *pix_mp; mfc_debug_enter(); @@ -426,55 +435,32 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) goto out; } if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - fmt = find_format(f, MFC_FMT_RAW); - if (!fmt) { - mfc_err("Unsupported format for source.\n"); - return -EINVAL; - } - if (!IS_MFCV6_PLUS(dev) && - (fmt->fourcc != V4L2_PIX_FMT_NV12MT)) { - mfc_err("Not supported format.\n"); - return -EINVAL; - } else if (IS_MFCV6_PLUS(dev) && - (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) { - mfc_err("Not supported format.\n"); - return -EINVAL; - } - ctx->dst_fmt = fmt; - mfc_debug_leave(); - return ret; - } else if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - mfc_err("Wrong type error for S_FMT : %d", f->type); - return -EINVAL; - } - fmt = find_format(f, MFC_FMT_DEC); - if (!fmt || fmt->codec_mode == S5P_MFC_CODEC_NONE) { - mfc_err("Unknown codec\n"); - ret = -EINVAL; + /* dst_fmt is validated by call to vidioc_try_fmt */ + ctx->dst_fmt = find_format(f, MFC_FMT_RAW); + ret = 0; goto out; - } - if (fmt->type != MFC_FMT_DEC) { - mfc_err("Wrong format selected, you should choose " - "format for decoding\n"); + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + /* src_fmt is validated by call to vidioc_try_fmt */ + ctx->src_fmt = find_format(f, MFC_FMT_DEC); + ctx->codec_mode = ctx->src_fmt->codec_mode; + 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 + pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size = + DEF_CPB_SIZE; + pix_mp->plane_fmt[0].bytesperline = 0; + ctx->state = MFCINST_INIT; + ret = 0; + goto out; + } else { + mfc_err("Wrong type error for S_FMT : %d", f->type); ret = -EINVAL; goto out; } - if (!IS_MFCV6_PLUS(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) { - mfc_err("Not supported format.\n"); - return -EINVAL; - } - ctx->src_fmt = fmt; - ctx->codec_mode = fmt->codec_mode; - 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 - pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size = - DEF_CPB_SIZE; - pix_mp->plane_fmt[0].bytesperline = 0; - ctx->state = MFCINST_INIT; + out: mfc_debug_leave(); return ret; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index fb077b3..41f5a3c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -967,6 +967,7 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { + struct s5p_mfc_dev *dev = video_drvdata(file); struct s5p_mfc_fmt *fmt; struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; @@ -977,6 +978,11 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) return -EINVAL; } + if (!IS_MFCV7(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) { + mfc_err("VP8 is supported only in MFC v7\n"); + return -EINVAL; + } + if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { mfc_err("must be set encoding output size\n"); return -EINVAL; @@ -991,6 +997,18 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) return -EINVAL; } + if (!IS_MFCV6_PLUS(dev)) { + if (fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) { + mfc_err("Not supported format.\n"); + return -EINVAL; + } + } else if (IS_MFCV6_PLUS(dev)) { + if (fmt->fourcc == V4L2_PIX_FMT_NV12MT) { + mfc_err("Not supported format.\n"); + return -EINVAL; + } + } + if (fmt->num_planes != pix_fmt_mp->num_planes) { mfc_err("failed to try output format\n"); return -EINVAL; @@ -1008,7 +1026,6 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct s5p_mfc_dev *dev = video_drvdata(file); struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); - struct s5p_mfc_fmt *fmt; struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; int ret = 0; @@ -1021,17 +1038,9 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) goto out; } if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - fmt = find_format(f, MFC_FMT_ENC); - if (!fmt) { - mfc_err("failed to set capture format\n"); - return -EINVAL; - } - if (!IS_MFCV7(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) { - mfc_err("VP8 is supported only in MFC v7\n"); - return -EINVAL; - } + /* dst_fmt is validated by call to vidioc_try_fmt */ + ctx->dst_fmt = find_format(f, MFC_FMT_ENC); ctx->state = MFCINST_INIT; - ctx->dst_fmt = fmt; ctx->codec_mode = ctx->dst_fmt->codec_mode; ctx->enc_dst_buf_size = pix_fmt_mp->plane_fmt[0].sizeimage; pix_fmt_mp->plane_fmt[0].bytesperline = 0; @@ -1052,28 +1061,8 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) } mfc_debug(2, "Got instance number: %d\n", ctx->inst_no); } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - fmt = find_format(f, MFC_FMT_RAW); - if (!fmt) { - mfc_err("failed to set output format\n"); - return -EINVAL; - } - - if (!IS_MFCV6_PLUS(dev) && - (fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16)) { - mfc_err("Not supported format.\n"); - return -EINVAL; - } else if (IS_MFCV6_PLUS(dev) && - (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) { - mfc_err("Not supported format.\n"); - return -EINVAL; - } - - if (fmt->num_planes != pix_fmt_mp->num_planes) { - mfc_err("failed to set output format\n"); - ret = -EINVAL; - goto out; - } - ctx->src_fmt = fmt; + /* src_fmt is validated by call to vidioc_try_fmt */ + ctx->src_fmt = find_format(f, MFC_FMT_RAW); ctx->img_width = pix_fmt_mp->width; ctx->img_height = pix_fmt_mp->height; mfc_debug(2, "codec number: %d\n", ctx->src_fmt->codec_mode); -- cgit v0.10.2 From 7b0dd9e60e714951b5400dd0740b3c4c3c3cb76f Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 15 Jun 2013 08:09:57 -0300 Subject: [media] media: coda: Fix DT driver data pointer for i.MX27 The data pointer should point to DT data, and not to the ID array. Signed-off-by: Alexander Shiyan Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 999f9dc..449d2fe 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -3109,7 +3109,7 @@ MODULE_DEVICE_TABLE(platform, coda_platform_ids); #ifdef CONFIG_OF static const struct of_device_id coda_dt_ids[] = { - { .compatible = "fsl,imx27-vpu", .data = &coda_platform_ids[CODA_IMX27] }, + { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, { /* sentinel */ } }; -- cgit v0.10.2 From 8a09a4cc9bd9389dc6a3b5b2dd3a7d64d2fab7e1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 15 Jul 2013 02:36:23 -0300 Subject: [media] s5p-g2d: Fix registration failure Commit 1c1d86a1ea ("[media] v4l2: always require v4l2_dev, rename parent to dev_parent") expects v4l2_dev to be always set. It converted most of the drivers using the parent field of video_device to v4l2_dev field. G2D driver did not set the parent field. Hence it got left out. Without this patch we get the following boot warning and G2D driver fails to register the video device. WARNING: CPU: 0 PID: 1 at drivers/media/v4l2-core/v4l2-dev.c:775 __video_register_device+0xfc0/0x1028() Modules linked in: CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.11.0-rc1-00001-g1c3e372-dirty #9 [] (unwind_backtrace+0x0/0xf4) from [] (show_stack+0x10/0x14) [] (show_stack+0x10/0x14) from [] (dump_stack+0x7c/0xb0) [] (dump_stack+0x7c/0xb0) from [] (warn_slowpath_common+0x6c/0x88) [] (warn_slowpath_common+0x6c/0x88) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null+0x1c/0x24) from [] (__video_register_device+0xfc0/0x1028) [] (__video_register_device+0xfc0/0x1028) from [] (g2d_probe+0x1f8/0x398) [] (g2d_probe+0x1f8/0x398) from [] (platform_drv_probe+0x14/0x18) [] (platform_drv_probe+0x14/0x18) from [] (driver_probe_device+0x108/0x220) [] (driver_probe_device+0x108/0x220) from [] (__driver_attach+0x8c/0x90) [] (__driver_attach+0x8c/0x90) from [] (bus_for_each_dev+0x60/0x94) [] (bus_for_each_dev+0x60/0x94) from [] (bus_add_driver+0x1c0/0x24c) [] (bus_add_driver+0x1c0/0x24c) from [] (driver_register+0x78/0x140) [] (driver_register+0x78/0x140) from [] (do_one_initcall+0xf8/0x144) [] (do_one_initcall+0xf8/0x144) from [] (kernel_init_freeable+0x13c/0x1d8) [] (kernel_init_freeable+0x13c/0x1d8) from [] (kernel_init+0xc/0x160) [] (kernel_init+0xc/0x160) from [] (ret_from_fork+0x14/0x3c) ---[ end trace 4e0ec028b0028e02 ]--- s5p-g2d 12800000.g2d: Failed to register video device s5p-g2d: probe of 12800000.g2d failed with error -22 Signed-off-by: Sachin Kamat Cc: Hans Verkuil Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 553d87e..fd6289d 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -784,6 +784,7 @@ static int g2d_probe(struct platform_device *pdev) } *vfd = g2d_videodev; vfd->lock = &dev->mutex; + vfd->v4l2_dev = &dev->v4l2_dev; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); -- cgit v0.10.2 From 037f4e6b6be47dcfb3f4e78e5c6abe7da92da1dd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 30 Jun 2013 04:40:32 -0300 Subject: [media] ml86v7667: fix compile warning: 'ret' set but not used media_build/v4l/ml86v7667.c: In function 'ml86v7667_s_ctrl': media_build/v4l/ml86v7667.c:120:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] int ret; ^ And indeed, ret is set but not used. Let's actually return the error code. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index dd1e6c9..a9110d8 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -117,7 +117,7 @@ static int ml86v7667_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; + int ret = -EINVAL; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -157,7 +157,7 @@ static int ml86v7667_s_ctrl(struct v4l2_ctrl *ctrl) break; } - return 0; + return ret; } static int ml86v7667_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) -- cgit v0.10.2 From 8bfd4a68ecc003c1a142f35551be846d6b13e822 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Fri, 28 Jun 2013 05:34:20 -0300 Subject: [media] DocBook: upgrade media_api DocBook version to 4.2 Fixes the last three errors of media_api DocBook validatation: (...) media_api.xml:414: element imagedata: validity error : Value "SVG" for attribute format of imagedata is not among the enumerated set media_api.xml:432: element imagedata: validity error : Value "SVG" for attribute format of imagedata is not among the enumerated set media_api.xml:452: element imagedata: validity error : Value "SVG" for attribute format of imagedata is not among the enumerated set (...) Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl index 07e7eea..4c8d282 100644 --- a/Documentation/DocBook/media_api.tmpl +++ b/Documentation/DocBook/media_api.tmpl @@ -1,6 +1,6 @@ - %media-entities; -- cgit v0.10.2 From a19dec6ea94c036af68c31930c1c92681f55af41 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Fri, 28 Jun 2013 05:44:22 -0300 Subject: [media] v4l2: added missing mutex.h include to v4l2-ctrls.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes following error: include/media/v4l2-ctrls.h:193:15: error: field ‘_lock’ has incomplete type include/media/v4l2-ctrls.h: In function ‘v4l2_ctrl_lock’: include/media/v4l2-ctrls.h:570:2: error: implicit declaration of function ‘mutex_lock’ [-Werror=implicit-function-declaration] include/media/v4l2-ctrls.h: In function ‘v4l2_ctrl_unlock’: include/media/v4l2-ctrls.h:579:2: error: implicit declaration of function ‘mutex_unlock’ [-Werror=implicit-function-declaration] Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 7343a27..47ada23 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -22,6 +22,7 @@ #define _V4L2_CTRLS_H #include +#include #include /* forward references */ -- cgit v0.10.2 From 3080072460bb0efd156e415bfea783279bf64e92 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 2 Jul 2013 07:56:38 -0300 Subject: [media] usbtv: Fix deinterlacing The image data is laid out a bit more weirdly and thus needs more work to properly interlace. What we get from hardware is V4L2_FIELD_ALTERNATE, but since userspace support for it is practically nonexistent, thus we make V4L2_FIELD_INTERLACED from it so that it's more easily interpreted. Signed-off-by: Lubomir Rintel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/drivers/media/usb/usbtv/usbtv.c b/drivers/media/usb/usbtv/usbtv.c index 0952316..55a45d34 100644 --- a/drivers/media/usb/usbtv/usbtv.c +++ b/drivers/media/usb/usbtv/usbtv.c @@ -56,7 +56,7 @@ #define USBTV_CHUNK_SIZE 256 #define USBTV_CHUNK 240 #define USBTV_CHUNKS (USBTV_WIDTH * USBTV_HEIGHT \ - / 2 / USBTV_CHUNK) + / 4 / USBTV_CHUNK) /* Chunk header. */ #define USBTV_MAGIC_OK(chunk) ((be32_to_cpu(chunk[0]) & 0xff000000) \ @@ -259,6 +259,26 @@ static int usbtv_setup_capture(struct usbtv *usbtv) return 0; } +/* Copy data from chunk into a frame buffer, deinterlacing the data + * into every second line. Unfortunately, they don't align nicely into + * 720 pixel lines, as the chunk is 240 words long, which is 480 pixels. + * Therefore, we break down the chunk into two halves before copyting, + * so that we can interleave a line if needed. */ +static void usbtv_chunk_to_vbuf(u32 *frame, u32 *src, int chunk_no, int odd) +{ + int half; + + for (half = 0; half < 2; half++) { + int part_no = chunk_no * 2 + half; + int line = part_no / 3; + int part_index = (line * 2 + !odd) * 3 + (part_no % 3); + + u32 *dst = &frame[part_index * USBTV_CHUNK/2]; + memcpy(dst, src, USBTV_CHUNK/2 * sizeof(*src)); + src += USBTV_CHUNK/2; + } +} + /* Called for each 256-byte image chunk. * First word identifies the chunk, followed by 240 words of image * data and padding. */ @@ -275,11 +295,6 @@ static void usbtv_image_chunk(struct usbtv *usbtv, u32 *chunk) frame_id = USBTV_FRAME_ID(chunk); odd = USBTV_ODD(chunk); chunk_no = USBTV_CHUNK_NO(chunk); - - /* Deinterlace. TODO: Use interlaced frame format. */ - chunk_no = (chunk_no - chunk_no % 3) * 2 + chunk_no % 3; - chunk_no += !odd * 3; - if (chunk_no >= USBTV_CHUNKS) return; @@ -298,12 +313,11 @@ static void usbtv_image_chunk(struct usbtv *usbtv, u32 *chunk) buf = list_first_entry(&usbtv->bufs, struct usbtv_buf, list); frame = vb2_plane_vaddr(&buf->vb, 0); - /* Copy the chunk. */ - memcpy(&frame[chunk_no * USBTV_CHUNK], &chunk[1], - USBTV_CHUNK * sizeof(chunk[1])); + /* Copy the chunk data. */ + usbtv_chunk_to_vbuf(frame, &chunk[1], chunk_no, odd); /* Last chunk in a frame, signalling an end */ - if (usbtv->frame_id && chunk_no == USBTV_CHUNKS-1) { + if (odd && chunk_no == USBTV_CHUNKS-1) { int size = vb2_plane_size(&buf->vb, 0); buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; @@ -582,7 +596,7 @@ static int usbtv_queue_setup(struct vb2_queue *vq, if (*nbuffers < 2) *nbuffers = 2; *nplanes = 1; - sizes[0] = USBTV_CHUNK * USBTV_CHUNKS * sizeof(u32); + sizes[0] = USBTV_WIDTH * USBTV_HEIGHT / 2 * sizeof(u32); return 0; } -- cgit v0.10.2 From 6ee0faa52676ab648e486afb654fc92313d55943 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 2 Jul 2013 07:56:39 -0300 Subject: [media] usbtv: Throw corrupted frames away Ignore out of order data and mark incomplete buffers as errored. This gets rid of annoying flicker due to occassional garbage from hardware. Signed-off-by: Lubomir Rintel Cc: Hans Verkuil Cc: Mauro Carvalho Chehab Cc: linux-kernel@vger.kernel.org Cc: linux-media@vger.kernel.org Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/drivers/media/usb/usbtv/usbtv.c b/drivers/media/usb/usbtv/usbtv.c index 55a45d34..8a505a9 100644 --- a/drivers/media/usb/usbtv/usbtv.c +++ b/drivers/media/usb/usbtv/usbtv.c @@ -88,6 +88,7 @@ struct usbtv { /* Number of currently processed frame, useful find * out when a new one begins. */ u32 frame_id; + int chunks_done; enum { USBTV_COMPOSITE_INPUT, @@ -299,8 +300,13 @@ static void usbtv_image_chunk(struct usbtv *usbtv, u32 *chunk) return; /* Beginning of a frame. */ - if (chunk_no == 0) + if (chunk_no == 0) { usbtv->frame_id = frame_id; + usbtv->chunks_done = 0; + } + + if (usbtv->frame_id != frame_id) + return; spin_lock_irqsave(&usbtv->buflock, flags); if (list_empty(&usbtv->bufs)) { @@ -315,16 +321,21 @@ static void usbtv_image_chunk(struct usbtv *usbtv, u32 *chunk) /* Copy the chunk data. */ usbtv_chunk_to_vbuf(frame, &chunk[1], chunk_no, odd); + usbtv->chunks_done++; /* Last chunk in a frame, signalling an end */ if (odd && chunk_no == USBTV_CHUNKS-1) { int size = vb2_plane_size(&buf->vb, 0); + enum vb2_buffer_state state = usbtv->chunks_done == + USBTV_CHUNKS ? + VB2_BUF_STATE_DONE : + VB2_BUF_STATE_ERROR; buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; buf->vb.v4l2_buf.sequence = usbtv->sequence++; v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); vb2_set_plane_payload(&buf->vb, 0, size); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&buf->vb, state); list_del(&buf->list); } -- cgit v0.10.2 From 2e923a0527ac439e135b9961e58d3acd876bba10 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Wed, 3 Jul 2013 16:17:34 -0300 Subject: [media] hdpvr: fix iteration over uninitialized lists in hdpvr_probe() free_buff_list and rec_buff_list are initialized in the middle of hdpvr_probe(), but if something bad happens before that, error handling code calls hdpvr_delete(), which contains iteration over the lists (via hdpvr_free_buffers()). The patch moves the lists initialization to the beginning and by the way fixes goto label in error handling of registering videodev. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c index cb69405..6e50707 100644 --- a/drivers/media/usb/hdpvr/hdpvr-core.c +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -303,6 +303,11 @@ static int hdpvr_probe(struct usb_interface *interface, dev->workqueue = 0; + /* init video transfer queues first of all */ + /* to prevent oops in hdpvr_delete() on error paths */ + INIT_LIST_HEAD(&dev->free_buff_list); + INIT_LIST_HEAD(&dev->rec_buff_list); + /* register v4l2_device early so it can be used for printks */ if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) { dev_err(&interface->dev, "v4l2_device_register failed\n"); @@ -325,10 +330,6 @@ static int hdpvr_probe(struct usb_interface *interface, if (!dev->workqueue) goto error; - /* init video transfer queues */ - INIT_LIST_HEAD(&dev->free_buff_list); - INIT_LIST_HEAD(&dev->rec_buff_list); - dev->options = hdpvr_default_options; if (default_video_input < HDPVR_VIDEO_INPUTS) @@ -405,7 +406,7 @@ static int hdpvr_probe(struct usb_interface *interface, video_nr[atomic_inc_return(&dev_nr)]); if (retval < 0) { v4l2_err(&dev->v4l2_dev, "registering videodev failed\n"); - goto error; + goto reg_fail; } /* let the user know what node this device is now attached to */ -- cgit v0.10.2 From 32bf7c6cdf63a6704822c81b7139c54ac1cbd6a1 Mon Sep 17 00:00:00 2001 From: Alban Browaeys Date: Tue, 16 Jul 2013 18:57:53 -0300 Subject: [media] em28xx: fix assignment of the eeprom data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the config structure pointer to the eeprom data pointer (data, here eedata dereferenced) not the pointer to the pointer to the eeprom data (eedata itself). Signed-off-by: Alban Browaeys Signed-off-by: Frank Schäfer Cc: stable@vger.kernel.org # for v3.10 Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 4851cc2..c4ff973 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -726,7 +726,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus, *eedata = data; *eedata_len = len; - dev_config = (void *)eedata; + dev_config = (void *)*eedata; switch (le16_to_cpu(dev_config->chip_conf) >> 4 & 0x3) { case 0: -- cgit v0.10.2 From 3396b096c54a84603c51bd705effa88f7f5b0d76 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Mon, 15 Jul 2013 07:51:23 -0300 Subject: [media] exynos4-is: Fix fimc-lite bayer formats The 10-bit and 12-bit Bayer output formats supported by FIMC-LITE actually use 16 bits where the extra bits are padded with zeros. The patch corrects buffer allocation for these two formats by modifying the depth field. This prevents memory corruption by the output DMA due to insufficient buffer size. Signed-off-by: Arun Kumar K Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 318d4c3..e5798f7 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -90,7 +90,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .name = "RAW10 (GRBG)", .fourcc = V4L2_PIX_FMT_SGRBG10, .colorspace = V4L2_COLORSPACE_SRGB, - .depth = { 10 }, + .depth = { 16 }, .color = FIMC_FMT_RAW10, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, @@ -99,7 +99,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { .name = "RAW12 (GRBG)", .fourcc = V4L2_PIX_FMT_SGRBG12, .colorspace = V4L2_COLORSPACE_SRGB, - .depth = { 12 }, + .depth = { 16 }, .color = FIMC_FMT_RAW12, .memplanes = 1, .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, -- cgit v0.10.2 From d0b1c31349969973204fad21a076aecf131cc5e4 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Fri, 26 Jul 2013 07:28:01 -0300 Subject: [media] exynos-gsc: Register v4l2 device Gscaler video device registration was happening without reference to a parent v4l2_dev causing probe to fail. The patch creates a parent v4l2 device and uses it for the gsc m2m video device registration. This fixes regression introduced with comit commit 1c1d86a1ea07506 [media] v4l2: always require v4l2_dev, rename parent to dev_parent Signed-off-by: Arun Kumar K Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index fe69eae..9d0cc04 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1122,10 +1122,14 @@ static int gsc_probe(struct platform_device *pdev) goto err_clk; } - ret = gsc_register_m2m_device(gsc); + ret = v4l2_device_register(dev, &gsc->v4l2_dev); if (ret) goto err_clk; + ret = gsc_register_m2m_device(gsc); + if (ret) + goto err_v4l2; + platform_set_drvdata(pdev, gsc); pm_runtime_enable(dev); ret = pm_runtime_get_sync(&pdev->dev); @@ -1147,6 +1151,8 @@ err_pm: pm_runtime_put(dev); err_m2m: gsc_unregister_m2m_device(gsc); +err_v4l2: + v4l2_device_unregister(&gsc->v4l2_dev); err_clk: gsc_clk_put(gsc); return ret; @@ -1157,6 +1163,7 @@ static int gsc_remove(struct platform_device *pdev) struct gsc_dev *gsc = platform_get_drvdata(pdev); gsc_unregister_m2m_device(gsc); + v4l2_device_unregister(&gsc->v4l2_dev); vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); pm_runtime_disable(&pdev->dev); diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index cc19bba..76435d3 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -343,6 +343,7 @@ struct gsc_dev { unsigned long state; struct vb2_alloc_ctx *alloc_ctx; struct video_device vdev; + struct v4l2_device v4l2_dev; }; /** diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 40a73f7..e576ff2 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -751,6 +751,7 @@ int gsc_register_m2m_device(struct gsc_dev *gsc) gsc->vdev.release = video_device_release_empty; gsc->vdev.lock = &gsc->lock; gsc->vdev.vfl_dir = VFL_DIR_M2M; + gsc->vdev.v4l2_dev = &gsc->v4l2_dev; snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m", GSC_MODULE_NAME, gsc->id); -- cgit v0.10.2 From d2b903b4427e417a73863cef36ad0796ea6b7404 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 29 Jul 2013 06:53:59 -0300 Subject: [media] exynos4-is: Fix entity unregistration on error path This patch corrects media entities unregistration order to make sure the fimc.N.capture and fimc-lite video nodes are unregistered with fimc->lock mutex held. This prevents races between video device open() and defered probing and NULL pointer dereference in open() callback as follows: [ 77.645000] Unable to handle kernel NULL pointer dereference at virtual address 00000290t [ 77.655000] pgd = ee7a8000 [ 77.660000] [00000290] *pgd=6e13c831, *pte=00000000, *ppte=00000000 [ 77.665000] Internal error: Oops: 17 [#1] PREEMPT SMP ARM [ 77.670000] Modules linked in: s5p_fimc ipv6 exynos_fimc_is exynos_fimc_lite s5p_csis v4l2_mem2mem videobuf2_dma_contig videobuf2_memops exynos4_is_common videobuf2_core [last unloaded: s5p_fimc] [ 77.685000] CPU: 0 PID : 2998 Comm: v4l_id Tainted: G W 3.10.0-next-20130709-00039-g39f491b-dirty #1548 [ 77.695000] task: ee084000 ti: ee46e000 task.ti: ee46e000 [ 77.700000] PC is at __mutex_lock_slowpath+0x54/0x368 [ 77.705000] LR is at __mutex_lock_slowpath+0x24/0x368 [ 77.710000] pc : [] lr : [] psr: 60000093 [ 77.710000] sp : ee46fd70 ip : 000008c8 fp : c054e34c [ 77.725000] r10: ee084000 r9 : 00000000 r8 : ee439480 [ 77.730000] r7 : ee46e000 r6 : 60000013 r5 : 00000290 r4 : 0000028c [ 77.735000] r3 : 00000000 r2 : 00000000 r1 : 20000093 r0 : 00000001 [ 77.740000] Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user [ 77.750000] Control: 10c5387d Table: 6e7a804a DAC: 00000015 [ 77.755000] Process v4l_id (pid: 2998, stack limit = 0xee46e238) [ 77.760000] Stack: (0xee46fd70 to 0xee470000) ... [ 77.935000] [] (__mutex_lock_slowpath+0x54/0x368) from [] (mutex_lock+0xc/0x24) [ 77.945000] [] (mutex_lock+0xc/0x24) from [] (fimc_lite_open+0x12c/0x2bc [exynos_fimc_lite]) [ 77.955000] [] (fimc_lite_open+0x12c/0x2bc [exynos_fimc_lite]) from [] (v4l2_open+0xa0/0xe0) [ 77.965000] [] (v4l2_open+0xa0/0xe0) from [] (chrdev_open+0x88/0x170) [ 77.975000] [] (chrdev_open+0x88/0x170) from [] (do_dentry_open.isra.14+0x1d8/0x258) [ 77.985000] [] (do_dentry_open.isra.14+0x1d8/0x258) from [] (finish_open+0x20/0x38) [ 77.995000] [] (finish_open+0x20/0x38) from [] (do_last.isra.43+0x538/0xb1c) [ 78.000000] [] (do_last.isra.43+0x538/0xb1c) from [] (path_openat+0xb4/0x5c4) [ 78.010000] [] (path_openat+0xb4/0x5c4) from [] (do_filp_open+0x2c/0x80) [ 78.020000] [] (do_filp_open+0x2c/0x80) from [] (do_sys_open+0xf4/0x1a8) [ 78.025000] [] (do_sys_open+0xf4/0x1a8) from [] (ret_fast_syscall+0x0/0x30) [ 78.035000] Code: 1a000093 e10f6000 f10c0080 e2845004 (e1953f9f) Reported-by: Andrzej Hajda Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index ef6642b..e327f45 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1523,9 +1523,9 @@ static int fimc_md_probe(struct platform_device *pdev) err_unlock: mutex_unlock(&fmd->media_dev.graph_mutex); err_clk: - media_device_unregister(&fmd->media_dev); fimc_md_put_clocks(fmd); fimc_md_unregister_entities(fmd); + media_device_unregister(&fmd->media_dev); err_md: v4l2_device_unregister(&fmd->v4l2_dev); return ret; -- cgit v0.10.2 From f66b2a1c7f2ae3fb0d5b67d07ab4f5055fd3cf16 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 27 Aug 2013 04:27:57 -0300 Subject: [media] cx88: Fix regression: CX88_AUDIO_WM8775 can't be 0 Cards using the wm8775 specify that in their card struct. Those that do not use it leave the audio_chip field to 0. Unfortunately, the CX88_AUDIO_WM8775 enum is 0 as well, so boards that do not have the wm8775 still try to load and use that driver. Change it to 1 to fix this. This regression was introduced in commit facd23664f1d63c33fbc6da52261c8548ed3fbd4. Signed-off-by: Hans Verkuil Reported-by: Knut Petersen Tested-by: Knut Petersen Signed-off-by: Mauro Carvalho Chehab Cc: stable@vger.kernel.org diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h index afe0eae..28893a6 100644 --- a/drivers/media/pci/cx88/cx88.h +++ b/drivers/media/pci/cx88/cx88.h @@ -259,7 +259,7 @@ struct cx88_input { }; enum cx88_audio_chip { - CX88_AUDIO_WM8775, + CX88_AUDIO_WM8775 = 1, CX88_AUDIO_TVAUDIO, }; -- cgit v0.10.2