summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c27
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c159
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c15
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c2
-rw-r--r--drivers/media/usb/em28xx/em28xx-reg.h1
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c173
-rw-r--r--drivers/media/usb/em28xx/em28xx.h18
7 files changed, 369 insertions, 26 deletions
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], &params);
+ }
+ 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);