[media] cx23885: Hauppauge HVR1850 Analog driver support
authorSteven Toth <stoth@kernellabs.com>
Thu, 5 Jan 2012 00:08:35 +0000 (21:08 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Thu, 5 Jan 2012 08:52:02 +0000 (06:52 -0200)
First in a series of patches that adds support to the cx23885 driver
for CX23888 analog video handling. Raw and MPEG video support is
being added for the HVR1850 driver in the patch, and the following
series of patches.

Some basic cx23885 driver cleanup. Partly to add HVR1850 support
and partly to allow -417.c V4L2 calls to be routed through thr
driver core and handled in a single place.

Make a number of core driver functions available to the -417.c
driver to streamline the driver.

Add the analog tuner ops definition so we can reach/tune the
hardware when we need to. Added the tff field so 888 based cards
(which have a weird field ordering issue) can be accomodated
and worked around in the driver.

Signed-off-by: Steven Toth <stoth@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/cx23885/cx23885-cards.c
drivers/media/video/cx23885/cx23885-core.c
drivers/media/video/cx23885/cx23885-dvb.c
drivers/media/video/cx23885/cx23885-video.c
drivers/media/video/cx23885/cx23885.h

index ac03c26d7a638231fb372981d94e485893cd0bf1..dc7864eb6b372616f179a04f438e863bb2b2853a 100644 (file)
@@ -335,8 +335,33 @@ struct cx23885_board cx23885_boards[] = {
        },
        [CX23885_BOARD_HAUPPAUGE_HVR1850] = {
                .name           = "Hauppauge WinTV-HVR1850",
+               .porta          = CX23885_ANALOG_VIDEO,
                .portb          = CX23885_MPEG_ENCODER,
                .portc          = CX23885_MPEG_DVB,
+               .tuner_type     = TUNER_ABSENT,
+               .tuner_addr     = 0x42, /* 0x84 >> 1 */
+               .force_bff      = 1,
+               .input          = {{
+                       .type   = CX23885_VMUX_TELEVISION,
+                       .vmux   =       CX25840_VIN7_CH3 |
+                                       CX25840_VIN5_CH2 |
+                                       CX25840_VIN2_CH1 |
+                                       CX25840_DIF_ON,
+                       .amux   = CX25840_AUDIO8,
+               }, {
+                       .type   = CX23885_VMUX_COMPOSITE1,
+                       .vmux   =       CX25840_VIN7_CH3 |
+                                       CX25840_VIN4_CH2 |
+                                       CX25840_VIN6_CH1,
+                       .amux   = CX25840_AUDIO7,
+               }, {
+                       .type   = CX23885_VMUX_SVIDEO,
+                       .vmux   =       CX25840_VIN7_CH3 |
+                                       CX25840_VIN4_CH2 |
+                                       CX25840_VIN8_CH1 |
+                                       CX25840_SVIDEO_ON,
+                       .amux   = CX25840_AUDIO7,
+               } },
        },
        [CX23885_BOARD_COMPRO_VIDEOMATE_E800] = {
                .name           = "Compro VideoMate E800",
@@ -1402,6 +1427,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
                ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
                ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
                break;
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
        case CX23885_BOARD_HAUPPAUGE_HVR1800:
                /* Defaults for VID B - Analog encoder */
                /* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */
@@ -1412,6 +1438,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
                /* APB_TSVALERR_POL (active low)*/
                ts1->vld_misc_val    = 0x2000;
                ts1->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4 | 0xc);
+               cx_write(0x130184, 0xc);
 
                /* Defaults for VID C */
                ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
@@ -1466,7 +1493,6 @@ void cx23885_card_setup(struct cx23885_dev *dev)
        case CX23885_BOARD_HAUPPAUGE_HVR1275:
        case CX23885_BOARD_HAUPPAUGE_HVR1255:
        case CX23885_BOARD_HAUPPAUGE_HVR1210:
-       case CX23885_BOARD_HAUPPAUGE_HVR1850:
        case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
        case CX23885_BOARD_HAUPPAUGE_HVR1290:
        case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
index 40e68b22015eea4c54ce28babecd2701feb03d1c..6ad227029a0f2b497f9b7853891fb107b85c192e 100644 (file)
@@ -206,12 +206,12 @@ static struct sram_channel cx23887_sram_channels[] = {
                .cnt2_reg       = DMA1_CNT2,
        },
        [SRAM_CH02] = {
-               .name           = "ch2",
-               .cmds_start     = 0x0,
-               .ctrl_start     = 0x0,
-               .cdt            = 0x0,
-               .fifo_start     = 0x0,
-               .fifo_size      = 0x0,
+               .name           = "VID A (VBI)",
+               .cmds_start     = 0x10050,
+               .ctrl_start     = 0x105F0,
+               .cdt            = 0x10810,
+               .fifo_start     = 0x3000,
+               .fifo_size      = 0x1000,
                .ptr1_reg       = DMA2_PTR1,
                .ptr2_reg       = DMA2_PTR2,
                .cnt1_reg       = DMA2_CNT1,
@@ -266,12 +266,12 @@ static struct sram_channel cx23887_sram_channels[] = {
                .cnt2_reg       = DMA5_CNT2,
        },
        [SRAM_CH07] = {
-               .name           = "ch7",
-               .cmds_start     = 0x0,
-               .ctrl_start     = 0x0,
-               .cdt            = 0x0,
-               .fifo_start     = 0x0,
-               .fifo_size      = 0x0,
+               .name           = "TV Audio",
+               .cmds_start     = 0x10190,
+               .ctrl_start     = 0x106B0,
+               .cdt            = 0x10930,
+               .fifo_start     = 0x7000,
+               .fifo_size      = 0x1000,
                .ptr1_reg       = DMA6_PTR1,
                .ptr2_reg       = DMA6_PTR2,
                .cnt1_reg       = DMA6_CNT1,
index 28d51d89748eeafc383841fedf16f6fc49ec01ce..a3906225c49397f0d59b086c2a91124d8932c804 100644 (file)
@@ -1026,6 +1026,20 @@ static int dvb_register(struct cx23885_tsport *port)
                }
                break;
        case CX23885_BOARD_HAUPPAUGE_HVR1850:
+               i2c_bus = &dev->i2c_bus[0];
+               fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+                       &hcw_s5h1411_config,
+                       &i2c_bus->i2c_adap);
+               if (fe0->dvb.frontend != NULL)
+                       dvb_attach(tda18271_attach, fe0->dvb.frontend,
+                               0x60, &dev->i2c_bus[0].i2c_adap,
+                               &hauppauge_tda18271_config);
+
+               tda18271_attach(&dev->ts1.analog_fe,
+                       0x60, &dev->i2c_bus[1].i2c_adap,
+                       &hauppauge_tda18271_config);
+
+               break;
        case CX23885_BOARD_HAUPPAUGE_HVR1290:
                i2c_bus = &dev->i2c_bus[0];
                fe0->dvb.frontend = dvb_attach(s5h1411_attach,
index 7f3b973081a0429df25d063236909a3fbe1137b2..e85412daa6dc2a8ef8427de7841aaf9e4b54c154 100644 (file)
@@ -316,7 +316,7 @@ void cx23885_video_wakeup(struct cx23885_dev *dev,
                        __func__, bc);
 }
 
-static int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
+int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
 {
        dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
                __func__,
@@ -504,7 +504,8 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
                        INPUT(input)->vmux, 0, 0);
 
        if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) ||
-               (dev->board == CX23885_BOARD_MPX885)) {
+               (dev->board == CX23885_BOARD_MPX885) ||
+               (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850)) {
                /* Configure audio routing */
                v4l2_subdev_call(dev->sd_cx25840, audio, s_routing,
                        INPUT(input)->amux, 0, 0);
@@ -650,6 +651,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
        int rc, init_buffer = 0;
        u32 line0_offset, line1_offset;
        struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+       int field_tff;
 
        BUG_ON(NULL == fh->fmt);
        if (fh->width  < 48 || fh->width  > norm_maxw(dev->tvnorm) ||
@@ -691,15 +693,25 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
                                         buf->bpl, 0, buf->vb.height);
                        break;
                case V4L2_FIELD_INTERLACED:
-                       if (dev->tvnorm & V4L2_STD_NTSC) {
+                       if (dev->tvnorm & V4L2_STD_NTSC)
+                               /* NTSC or  */
+                               field_tff = 1;
+                       else
+                               field_tff = 0;
+
+                       if (cx23885_boards[dev->board].force_bff)
+                               /* PAL / SECAM OR 888 in NTSC MODE */
+                               field_tff = 0;
+
+                       if (field_tff) {
                                /* cx25840 transmits NTSC bottom field first */
-                               dprintk(1, "%s() Creating NTSC risc\n",
+                               dprintk(1, "%s() Creating TFF/NTSC risc\n",
                                        __func__);
                                line0_offset = buf->bpl;
                                line1_offset = 0;
                        } else {
                                /* All other formats are top field first */
-                               dprintk(1, "%s() Creating PAL/SECAM risc\n",
+                               dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n",
                                        __func__);
                                line0_offset = 0;
                                line1_offset = buf->bpl;
@@ -982,6 +994,8 @@ static int video_release(struct file *file)
        }
 
        videobuf_mmap_free(&fh->vidq);
+       videobuf_mmap_free(&fh->vbiq);
+
        file->private_data = NULL;
        kfree(fh);
 
@@ -1003,7 +1017,7 @@ static int video_mmap(struct file *file, struct vm_area_struct *vma)
 /* ------------------------------------------------------------------ */
 /* VIDEO CTRL IOCTLS                                                  */
 
-static int cx23885_get_control(struct cx23885_dev *dev,
+int cx23885_get_control(struct cx23885_dev *dev,
        struct v4l2_control *ctl)
 {
        dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__);
@@ -1011,7 +1025,7 @@ static int cx23885_get_control(struct cx23885_dev *dev,
        return 0;
 }
 
-static int cx23885_set_control(struct cx23885_dev *dev,
+int cx23885_set_control(struct cx23885_dev *dev,
        struct v4l2_control *ctl)
 {
        dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__);
@@ -1230,6 +1244,16 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
        return 0;
 }
 
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+       struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+       dprintk(1, "%s()\n", __func__);
+
+       call_all(dev, core, g_std, id);
+
+       return 0;
+}
+
 static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms)
 {
        struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
@@ -1242,7 +1266,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms)
        return 0;
 }
 
-static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
+int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
 {
        static const char *iname[] = {
                [CX23885_VMUX_COMPOSITE1] = "Composite1",
@@ -1290,7 +1314,7 @@ static int vidioc_enum_input(struct file *file, void *priv,
        return cx23885_enum_input(dev, i);
 }
 
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+int cx23885_get_input(struct file *file, void *priv, unsigned int *i)
 {
        struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
 
@@ -1299,7 +1323,12 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
        return 0;
 }
 
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+       return cx23885_get_input(file, priv, i);
+}
+
+int cx23885_set_input(struct file *file, void *priv, unsigned int i)
 {
        struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
 
@@ -1323,6 +1352,11 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
        return 0;
 }
 
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+       return cx23885_set_input(file, priv, i);
+}
+
 static int vidioc_log_status(struct file *file, void *priv)
 {
        struct cx23885_fh  *fh  = priv;
@@ -1330,11 +1364,11 @@ static int vidioc_log_status(struct file *file, void *priv)
 
        printk(KERN_INFO
                "%s/0: ============  START LOG STATUS  ============\n",
-              dev->name);
+               dev->name);
        call_all(dev, core, log_status);
        printk(KERN_INFO
                "%s/0: =============  END LOG STATUS  =============\n",
-              dev->name);
+               dev->name);
        return 0;
 }
 
@@ -1472,6 +1506,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
 
 static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
 {
+       struct v4l2_control ctrl;
+
        if (unlikely(UNSET == dev->tuner_type))
                return -EINVAL;
        if (unlikely(f->tuner != 0))
@@ -1480,29 +1516,102 @@ static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
        mutex_lock(&dev->lock);
        dev->freq = f->frequency;
 
+       /* I need to mute audio here */
+       ctrl.id = V4L2_CID_AUDIO_MUTE;
+       ctrl.value = 1;
+       cx23885_set_control(dev, &ctrl);
+
        call_all(dev, tuner, s_frequency, f);
 
        /* When changing channels it is required to reset TVAUDIO */
-       msleep(10);
+       msleep(100);
+
+       /* I need to unmute audio here */
+       ctrl.value = 0;
+       cx23885_set_control(dev, &ctrl);
 
        mutex_unlock(&dev->lock);
 
        return 0;
 }
 
-static int vidioc_s_frequency(struct file *file, void *priv,
-                               struct v4l2_frequency *f)
+static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
+       struct v4l2_frequency *f)
+{
+       struct v4l2_control ctrl;
+       struct videobuf_dvb_frontend *vfe;
+       struct dvb_frontend *fe;
+       int err = 0;
+
+       struct analog_parameters params = {
+               .mode      = V4L2_TUNER_ANALOG_TV,
+               .audmode   = V4L2_TUNER_MODE_STEREO,
+               .std       = dev->tvnorm,
+               .frequency = f->frequency
+       };
+
+       mutex_lock(&dev->lock);
+       dev->freq = f->frequency;
+
+       /* I need to mute audio here */
+       ctrl.id = V4L2_CID_AUDIO_MUTE;
+       ctrl.value = 1;
+       cx23885_set_control(dev, &ctrl);
+
+       /* If HVR1850 */
+       dprintk(1, "%s() frequency=%d tuner=%d std=0x%llx\n", __func__,
+               params.frequency, f->tuner, params.std);
+
+       vfe = videobuf_dvb_get_frontend(&dev->ts2.frontends, 1);
+       if (!vfe)
+               err = -EINVAL;
+
+       fe = vfe->dvb.frontend;
+
+       if (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850)
+               fe = &dev->ts1.analog_fe;
+
+       if (fe && fe->ops.tuner_ops.set_analog_params) {
+               call_all(dev, core, s_std, dev->tvnorm);
+               fe->ops.tuner_ops.set_analog_params(fe, &params);
+       }
+       else
+               printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
+
+       /* When changing channels it is required to reset TVAUDIO */
+       msleep(100);
+
+       /* I need to unmute audio here */
+       ctrl.value = 0;
+       cx23885_set_control(dev, &ctrl);
+
+       mutex_unlock(&dev->lock);
+
+       return 0;
+}
+
+int cx23885_set_frequency(struct file *file, void *priv,
+       struct v4l2_frequency *f)
 {
        struct cx23885_fh *fh = priv;
        struct cx23885_dev *dev = fh->dev;
+       int ret;
 
-       if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
-               return -EINVAL;
-       if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
-               return -EINVAL;
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+               ret = cx23885_set_freq_via_ops(dev, f);
+               break;
+       default:
+               ret = cx23885_set_freq(dev, f);
+       }
 
-       return
-               cx23885_set_freq(dev, f);
+       return ret;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+       struct v4l2_frequency *f)
+{
+       return cx23885_set_frequency(file, priv, f);
 }
 
 /* ----------------------------------------------------------- */
@@ -1614,6 +1723,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
        .vidioc_qbuf          = vidioc_qbuf,
        .vidioc_dqbuf         = vidioc_dqbuf,
        .vidioc_s_std         = vidioc_s_std,
+       .vidioc_g_std         = vidioc_g_std,
+       .vidioc_querystd      = vidioc_g_std,
        .vidioc_enum_input    = vidioc_enum_input,
        .vidioc_g_input       = vidioc_g_input,
        .vidioc_s_input       = vidioc_s_input,
index 519f40d2af5d9848168c87546d4c4b62aa8dae67..78fdb841cc1767edcb88769f08da9bfb9b990ad1 100644 (file)
@@ -227,6 +227,8 @@ struct cx23885_board {
        u32                     clk_freq;
        struct cx23885_input    input[MAX_CX23885_INPUT];
        int                     ci_type; /* for NetUP */
+       /* Force bottom field first during DMA (888 workaround) */
+       u32                     force_bff;
 };
 
 struct cx23885_subid {
@@ -311,6 +313,9 @@ struct cx23885_tsport {
        u32                        num_frontends;
        void                (*gate_ctrl)(struct cx23885_tsport *port, int open);
        void                       *port_priv;
+
+       /* Workaround for a temp dvb_frontend that the tuner can attached to */
+       struct dvb_frontend analog_fe;
 };
 
 struct cx23885_kernel_ir {
@@ -575,6 +580,13 @@ extern void cx23885_video_unregister(struct cx23885_dev *dev);
 extern int cx23885_video_irq(struct cx23885_dev *dev, u32 status);
 extern void cx23885_video_wakeup(struct cx23885_dev *dev,
        struct cx23885_dmaqueue *q, u32 count);
+int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i);
+int cx23885_set_input(struct file *file, void *priv, unsigned int i);
+int cx23885_get_input(struct file *file, void *priv, unsigned int *i);
+int cx23885_set_frequency(struct file *file, void *priv, struct v4l2_frequency *f);
+int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
+int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
+int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm);
 
 /* ----------------------------------------------------------- */
 /* cx23885-vbi.c                                               */