From: Bradford Love Date: Tue, 17 Mar 2026 20:45:54 +0000 (-0500) Subject: media: em28xx: Add support for Empia em2828X bridge X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8e53399c63c3152fd572596acedcdaea4cabbc45;p=thirdparty%2Fkernel%2Fstable.git media: em28xx: Add support for Empia em2828X bridge The empia em2828X usb bridge contains previous functionality, but also contains an embedded video decoder. The implemented capabilities include composite and s-video inputs, as well as analog TV. Analog TV is expected in CVBS format, it must be demodulated already. Media controller decoder entity is included so pipeline verification passes and graph is properly constructed. Analog TV bits based off cx231xx driver. Signed-off-by: Bradford Love Signed-off-by: Hans Verkuil [hverkuil: fix a few minor checkpatch issues] --- diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index d7075ebabceb..67266bddb713 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3633,6 +3633,11 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, } /* NOTE: the em2820 is used in webcams, too ! */ break; + case CHIP_ID_EM2828X: + chip_name = "em2828X"; + dev->wait_after_write = 0; + dev->eeprom_addrwidth_16bit = 1; + break; case CHIP_ID_EM2840: chip_name = "em2840"; break; @@ -3791,6 +3796,7 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev, * 0x84 bulk => analog or digital** * 0x85 isoc => digital TS2 * 0x85 bulk => digital TS2 + * 0x8a isoc => digital video * (*: audio should always be isoc) * (**: analog, if ep 0x82 is isoc, otherwise digital) * @@ -3814,6 +3820,8 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev, /* Only inspect input endpoints */ switch (e->bEndpointAddress) { + case 0x81: /* unknown function */ + return; case 0x82: *has_video = true; if (usb_endpoint_xfer_isoc(e)) { @@ -3831,7 +3839,10 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev, "error: skipping audio endpoint 0x83, because it uses bulk transfers !\n"); return; case 0x84: - if (*has_video && (usb_endpoint_xfer_bulk(e))) { + if (*has_dvb && (usb_endpoint_xfer_bulk(e))) { + *has_dvb = true; + dev->dvb_ep_bulk = e->bEndpointAddress; + } else if (*has_video && (usb_endpoint_xfer_bulk(e))) { dev->analog_ep_bulk = e->bEndpointAddress; } else { if (usb_endpoint_xfer_isoc(e)) { @@ -3865,7 +3876,17 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev, dev->dvb_ep_bulk_ts2 = e->bEndpointAddress; } return; - } + case 0x8a: + *has_video = true; + *has_dvb = true; + if (usb_endpoint_xfer_isoc(e)) { + dev->analog_ep_isoc = e->bEndpointAddress; + dev->alt_max_pkt_size_isoc[alt] = size; + } else if (usb_endpoint_xfer_bulk(e)) { + dev->analog_ep_bulk = e->bEndpointAddress; + } + return; + }; } /* @@ -4047,6 +4068,8 @@ static int em28xx_usb_probe(struct usb_interface *intf, try_bulk = 1; else try_bulk = 0; + } else if (dev->board.decoder == EM28XX_BUILTIN && dev->analog_xfer_mode) { + try_bulk = 1; } else { try_bulk = usb_xfer_mode > 0; } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 29a7f3f19b56..5bbb082dbed9 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -499,7 +499,8 @@ int em28xx_audio_setup(struct em28xx *dev) if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174 || - dev->chip_id == CHIP_ID_EM28178) { + dev->chip_id == CHIP_ID_EM28178 || + dev->chip_id == CHIP_ID_EM2828X) { /* Digital only device - don't load any alsa module */ dev->int_audio_type = EM28XX_INT_AUDIO_NONE; dev->usb_audio_type = EM28XX_USB_AUDIO_NONE; @@ -619,6 +620,65 @@ const struct em28xx_led *em28xx_find_led(struct em28xx *dev, } EXPORT_SYMBOL_GPL(em28xx_find_led); +void em2828X_decoder_vmux(struct em28xx *dev, unsigned int vin) +{ + switch (vin) { + case EM2828X_TELEVISION: + dev_dbg(&dev->intf->dev, "EM2828X_TELEVISION\n"); + break; + case EM2828X_COMPOSITE: + dev_dbg(&dev->intf->dev, "EM2828X_COMPOSITE\n"); + break; + default: + dev_dbg(&dev->intf->dev, "EM2828X_SVIDEO\n"); + break; + }; + + em28xx_write_reg(dev, 0x24, 0x00); + em28xx_write_reg(dev, 0x25, 0x02); + em28xx_write_reg(dev, 0x2E, 0x00); + + if (vin == EM2828X_TELEVISION) { + em28xx_write_reg(dev, 0x7A0B, 0xfc); + em28xx_write_reg(dev, 0xB6, 0x8F); + em28xx_write_reg(dev, 0xB8, 0x01); + } else { + em28xx_write_reg(dev, 0x7A0B, 0x00); + em28xx_write_reg(dev, 0xB6, 0x8F); + em28xx_write_reg(dev, 0xB8, 0x00); + } + + em28xx_write_reg(dev, 0x7A1C, 0x1E); + em28xx_write_reg(dev, 0x7A1D, 0x99); + em28xx_write_reg(dev, 0x7A1E, 0x99); + em28xx_write_reg(dev, 0x7A1F, 0x9A); + em28xx_write_reg(dev, 0x7A20, 0x3d); + em28xx_write_reg(dev, 0x7A21, 0x3e); + em28xx_write_reg(dev, 0x7A29, 0x00); + em28xx_write_reg(dev, 0x7A2F, 0x52); + em28xx_write_reg(dev, 0x7A40, 0x05); + em28xx_write_reg(dev, 0x7A51, 0x00); + em28xx_write_reg(dev, 0x7AC1, 0x1B); + + if (vin == EM2828X_COMPOSITE || vin == EM2828X_TELEVISION) { + em28xx_write_reg(dev, 0x38, 0x01); + em28xx_write_reg(dev, 0xB1, 0x70); + em28xx_write_reg(dev, 0xB3, 0x00); + em28xx_write_reg(dev, 0xB5, 0x00); + em28xx_write_reg(dev, 0x7A02, 0x4f); + } else { /* EM2828X_SVIDEO */ + em28xx_write_reg(dev, 0x38, 0x00); + em28xx_write_reg(dev, 0xB1, 0x60); + em28xx_write_reg(dev, 0xB3, 0x10); + em28xx_write_reg(dev, 0xB5, 0x10); + em28xx_write_reg(dev, 0x7A02, 0x4e); + } + + em28xx_write_reg(dev, 0x7A3F, 0x01); + em28xx_write_reg(dev, 0x7A3F, 0x00); +} +EXPORT_SYMBOL_GPL(em2828X_decoder_vmux); + int em28xx_capture_start(struct em28xx *dev, int start) { int rc; @@ -646,12 +706,16 @@ int em28xx_capture_start(struct em28xx *dev, int start) rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, start ? EM2874_TS1_CAPTURE_ENABLE : 0x00, - EM2874_TS1_CAPTURE_ENABLE | EM2874_TS1_FILTER_ENABLE | EM2874_TS1_NULL_DISCARD); + EM2874_TS1_CAPTURE_ENABLE | + EM2874_TS1_FILTER_ENABLE | + EM2874_TS1_NULL_DISCARD); else rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, start ? EM2874_TS2_CAPTURE_ENABLE : 0x00, - EM2874_TS2_CAPTURE_ENABLE | EM2874_TS2_FILTER_ENABLE | EM2874_TS2_NULL_DISCARD); + EM2874_TS2_CAPTURE_ENABLE | + EM2874_TS2_FILTER_ENABLE | + EM2874_TS2_NULL_DISCARD); } else { /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ @@ -664,26 +728,93 @@ int em28xx_capture_start(struct em28xx *dev, int start) if (dev->is_webcam) rc = em28xx_write_reg(dev, 0x13, 0x0c); - /* Enable video capture */ - rc = em28xx_write_reg(dev, 0x48, 0x00); - if (rc < 0) - return rc; + if (dev->mode == EM28XX_ANALOG_MODE) { + /* Enable video capture */ + rc = em28xx_write_reg(dev, 0x48, 0x00); + if (rc < 0) + return rc; - if (dev->mode == EM28XX_ANALOG_MODE) rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); - else - rc = em28xx_write_reg(dev, - EM28XX_R12_VINENABLE, - 0x37); + + } else if (dev->chip_id == CHIP_ID_EM2828X) { + /* The Transport Stream Enable Register moved in em2874 */ + if (dev->dvb_xfer_bulk) { + /* Max Tx Size = 188 * 256 = 48128 - LCM(188,512) * 2 */ + em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ? + EM2874_R5D_TS1_PKT_SIZE : + EM2874_R5E_TS2_PKT_SIZE, + 0xff); + } else { + /* ISOC Maximum Transfer Size = 188 * 5 */ + em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ? + EM2874_R5D_TS1_PKT_SIZE : + EM2874_R5E_TS2_PKT_SIZE, + dev->dvb_max_pkt_size_isoc / 188); + } + + if (dev->ts == PRIMARY_TS) + rc = em28xx_write_reg_bits(dev, + EM2874_R5F_TS_ENABLE, + start ? EM2874_TS1_CAPTURE_ENABLE : 0x00, + EM2874_TS1_CAPTURE_ENABLE | + EM2874_TS1_FILTER_ENABLE | + EM2874_TS1_NULL_DISCARD); + else + rc = em28xx_write_reg_bits(dev, + EM2874_R5F_TS_ENABLE, + start ? EM2874_TS2_CAPTURE_ENABLE : 0x00, + EM2874_TS2_CAPTURE_ENABLE | + EM2874_TS2_FILTER_ENABLE | + EM2874_TS2_NULL_DISCARD); + } else { + /* Enable video capture */ + rc = em28xx_write_reg(dev, 0x48, 0x00); + if (rc < 0) + return rc; + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); + } + if (rc < 0) return rc; usleep_range(10000, 11000); } else { - /* disable video capture */ - rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); + if (dev->mode == EM28XX_DIGITAL_MODE && dev->chip_id == CHIP_ID_EM2828X) { + /* The Transport Stream Enable Register moved in em2874 */ + if (dev->dvb_xfer_bulk) { + /* Max Tx Size = 188 * 256 = 48128 - LCM(188,512) * 2 */ + em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ? + EM2874_R5D_TS1_PKT_SIZE : + EM2874_R5E_TS2_PKT_SIZE, + 0xff); + } else { + /* ISOC Maximum Transfer Size = 188 * 5 */ + em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ? + EM2874_R5D_TS1_PKT_SIZE : + EM2874_R5E_TS2_PKT_SIZE, + dev->dvb_max_pkt_size_isoc / 188); + } + + if (dev->ts == PRIMARY_TS) + rc = em28xx_write_reg_bits(dev, + EM2874_R5F_TS_ENABLE, + start ? EM2874_TS1_CAPTURE_ENABLE : 0x00, + EM2874_TS1_CAPTURE_ENABLE | + EM2874_TS1_FILTER_ENABLE | + EM2874_TS1_NULL_DISCARD); + else + rc = em28xx_write_reg_bits(dev, + EM2874_R5F_TS_ENABLE, + start ? EM2874_TS2_CAPTURE_ENABLE : 0x00, + EM2874_TS2_CAPTURE_ENABLE | + EM2874_TS2_FILTER_ENABLE | + EM2874_TS2_NULL_DISCARD); + } else { + /* disable video capture */ + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); + } } } diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 2eb9a88e595e..95c6a38c9cc5 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -296,6 +296,21 @@ static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) return em28xx_set_mode(dev, EM28XX_SUSPEND); } +static int em28xx_set_analog_freq(struct em28xx *dev, u32 freq) +{ + const struct dvb_tuner_ops *dops = &dev->dvb->fe[0]->ops.tuner_ops; + + if (dops->set_analog_params) { + struct analog_parameters params; + + params.frequency = freq; + params.std = dev->v4l2->norm; + params.mode = 0; + + dops->set_analog_params(dev->dvb->fe[0], ¶ms); + } + return 0; +} /* ------------------------------------------------------------------ */ static struct lgdt330x_config em2880_lgdt3303_dev = { diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index a7eb11f7fb34..f0a901c3b49a 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -864,6 +864,8 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned int bus, le16_to_cpu(dev_config->string2), le16_to_cpu(dev_config->string3)); + dev->analog_xfer_mode = data[67] & 0x01; + return 0; error: diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index d7c60862874a..68a0fcc2fa72 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -283,6 +283,7 @@ enum em28xx_chip_id { CHIP_ID_EM2884 = 68, CHIP_ID_EM28174 = 113, CHIP_ID_EM28178 = 114, + CHIP_ID_EM2828X = 148, }; /* diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 5f13f63fbdee..d80a8aca2a5c 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -161,13 +161,19 @@ static int em28xx_vbi_supported(struct em28xx *dev) /* FIXME: check subdevices for VBI support */ if (dev->chip_id == CHIP_ID_EM2860 || - dev->chip_id == CHIP_ID_EM2883) + dev->chip_id == CHIP_ID_EM2883 || + dev->board.decoder == EM28XX_BUILTIN) return 1; /* Version of em28xx that does not support VBI */ return 0; } +static int em28xx_analogtv_supported(struct em28xx *dev) +{ + return 0; +} + /* * em28xx_wake_i2c() * configure i2c attached devices @@ -344,6 +350,109 @@ static int em28xx_resolution_set(struct em28xx *dev) return em28xx_scaler_set(dev, v4l2->hscale, v4l2->vscale); } +static void em2828X_decoder_set_std(struct em28xx *dev, v4l2_std_id norm) +{ + if (norm & V4L2_STD_525_60) { + dev_dbg(&dev->intf->dev, "V4L2_STD_525_60"); + em28xx_write_reg(dev, 0x7A01, 0x0d); // 0x05 + em28xx_write_reg(dev, 0x7A04, 0xDD); + em28xx_write_reg(dev, 0x7A07, 0x60); + em28xx_write_reg(dev, 0x7A08, 0x7A); + em28xx_write_reg(dev, 0x7A09, 0x02); + em28xx_write_reg(dev, 0x7A0A, 0x7C); + em28xx_write_reg(dev, 0x7A0C, 0x8A); + em28xx_write_reg(dev, 0x7A0F, 0x1C); + em28xx_write_reg(dev, 0x7A18, 0x20); + em28xx_write_reg(dev, 0x7A19, 0x74); + em28xx_write_reg(dev, 0x7A1A, 0x5D); + em28xx_write_reg(dev, 0x7A1B, 0x17); + em28xx_write_reg(dev, 0x7A2E, 0x85); + em28xx_write_reg(dev, 0x7A31, 0x63); + em28xx_write_reg(dev, 0x7A82, 0x42); + em28xx_write_reg(dev, 0x7AC0, 0xD4); + + if (INPUT(dev->ctl_input)->vmux == EM2828X_COMPOSITE) { + em28xx_write_reg(dev, 0x7A00, 0x00); + em28xx_write_reg(dev, 0x7A03, 0x00); + em28xx_write_reg(dev, 0x7A30, 0x22); + em28xx_write_reg(dev, 0x7A80, 0x03); + } else if (INPUT(dev->ctl_input)->vmux == EM2828X_TELEVISION) { + em28xx_write_reg(dev, 0x7A17, 0xc3); + em28xx_write_reg(dev, 0x7A31, 0x62); // BRL 0x63 + em28xx_write_reg(dev, 0x7A82, 0x42); + em28xx_write_reg(dev, 0x7AC0, 0xD4); + em28xx_write_reg(dev, 0x7A00, 0x00); + em28xx_write_reg(dev, 0x7A03, 0x00); + em28xx_write_reg(dev, 0x7A30, 0x20); + em28xx_write_reg(dev, 0x7A80, 0x00); + + em28xx_write_reg(dev, 0x7A50, 0xdd); + em28xx_write_reg(dev, 0x7A5d, 0x0e); + em28xx_write_reg(dev, 0x7A5e, 0xea); + em28xx_write_reg(dev, 0x7A60, 0x64); + em28xx_write_reg(dev, 0x7A67, 0x5a); + } else { + em28xx_write_reg(dev, 0x7A00, 0x01); + em28xx_write_reg(dev, 0x7A03, 0x03); + em28xx_write_reg(dev, 0x7A30, 0x20); + em28xx_write_reg(dev, 0x7A80, 0x04); + } + } else if (norm & V4L2_STD_625_50) { + dev_dbg(&dev->intf->dev, "V4L2_STD_625_50"); + em28xx_write_reg(dev, 0x7A04, 0xDC); + em28xx_write_reg(dev, 0x7A0C, 0x67); + em28xx_write_reg(dev, 0x7A0F, 0x1C); + em28xx_write_reg(dev, 0x7A18, 0x28); + em28xx_write_reg(dev, 0x7A19, 0x32); + em28xx_write_reg(dev, 0x7A1A, 0xB9); + em28xx_write_reg(dev, 0x7A1B, 0x86); + em28xx_write_reg(dev, 0x7A31, 0xC3); + em28xx_write_reg(dev, 0x7A82, 0x52); + + if (INPUT(dev->ctl_input)->vmux == EM2828X_COMPOSITE) { + em28xx_write_reg(dev, 0x7A00, 0x32); + em28xx_write_reg(dev, 0x7A01, 0x10); + em28xx_write_reg(dev, 0x7A03, 0x06); + em28xx_write_reg(dev, 0x7A07, 0x2f); + em28xx_write_reg(dev, 0x7A08, 0x77); + em28xx_write_reg(dev, 0x7A09, 0x0f); + em28xx_write_reg(dev, 0x7A0A, 0x8c); + em28xx_write_reg(dev, 0x7A20, 0x3d); + em28xx_write_reg(dev, 0x7A2E, 0x88); + em28xx_write_reg(dev, 0x7A30, 0x2c); + em28xx_write_reg(dev, 0x7A80, 0x07); + } else if (INPUT(dev->ctl_input)->vmux == EM2828X_TELEVISION) { + em28xx_write_reg(dev, 0x7A00, 0x32); + em28xx_write_reg(dev, 0x7A03, 0x09); + em28xx_write_reg(dev, 0x7A30, 0x2a); + em28xx_write_reg(dev, 0x7A80, 0x03); + em28xx_write_reg(dev, 0x7A20, 0x35); + em28xx_write_reg(dev, 0x7A2e, 0x88); + em28xx_write_reg(dev, 0x7A53, 0xcc); + em28xx_write_reg(dev, 0x7A5d, 0x16); + em28xx_write_reg(dev, 0x7A5e, 0x50); + em28xx_write_reg(dev, 0x7A60, 0xb4); + em28xx_write_reg(dev, 0x7A67, 0x64); + } else { + em28xx_write_reg(dev, 0x7A00, 0x33); + em28xx_write_reg(dev, 0x7A01, 0x04); + em28xx_write_reg(dev, 0x7A03, 0x04); + em28xx_write_reg(dev, 0x7A07, 0x20); + em28xx_write_reg(dev, 0x7A08, 0x6a); + em28xx_write_reg(dev, 0x7A09, 0x16); + em28xx_write_reg(dev, 0x7A0A, 0x80); + em28xx_write_reg(dev, 0x7A2E, 0x8a); + em28xx_write_reg(dev, 0x7A30, 0x26); + em28xx_write_reg(dev, 0x7A80, 0x08); + } + } else { + dev_err(&dev->intf->dev, "%s() Unsupported STD: %X", __func__, (unsigned int)norm); + } + + em28xx_write_reg(dev, 0x7A3F, 0x01); + em28xx_write_reg(dev, 0x7A3F, 0x00); +} + /* Set USB alternate setting for analog video */ static int em28xx_set_alternate(struct em28xx *dev) { @@ -880,6 +989,12 @@ static void em28xx_v4l2_media_release(struct em28xx *dev) #ifdef CONFIG_MEDIA_CONTROLLER int i; + if (dev->board.decoder == EM28XX_BUILTIN) { + media_device_unregister_entity(dev->v4l2->decoder); + kfree(dev->v4l2->decoder); + dev->v4l2->decoder = NULL; + } + for (i = 0; i < MAX_EM28XX_INPUT; i++) { if (!INPUT(i)->type) return; @@ -1003,7 +1118,7 @@ static void em28xx_v4l2_create_entities(struct em28xx *dev) ent->function = MEDIA_ENT_F_CONN_SVIDEO; break; default: /* EM28XX_VMUX_TELEVISION or EM28XX_RADIO */ - if (dev->tuner_type != TUNER_ABSENT) + if (dev->tuner_type != TUNER_ABSENT || em28xx_analogtv_supported(dev)) ent->function = MEDIA_ENT_F_CONN_RF; break; } @@ -1018,6 +1133,27 @@ static void em28xx_v4l2_create_entities(struct em28xx *dev) dev_err(&dev->intf->dev, "failed to register input entity %d!\n", i); } + + if (dev->board.decoder == EM28XX_BUILTIN) { + v4l2->decoder_pads[EM2828X_PAD_INPUT].flags = MEDIA_PAD_FL_SINK; + v4l2->decoder_pads[EM2828X_PAD_INPUT].sig_type = PAD_SIGNAL_ANALOG; + v4l2->decoder_pads[EM2828X_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; + v4l2->decoder_pads[EM2828X_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV; + + v4l2->decoder = kzalloc_obj(*v4l2->decoder); + v4l2->decoder->name = "em2828x_builtin"; + v4l2->decoder->function = MEDIA_ENT_F_ATV_DECODER; + + ret = media_entity_pads_init(v4l2->decoder, EM2828X_NUM_PADS, + &v4l2->decoder_pads[0]); + if (ret < 0) + dev_err(&dev->intf->dev, "failed to initialize decoder pads %d!\n", ret); + + ret = media_device_register_entity(dev->media_dev, v4l2->decoder); + if (ret < 0) + dev_err(&dev->intf->dev, "failed to register decoder entity %d!\n", ret); + } + #endif } @@ -1297,6 +1433,13 @@ static void video_mux(struct em28xx *dev, int index) MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); } + if (dev->board.decoder == EM28XX_BUILTIN) { + em2828X_decoder_vmux(dev, INPUT(index)->vmux); + em2828X_decoder_set_std(dev, dev->v4l2->norm); + + em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio); + } + if (dev->board.adecoder != EM28XX_NOADECODER) { v4l2_device_call_all(v4l2_dev, 0, audio, s_routing, dev->ctl_ainput, dev->ctl_aoutput, 0); @@ -1586,6 +1729,9 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) em28xx_resolution_set(dev); v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); + if (dev->board.decoder == EM28XX_BUILTIN) + em2828X_decoder_set_std(dev, v4l2->norm); + return 0; } @@ -1829,6 +1975,11 @@ static int vidioc_g_tuner(struct file *file, void *priv, strscpy(t->name, "Tuner", sizeof(t->name)); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + t->signal = 0xffff; /* LOCKED */ + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); return 0; } @@ -1978,7 +2129,7 @@ static int vidioc_querycap(struct file *file, void *priv, V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) cap->capabilities |= V4L2_CAP_AUDIO; - if (dev->tuner_type != TUNER_ABSENT) + if (dev->tuner_type != TUNER_ABSENT || em28xx_analogtv_supported(dev)) cap->capabilities |= V4L2_CAP_TUNER; if (video_is_registered(&v4l2->vbi_dev)) cap->capabilities |= V4L2_CAP_VBI_CAPTURE; @@ -2555,7 +2706,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) } hdl = &v4l2->ctrl_handler; - v4l2_ctrl_handler_init(hdl, 8); + v4l2_ctrl_handler_init(hdl, 9); v4l2->v4l2_dev.ctrl_handler = hdl; if (dev->is_webcam) @@ -2681,7 +2832,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) } /* set default norm */ - v4l2->norm = V4L2_STD_PAL; + v4l2->norm = -1; v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); v4l2->interlaced_fieldmode = EM28XX_INTERLACED_DEFAULT; @@ -2761,10 +2912,9 @@ static int em28xx_v4l2_init(struct em28xx *dev) V4L2_CAP_STREAMING; if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) v4l2->vdev.device_caps |= V4L2_CAP_AUDIO; - if (dev->tuner_type != TUNER_ABSENT) + if (dev->tuner_type != TUNER_ABSENT || em28xx_analogtv_supported(dev)) v4l2->vdev.device_caps |= V4L2_CAP_TUNER; - /* disable inapplicable ioctls */ if (dev->is_webcam) { v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_QUERYSTD); @@ -2773,7 +2923,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) } else { v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_PARM); } - if (dev->tuner_type == TUNER_ABSENT) { + if ((v4l2->vdev.device_caps & V4L2_CAP_TUNER) == 0) { v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_TUNER); v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_TUNER); v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_FREQUENCY); @@ -2784,6 +2934,9 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_AUDIO); } + if (dev->chip_id == CHIP_ID_EM2828X || dev->board.decoder == EM28XX_BUILTIN) + v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_ENUM_FRAMESIZES); + /* register v4l2 video video_device */ ret = video_register_device(&v4l2->vdev, VFL_TYPE_VIDEO, video_nr[dev->devno]); @@ -2802,12 +2955,12 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2->vbi_dev.queue->lock = &v4l2->vb_vbi_queue_lock; v4l2->vbi_dev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; - if (dev->tuner_type != TUNER_ABSENT) + if ((v4l2->vdev.device_caps & V4L2_CAP_TUNER) == 0) v4l2->vbi_dev.device_caps |= V4L2_CAP_TUNER; /* disable inapplicable ioctls */ v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_PARM); - if (dev->tuner_type == TUNER_ABSENT) { + if ((v4l2->vbi_dev.device_caps & V4L2_CAP_TUNER) == 0) { v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_TUNER); v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_TUNER); v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_G_FREQUENCY); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index f3449c240d21..b77357f71cf3 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -425,8 +425,14 @@ enum em28xx_decoder { EM28XX_NODECODER = 0, EM28XX_TVP5150, EM28XX_SAA711X, + EM28XX_BUILTIN, }; +/* Built in decoder capture options */ +#define EM2828X_COMPOSITE 0 +#define EM2828X_SVIDEO 1 +#define EM2828X_TELEVISION 2 + enum em28xx_sensor { EM28XX_NOSENSOR = 0, EM28XX_MT9V011, @@ -469,6 +475,12 @@ struct em28xx_button { bool inverted; }; +enum em2828x_media_pads { + EM2828X_PAD_INPUT, + EM2828X_PAD_VID_OUT, + EM2828X_NUM_PADS +}; + struct em28xx_board { char *name; int vchannels; @@ -593,6 +605,7 @@ struct em28xx_v4l2 { #ifdef CONFIG_MEDIA_CONTROLLER struct media_pad video_pad, vbi_pad; + struct media_pad decoder_pads[EM2828X_NUM_PADS]; struct media_entity *decoder; #endif }; @@ -752,6 +765,8 @@ struct em28xx { char *buf, int len); int (*em28xx_read_reg_req)(struct em28xx *dev, u8 req, u16 reg); + int (*em28xx_set_analog_freq)(struct em28xx *dev, u32 freq); + enum em28xx_mode mode; // Button state polling @@ -763,6 +778,7 @@ struct em28xx { // Snapshot button input device char snapshot_button_path[30]; // path of the input dev struct input_dev *sbutton_input_dev; + int analog_xfer_mode; #ifdef CONFIG_MEDIA_CONTROLLER struct media_device *media_dev; @@ -811,6 +827,8 @@ int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val); int em28xx_audio_analog_set(struct em28xx *dev); int em28xx_audio_setup(struct em28xx *dev); +void em2828X_decoder_vmux(struct em28xx *dev, unsigned int vin); + const struct em28xx_led *em28xx_find_led(struct em28xx *dev, enum em28xx_led_role role); int em28xx_capture_start(struct em28xx *dev, int start);