public inbox for linux-media@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] si2177 tuner support and new Hauppauge 9x5 devices
@ 2026-03-17 20:50 Bradford Love
  2026-03-17 20:50 ` [PATCH 1/3] media: em28xx: driver supports two frontends, but not i2c clients Bradford Love
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Bradford Love @ 2026-03-17 20:50 UTC (permalink / raw)
  To: linux-media; +Cc: Bradford Love

Hauppauge 9x5 devices, such as HVR-935, HVR-955Q, and HVR-975
have moved from cx231xx usb bridge to Empia em2828x bridge.

This series depends on my previous series:

em28xx: Add empia EM2828X chip

The new devices have all capabilities as previous generation.
- 935 - DVB-T/T2/C, analog tv, composite, s-video, stereo audio
- 955 - QAM/ATSC, analog tv, composite, s-video, stereo audio
- 975 - DVB-T/T2/C + QAM/ATSC, analog tv, composite, s-video, stereo audio

DVB demod: si2168
ATSC demod: lgdt3306a
tuner: si2177

The em28xx driver needed to be expanded slightly to accommodate
a second i2c client frontend, it already was setup for "old"
style multi frontend use already.

The si2177 tuner chip puts out decoded CVBS video, and therefore
requires additional configuration when setting up analog tuning parameters.

Regards,

Bradford



Bradford Love (3):
  em28xx: driver supports two frontends, but not i2c clients
  si2157: Include support for si2177 chip
  em28xx: Add Hauppauge em2828X based 9x5 revisions

 drivers/media/tuners/si2157.c           | 208 +++++++++++++----
 drivers/media/tuners/si2157_priv.h      |   3 +-
 drivers/media/usb/em28xx/em28xx-cards.c | 157 +++++++++++++
 drivers/media/usb/em28xx/em28xx-dvb.c   | 284 ++++++++++++++++++++----
 drivers/media/usb/em28xx/em28xx-video.c |  22 +-
 drivers/media/usb/em28xx/em28xx.h       |   3 +
 6 files changed, 588 insertions(+), 89 deletions(-)

-- 
2.35.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/3] media: em28xx: driver supports two frontends, but not i2c clients
  2026-03-17 20:50 [PATCH 0/3] si2177 tuner support and new Hauppauge 9x5 devices Bradford Love
@ 2026-03-17 20:50 ` Bradford Love
  2026-03-24 18:24   ` [PATCH v2 " Bradford Love
  2026-03-17 20:50 ` [PATCH 2/3] media: si2157: Include support for si2177 chip Bradford Love
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Bradford Love @ 2026-03-17 20:50 UTC (permalink / raw)
  To: linux-media; +Cc: Bradford Love

Fix em28xx driver to accommodate a device with two i2c client demods.

The em28xx driver already had a multi frontend aware device struct,
with two fe, just not two i2c client demods. Since Hauppauge HVR975
has transitioned from cx231xx to em2828X, this is required for full
functionality of the devices.

Signed-off-by: Bradford Love <brad@nextdimension.cc>
---
 drivers/media/usb/em28xx/em28xx-dvb.c | 81 ++++++++++++++-------------
 1 file changed, 42 insertions(+), 39 deletions(-)

diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 4d632ca91e30..389c40a1ecdb 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -97,7 +97,7 @@ struct em28xx_dvb {
 	struct semaphore      pll_mutex;
 	bool			dont_attach_fe1;
 	int			lna_gpio;
-	struct i2c_client	*i2c_client_demod;
+	struct i2c_client	*i2c_client_demod[2];
 	struct i2c_client	*i2c_client_tuner;
 	struct i2c_client	*i2c_client_sec;
 };
@@ -854,14 +854,14 @@ static void px_bcud_init(struct em28xx *dev)
 	};
 	em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x46);
 	/* sleeping ISDB-T */
-	dev->dvb->i2c_client_demod->addr = 0x14;
+	dev->dvb->i2c_client_demod[0]->addr = 0x14;
 	for (i = 0; i < ARRAY_SIZE(regs1); i++)
-		i2c_master_send(dev->dvb->i2c_client_demod,
+		i2c_master_send(dev->dvb->i2c_client_demod[0],
 				regs1[i].r, regs1[i].len);
 	/* sleeping ISDB-S */
-	dev->dvb->i2c_client_demod->addr = 0x15;
+	dev->dvb->i2c_client_demod[0]->addr = 0x15;
 	for (i = 0; i < ARRAY_SIZE(regs2); i++)
-		i2c_master_send(dev->dvb->i2c_client_demod, regs2[i].r,
+		i2c_master_send(dev->dvb->i2c_client_demod[0], regs2[i].r,
 				regs2[i].len);
 	for (i = 0; i < ARRAY_SIZE(gpio); i++) {
 		em28xx_write_reg_bits(dev, gpio[i].reg, gpio[i].val,
@@ -1155,13 +1155,13 @@ static int em28174_dvb_init_pctv_460e(struct em28xx *dev)
 	tda10071_pdata.pll_multiplier = 20;
 	tda10071_pdata.tuner_i2c_addr = 0x14;
 
-	dvb->i2c_client_demod = dvb_module_probe("tda10071", "tda10071_cx24118",
+	dvb->i2c_client_demod[0] = dvb_module_probe("tda10071", "tda10071_cx24118",
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x55, &tda10071_pdata);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
-	dvb->fe[0] = tda10071_pdata.get_dvb_frontend(dvb->i2c_client_demod);
+	dvb->fe[0] = tda10071_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]);
 
 	/* attach SEC */
 	a8293_pdata.dvb_frontend = dvb->fe[0];
@@ -1170,7 +1170,7 @@ static int em28174_dvb_init_pctv_460e(struct em28xx *dev)
 					       &dev->i2c_adap[dev->def_i2c_bus],
 					       0x08, &a8293_pdata);
 	if (!dvb->i2c_client_sec) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1193,14 +1193,14 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev)
 	m88ds3103_pdata.ts_clk_pol = 1;
 	m88ds3103_pdata.agc = 0x99;
 
-	dvb->i2c_client_demod = dvb_module_probe("m88ds3103", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x68, &m88ds3103_pdata);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
-	dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod);
-	i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod);
+	dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]);
+	i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod[0]);
 
 	/* attach tuner */
 	ts2020_config.fe = dvb->fe[0];
@@ -1209,7 +1209,7 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev)
 						 i2c_adapter,
 						 0x60, &ts2020_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1230,7 +1230,7 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev)
 					       0x08, &a8293_pdata);
 	if (!dvb->i2c_client_sec) {
 		dvb_module_release(dvb->i2c_client_tuner);
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1254,15 +1254,15 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev)
 	m88ds3103_pdata.agc = 0x99;
 	m88ds3103_pdata.agc_inv = 0;
 	m88ds3103_pdata.spec_inv = 0;
-	dvb->i2c_client_demod = dvb_module_probe("m88ds3103", "m88ds3103b",
+	dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", "m88ds3103b",
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x6a, &m88ds3103_pdata);
 
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
-	dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod);
-	i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod);
+	dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]);
+	i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod[0]);
 
 	/* attach tuner */
 	ts2020_config.fe = dvb->fe[0];
@@ -1270,7 +1270,7 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev)
 						 i2c_adapter,
 						 0x60, &ts2020_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1285,7 +1285,7 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev)
 					       0x08, &a8293_pdata);
 	if (!dvb->i2c_client_sec) {
 		dvb_module_release(dvb->i2c_client_tuner);
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1305,10 +1305,10 @@ static int em28178_dvb_init_pctv_292e(struct em28xx *dev)
 	si2168_config.ts_mode = SI2168_TS_PARALLEL;
 	si2168_config.spectral_inversion = true;
 
-	dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x64, &si2168_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	/* attach tuner */
@@ -1321,7 +1321,7 @@ static int em28178_dvb_init_pctv_292e(struct em28xx *dev)
 						 adapter,
 						 0x60, &si2157_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 	dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna;
@@ -1341,10 +1341,10 @@ static int em28178_dvb_init_terratec_t2_stick_hd(struct em28xx *dev)
 	si2168_config.fe = &dvb->fe[0];
 	si2168_config.ts_mode = SI2168_TS_PARALLEL;
 
-	dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x64, &si2168_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	/* attach tuner */
@@ -1358,7 +1358,7 @@ static int em28178_dvb_init_terratec_t2_stick_hd(struct em28xx *dev)
 						 adapter,
 						 0x60, &si2157_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1372,10 +1372,10 @@ static int em28178_dvb_init_plex_px_bcud(struct em28xx *dev)
 	struct qm1d1c0042_config qm1d1c0042_config = {};
 
 	/* attach demod */
-	dvb->i2c_client_demod = dvb_module_probe("tc90522", "tc90522sat",
+	dvb->i2c_client_demod[0] = dvb_module_probe("tc90522", "tc90522sat",
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x15, &tc90522_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	/* attach tuner */
@@ -1386,7 +1386,7 @@ static int em28178_dvb_init_plex_px_bcud(struct em28xx *dev)
 						 tc90522_config.tuner_i2c,
 						 0x61, &qm1d1c0042_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1411,10 +1411,10 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_dvb(struct em28xx *dev)
 	si2168_config.spectral_inversion = true;
 	addr = (dev->ts == PRIMARY_TS) ? 0x64 : 0x67;
 
-	dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 addr, &si2168_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	/* attach tuner */
@@ -1430,7 +1430,7 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_dvb(struct em28xx *dev)
 						 adapter,
 						 addr, &si2157_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1451,10 +1451,10 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev)
 	lgdt3306a_config.i2c_adapter = &adapter;
 	addr = (dev->ts == PRIMARY_TS) ? 0x59 : 0x0e;
 
-	dvb->i2c_client_demod = dvb_module_probe("lgdt3306a", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 addr, &lgdt3306a_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	/* attach tuner */
@@ -1470,7 +1470,7 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev)
 						 adapter,
 						 addr, &si2157_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1488,10 +1488,10 @@ static int em2874_dvb_init_hauppauge_usb_quadhd(struct em28xx *dev)
 	mxl692_config.fe = &dvb->fe[0];
 	addr = (dev->ts == PRIMARY_TS) ? 0x60 : 0x63;
 
-	dvb->i2c_client_demod = dvb_module_probe("mxl692", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("mxl692", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 addr, &mxl692_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	return 0;
@@ -1522,6 +1522,8 @@ static int em28xx_dvb_init(struct em28xx *dev)
 	dev->dvb = dvb;
 	dvb->fe[0] = NULL;
 	dvb->fe[1] = NULL;
+	dvb->i2c_client_demod[0] = NULL;
+	dvb->i2c_client_demod[1] = NULL;
 
 	/* pre-allocate DVB usb transfer buffers */
 	if (dev->dvb_xfer_bulk) {
@@ -2083,7 +2085,8 @@ static int em28xx_dvb_fini(struct em28xx *dev)
 	/* release I2C module bindings */
 	dvb_module_release(dvb->i2c_client_sec);
 	dvb_module_release(dvb->i2c_client_tuner);
-	dvb_module_release(dvb->i2c_client_demod);
+	dvb_module_release(dvb->i2c_client_demod[1]);
+	dvb_module_release(dvb->i2c_client_demod[0]);
 
 	kfree(dvb);
 	dev->dvb = NULL;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/3] media: si2157: Include support for si2177 chip
  2026-03-17 20:50 [PATCH 0/3] si2177 tuner support and new Hauppauge 9x5 devices Bradford Love
  2026-03-17 20:50 ` [PATCH 1/3] media: em28xx: driver supports two frontends, but not i2c clients Bradford Love
@ 2026-03-17 20:50 ` Bradford Love
  2026-03-24 18:25   ` [PATCH v2 " Bradford Love
  2026-03-17 20:50 ` [PATCH 3/3] media: em28xx: Add Hauppauge em2828X based 9x5 revisions Bradford Love
  2026-03-24 18:23 ` [PATCH v2 0/3] si2177 tuner support and new Hauppauge 9x5 devices Bradford Love
  3 siblings, 1 reply; 8+ messages in thread
From: Bradford Love @ 2026-03-17 20:50 UTC (permalink / raw)
  To: linux-media; +Cc: Bradford Love

The si2177 is very closely related to si2157, with slight differences
when doing analog operations. Digital is left as is, but analog needs
to be configured specially because the signal is internally demodulated
and CVBS video is output directly from the tuner.

Verified locked and working with all supported standards.

Signed-off-by: Bradford Love <brad@nextdimension.cc>
---
 drivers/media/tuners/si2157.c      | 208 ++++++++++++++++++++++-------
 drivers/media/tuners/si2157_priv.h |   3 +-
 2 files changed, 163 insertions(+), 48 deletions(-)

diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
index b041cd854732..93ab8e0014ee 100644
--- a/drivers/media/tuners/si2157.c
+++ b/drivers/media/tuners/si2157.c
@@ -687,60 +687,104 @@ static int si2157_set_analog_params(struct dvb_frontend *fe,
 		params->mode, system, std, params->frequency,
 		freq, if_frequency, bandwidth);
 
-	/* set analog IF port */
-	memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6);
-	/* in using dev->if_port, we assume analog and digital IF's */
-	/*   are always on different ports */
-	/* assumes if_port definition is 0 or 1 for digital out */
-	cmd.args[4] = (dev->if_port == 1) ? 8 : 10;
-	/* Analog AGC assumed external */
-	cmd.args[5] = (dev->if_port == 1) ? 2 : 1;
-	cmd.wlen = 6;
-	cmd.rlen = 4;
-	ret = si2157_cmd_execute(client, &cmd);
-	if (ret)
-		goto err;
+	if (dev->part_id != SI2177) {
+		/* AGC speed */
+		memcpy(cmd.args, "\x14\x00\x11\x06\x00\x00", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);		/* NEW BRL */
+		if (ret)
+			goto err;
 
-	/* set analog IF output config */
-	memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6);
-	cmd.wlen = 6;
-	cmd.rlen = 4;
-	ret = si2157_cmd_execute(client, &cmd);
-	if (ret)
-		goto err;
+		/* set analog IF port */
+		memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6);
+		/* in using dev->if_port, we assume analog and digital IF's */
+		/*   are always on different ports */
+		/* assumes if_port definition is 0 or 1 for digital out */
+		cmd.args[4] = (dev->if_port == 1) ? 8 : 10;
+		/* Analog AGC assumed external */
+		cmd.args[5] = (dev->if_port == 1) ? 2 : 1;
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
 
-	/* make this distinct from a digital IF */
-	dev->if_frequency = if_frequency | 1;
+		/* set analog IF output config */
+		memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
 
-	/* calc and set tuner analog if center frequency */
-	if_frequency = if_frequency + 1250000 - (bandwidth / 2);
-	dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency);
 
-	memcpy(cmd.args, "\x14\x00\x0C\x06", 4);
-	cmd.args[4] = (if_frequency / 1000) & 0xff;
-	cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff;
-	cmd.wlen = 6;
-	cmd.rlen = 4;
-	ret = si2157_cmd_execute(client, &cmd);
-	if (ret)
-		goto err;
+		/* make this distinct from a digital IF */
+		dev->if_frequency = if_frequency | 1;
 
-	/* set analog AGC config */
-	memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6);
-	cmd.wlen = 6;
-	cmd.rlen = 4;
-	ret = si2157_cmd_execute(client, &cmd);
-	if (ret)
-		goto err;
+		/* calc and set tuner analog if center frequency */
+		if_frequency = if_frequency + 1250000 - (bandwidth / 2);
+		dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency);
+
+		memcpy(cmd.args, "\x14\x00\x0C\x06", 4);
+		cmd.args[4] = (if_frequency / 1000) & 0xff;
+		cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff;
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* set analog AGC config */
+		memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* set analog video mode */
+		memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6);
+		cmd.args[4] = system | color;
+		/* can use dev->inversion if assumed applies to both digital/analog */
+		if (invert_analog)
+			cmd.args[5] |= 0x02;
+		cmd.wlen = 6;
+		cmd.rlen = 1;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+	} else {
+		/* analog video equalizer - Si2177_ATV_VIDEO_EQUALIZER_PROP */
+		memcpy(cmd.args, "\x14\x00\x08\x06\xf8\x00", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* analog CVBS output properties - Si2177_ATV_CVBS_OUT_FINE_PROP */
+		memcpy(cmd.args, "\x14\x00\x14\x06\x00\x64", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
 
-	/* set analog video mode */
-	memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6);
-	cmd.args[4] = system | color;
-	/* can use dev->inversion if assumed applies to both digital/analog */
-	if (invert_analog)
-		cmd.args[5] |= 0x02;
+		dev_err(&client->dev, "%s() Settings HSYNC\n", __func__);
+		/* HSYNC output - Si2177_ATV_HSYNC_OUT_PROP */
+		memcpy(cmd.args, "\x14\x00\x27\x06\xa8\x00", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+	}
+
+	/* AFC qcuisition range 1.5MHz */
+	memcpy(cmd.args, "\x14\x00\x10\x06\xdc\x05", 6);
 	cmd.wlen = 6;
-	cmd.rlen = 1;
+	cmd.rlen = 4;
 	ret = si2157_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
@@ -757,6 +801,76 @@ static int si2157_set_analog_params(struct dvb_frontend *fe,
 	if (ret)
 		goto err;
 
+	if (dev->part_id == SI2177) {
+		/* Ref driver tunes, resets registers, then retunes, leaving steps as is */
+		/* set analog video mode - Si2158_ATV_VIDEO_MODE_PROP */
+		memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6);
+		cmd.args[4] = system | color;
+		/* can use dev->inversion if assumed applies to both digital/analog */
+		if (invert_analog)
+			cmd.args[5] |= 0x02;
+
+		cmd.wlen = 6;
+		cmd.rlen = 1;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* Si2177_ATV_AUDIO_MODE_PROP */
+		memcpy(cmd.args, "\x14\x00\x02\x06\x20\x0f", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* af out - Si2177_ATV_AF_OUT_PROP */			/* BRL */
+		memcpy(cmd.args, "\x14\x00\x0b\x06\x30\x00", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* analog CVBS output enable - Si2177_ATV_CVBS_OUT_PROP */
+		memcpy(cmd.args, "\x14\x00\x09\x06\x19\x99", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* analog video equalizer - Si2177_ATV_VIDEO_EQUALIZER_PROP */
+		memcpy(cmd.args, "\x14\x00\x08\x06\xf8\x00", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* ATV restart */
+		memcpy(cmd.args, "\x51\x00", 2);
+		cmd.wlen = 2;
+		cmd.rlen = 1;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		usleep_range(10000, 11000);
+
+		/* set analog frequency */
+		memcpy(cmd.args, "\x41\x01\x00\x00\x00\x00\x00\x00", 8);
+		cmd.args[4] = (freq >>  0) & 0xff;
+		cmd.args[5] = (freq >>  8) & 0xff;
+		cmd.args[6] = (freq >> 16) & 0xff;
+		cmd.args[7] = (freq >> 24) & 0xff;
+		cmd.wlen = 8;
+		cmd.rlen = 1;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+	}
+
 	dev->bandwidth = bandwidth;
 
 	si2157_tune_wait(client, 0); /* wait to complete, ignore any errors */
diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h
index 8579e80f7af7..aaada2bb0f21 100644
--- a/drivers/media/tuners/si2157_priv.h
+++ b/drivers/media/tuners/si2157_priv.h
@@ -72,7 +72,8 @@ struct si2157_cmd {
 			       ((dev)->part_id == SI2177))
 
 #define SUPPORTS_ATV_IF(dev) (((dev)->part_id == SI2157) || \
-			      ((dev)->part_id == SI2158))
+			      ((dev)->part_id == SI2158) || \
+			      ((dev)->part_id == SI2177))
 
 /* Old firmware namespace */
 #define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw"
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/3] media: em28xx: Add Hauppauge em2828X based 9x5 revisions
  2026-03-17 20:50 [PATCH 0/3] si2177 tuner support and new Hauppauge 9x5 devices Bradford Love
  2026-03-17 20:50 ` [PATCH 1/3] media: em28xx: driver supports two frontends, but not i2c clients Bradford Love
  2026-03-17 20:50 ` [PATCH 2/3] media: si2157: Include support for si2177 chip Bradford Love
@ 2026-03-17 20:50 ` Bradford Love
  2026-03-24 18:25   ` [PATCH v2 " Bradford Love
  2026-03-24 18:23 ` [PATCH v2 0/3] si2177 tuner support and new Hauppauge 9x5 devices Bradford Love
  3 siblings, 1 reply; 8+ messages in thread
From: Bradford Love @ 2026-03-17 20:50 UTC (permalink / raw)
  To: linux-media; +Cc: Bradford Love

The HVR-935, HVR-955, and HVR-975 have moved
from cx231xx bridge to em2828x bridge

The devices all now utilize si2177 tuner. Capabilities are:
- Digital TV
- Composite video input
- S-Video input
- Analog stereo input

HVR-955 has ATSC/QAM demod (si2168).
HVR-935 has DVB-C/T/T2 demod (lgdt3306a).
HVR-975 has both ATSC/QAM and DVB-C/T/T2 demods.

Signed-off-by: Bradford Love <brad@nextdimension.cc>
---
 drivers/media/usb/em28xx/em28xx-cards.c | 157 ++++++++++++++++++
 drivers/media/usb/em28xx/em28xx-dvb.c   | 203 ++++++++++++++++++++++++
 drivers/media/usb/em28xx/em28xx-video.c |  22 ++-
 drivers/media/usb/em28xx/em28xx.h       |   3 +
 4 files changed, 383 insertions(+), 2 deletions(-)

diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 0c5851bf4ef0..b4bafd2fa482 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -553,6 +553,36 @@ static struct em28xx_reg_seq hauppauge_usb_quadhd_atsc_reg_seq[] = {
 	{EM2874_R5E_TS2_PKT_SIZE,      0x05, 0xff,     50},
 	{-1,                           -1,   -1,       -1},
 };
+/* Hauppauge HVR-935 \ HVR-955 / HVR-975 V2 */
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2[] = {
+	{EM2874_R80_GPIO_P0_CTRL,	0xdc,	0xff,	50},
+	{EM2874_R5F_TS_ENABLE,		0x00,	0xff,	50}, /* disable TS filters */
+	{EM2874_R5D_TS1_PKT_SIZE,	0x05,	0xff,	50},
+	{-1,				-1,	-1,	-1},
+};
+
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_comp[] = {
+	{0x0b,				0x00,	0xff,		100},
+	{0x0b,				0x96,	0xff,		100},
+	{0x0b,				0x00,	0xff,		100},
+	{EM2874_R80_GPIO_P0_CTRL,	0,	EM_GPIO_5,	10},
+	{-1,				-1,	-1,		-1},
+};
+
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_television[] = {
+	{0x0b,				0x00,		0xff,		100},
+	{0x0b,				0x96,		0xff,		100},
+	{0x0b,				0x00,		0xff,		100},
+	{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_5,	EM_GPIO_5,	10},
+	{-1,				-1,		-1,		-1},
+};
+
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_dvb[] = {
+	{0x0b,		0x80,	0xff,	100},
+	{0x0b,		0x96,	0xff,	100},
+	{0x0b,		0x80,	0xff,	100},
+	{-1,		-1,	-1,	-1},
+};
 
 /*
  * MyGica USB TV Box
@@ -689,6 +719,16 @@ static struct em28xx_led hauppauge_usb_quadhd_leds[] = {
 	{-1, 0, 0, 0},
 };
 
+static struct em28xx_led hauppauge_9x5_v2_leds[] = {
+	{
+		.role      = EM28XX_LED_DIGITAL_CAPTURING,
+		.gpio_reg  = EM2874_R80_GPIO_P0_CTRL,
+		.gpio_mask = EM_GPIO_0,
+		.inverted  = 0,
+	},
+	{-1, 0, 0, 0},
+};
+
 /*
  *  Board definitions
  */
@@ -2640,6 +2680,108 @@ const struct em28xx_board em28xx_boards[] = {
 			.amux     = EM28XX_AMUX_LINE_IN,
 		} },
 	},
+	/* 2040:8360 Hauppauge HVR-935
+	 * Empia EM2828X, si2168 demod, si2177 tuner
+	 * Composite input, s-video input, analog TV, stereo audio input
+	 */
+	[EM2828X_BOARD_HAUPPAUGE_935_V2] = {
+		.name         = "Hauppauge WinTV-HVR-935",
+		.def_i2c_bus  = 1,
+		.has_dvb      = 1,
+		.vchannels    = 3,
+		.tuner_type   = TUNER_ABSENT,
+		.decoder      = EM28XX_BUILTIN,
+		.i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+		.tuner_gpio   = hauppauge_hvr_9x5_v2,
+		.dvb_gpio     = hauppauge_hvr_9x5_v2_dvb,
+		.leds         = hauppauge_9x5_v2_leds,
+		.xclk         = 0x8f,
+		.input           = { {
+			.type     = EM28XX_VMUX_COMPOSITE,
+			.vmux     = 0,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_SVIDEO,
+			.vmux     = 1,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_TELEVISION,
+			.vmux     = 2,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_television,
+
+		} },
+	},
+	/* 2040:8360 Hauppauge HVR-955
+	 * Empia EM2828X, lgdt3306a demod, si2177 tuner
+	 * Composite input, s-video input, analog TV, stereo audio input
+	 */
+	[EM2828X_BOARD_HAUPPAUGE_955_V2] = {
+		.name         = "Hauppauge WinTV-HVR-955",
+		.def_i2c_bus  = 1,
+		.has_dvb      = 1,
+		.vchannels    = 3,
+		.tuner_type   = TUNER_ABSENT,
+		.decoder      = EM28XX_BUILTIN,
+		.i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+		.tuner_gpio   = hauppauge_hvr_9x5_v2,
+		.dvb_gpio     = hauppauge_hvr_9x5_v2_dvb,
+		.leds         = hauppauge_9x5_v2_leds,
+		.xclk         = 0x8f,
+		.input           = { {
+			.type     = EM28XX_VMUX_COMPOSITE,
+			.vmux     = 0,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_SVIDEO,
+			.vmux     = 1,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_TELEVISION,
+			.vmux     = 2,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_television,
+
+		} },
+	},
+	/* 2040:8360 Hauppauge HVR-975
+	 * Empia EM2828X, si2168 demod, lgdt3306a demod, si2177 tuner
+	 * Composite input, s-video input, analog TV, stereo audio input
+	 */
+	[EM2828X_BOARD_HAUPPAUGE_975_V2] = {
+		.name         = "Hauppauge WinTV-HVR-975",
+		.def_i2c_bus  = 1,
+		.has_dvb      = 1,
+		.vchannels    = 3,
+		.tuner_type   = TUNER_ABSENT,
+		.decoder      = EM28XX_BUILTIN,
+		.i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+		.tuner_gpio   = hauppauge_hvr_9x5_v2,
+		.dvb_gpio     = hauppauge_hvr_9x5_v2_dvb,
+		.leds         = hauppauge_9x5_v2_leds,
+		.xclk         = 0x8f,
+		.input           = { {
+			.type     = EM28XX_VMUX_COMPOSITE,
+			.vmux     = 0,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_SVIDEO,
+			.vmux     = 1,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_TELEVISION,
+			.vmux     = 2,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_television,
+
+		} },
+	},
 };
 EXPORT_SYMBOL_GPL(em28xx_boards);
 
@@ -2789,6 +2931,18 @@ struct usb_device_id em28xx_id_table[] = {
 			.driver_info = EM2874_BOARD_HAUPPAUGE_USB_QUADHD },
 	{ USB_DEVICE(0x2040, 0xc220),
 			.driver_info = EM2828X_BOARD_HAUPPAUGE_USB_LIVE2 },
+	{ USB_DEVICE(0x2040, 0x0360),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_935_V2 },
+	{ USB_DEVICE(0x2040, 0x8360),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_935_V2 },
+	{ USB_DEVICE(0x2040, 0x0366),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_955_V2 },
+	{ USB_DEVICE(0x2040, 0x8366),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_955_V2 },
+	{ USB_DEVICE(0x2040, 0x036a),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_975_V2 },
+	{ USB_DEVICE(0x2040, 0x836a),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_975_V2 },
 	{ USB_DEVICE(0x0438, 0xb002),
 			.driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
 	{ USB_DEVICE(0x2001, 0xf112),
@@ -3280,6 +3434,9 @@ static void em28xx_card_setup(struct em28xx *dev)
 	case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB:
 	case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595:
 	case EM2828X_BOARD_HAUPPAUGE_USB_LIVE2:
+	case EM2828X_BOARD_HAUPPAUGE_935_V2:
+	case EM2828X_BOARD_HAUPPAUGE_955_V2:
+	case EM2828X_BOARD_HAUPPAUGE_975_V2:
 	{
 		struct tveeprom tv;
 
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 389c40a1ecdb..2f0972072a00 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -1497,6 +1497,194 @@ static int em2874_dvb_init_hauppauge_usb_quadhd(struct em28xx *dev)
 	return 0;
 }
 
+static int em2828X_dvb_init_hauppauge_wintv_935_v2(struct em28xx *dev)
+{
+	struct em28xx_dvb *dvb = dev->dvb;
+	struct i2c_adapter *adapter;
+	struct si2168_config si2168_config = {};
+	struct si2157_config si2157_config = {};
+
+	/* Hauppauge HVR-975 V2 */
+	static const struct em28xx_reg_seq hauppauge_hvr_975_v2_init[] = {
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	0,		EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{0x90,				EM_GPIO_5,	EM_GPIO_5,	50},
+		{	-1,			-1,		-1,	-1},
+	};
+
+	em28xx_gpio_set(dev, hauppauge_hvr_975_v2_init);
+
+	/* attach demod */
+	si2168_config.i2c_adapter = &adapter;
+	si2168_config.fe = &dvb->fe[0];
+	si2168_config.ts_mode = SI2168_TS_SERIAL;
+	si2168_config.ts_clock_inv = true;
+
+	dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x64, &si2168_config);
+	if (!dvb->i2c_client_demod[0]) {
+		dev_err(&dev->intf->dev, "si2168 demod initialization failure\n");
+		return -ENODEV;
+	}
+
+	/* attach tuner */
+	si2157_config.fe = dvb->fe[0];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+	si2157_config.mdev = dev->media_dev;
+#endif
+	si2157_config.if_port = 0;
+	si2157_config.inversion = true;
+	si2157_config.dont_load_firmware = 1;
+
+	dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177",
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x60, &si2157_config);
+	if (!dvb->i2c_client_tuner) {
+		dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n");
+		dvb_module_release(dvb->i2c_client_demod[0]);
+		return -ENODEV;
+	}
+
+	dev->em28xx_set_analog_freq = em28xx_set_analog_freq;
+
+	return 0;
+}
+
+static int em2828X_dvb_init_hauppauge_wintv_955_v2(struct em28xx *dev)
+{
+	struct em28xx_dvb *dvb = dev->dvb;
+	struct i2c_adapter *adapter;
+	struct lgdt3306a_config lgdt3306a_config = {};
+	struct si2157_config si2157_config = {};
+
+	/* Hauppauge HVR-955 V2 */
+	static const struct em28xx_reg_seq hauppauge_hvr_975_v2_init[] = {
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	0,		EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{0x90,				EM_GPIO_5,	EM_GPIO_5,	50},
+		{	-1,			-1,		-1,	-1},
+	};
+
+	em28xx_gpio_set(dev, hauppauge_hvr_975_v2_init);
+
+	/* attach demod */
+	lgdt3306a_config = hauppauge_01595_lgdt3306a_config;
+	lgdt3306a_config.fe = &dvb->fe[0];
+	lgdt3306a_config.i2c_adapter = &adapter;
+
+	dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL,
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x59, &lgdt3306a_config);
+	if (!dvb->i2c_client_demod[0]) {
+		dev_err(&dev->intf->dev, "lgdt3306a demod initialization failure\n");
+		return -ENODEV;
+	}
+
+	/* attach tuner */
+	si2157_config.fe = dvb->fe[0];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+	si2157_config.mdev = dev->media_dev;
+#endif
+	si2157_config.if_port = 0;
+	si2157_config.inversion = true;
+	si2157_config.dont_load_firmware = 1;
+
+	dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177",
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x60, &si2157_config);
+	if (!dvb->i2c_client_tuner) {
+		dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n");
+		dvb_module_release(dvb->i2c_client_demod[0]);
+		return -ENODEV;
+	}
+
+	dev->em28xx_set_analog_freq = em28xx_set_analog_freq;
+
+	return 0;
+}
+
+static int em2828X_dvb_init_hauppauge_wintv_975_v2(struct em28xx *dev)
+{
+	struct em28xx_dvb *dvb = dev->dvb;
+	struct i2c_adapter *adapter;
+	struct i2c_adapter *adapter2;
+	struct lgdt3306a_config lgdt3306a_config = {};
+	struct si2168_config si2168_config = {};
+	struct si2157_config si2157_config = {};
+
+	/* Hauppauge HVR-975 V2 */
+	static const struct em28xx_reg_seq hauppauge_hvr_975_v2_init[] = {
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	0,		EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{0x90,				EM_GPIO_5,	EM_GPIO_5,	50},
+		{-1,				-1,		-1,		-1},
+	};
+
+	em28xx_gpio_set(dev, hauppauge_hvr_975_v2_init);
+
+	/* attach demod */
+	lgdt3306a_config = hauppauge_01595_lgdt3306a_config;
+	lgdt3306a_config.fe = &dvb->fe[0];
+	lgdt3306a_config.i2c_adapter = &adapter;
+
+	dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL,
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x59, &lgdt3306a_config);
+	if (!dvb->i2c_client_demod[0]) {
+		dev_err(&dev->intf->dev, "lgdt3306a demod initialization failure\n");
+		return -ENODEV;
+	}
+
+	/* attach demod */
+	si2168_config.i2c_adapter = &adapter2;
+	si2168_config.fe = &dvb->fe[1];
+	si2168_config.ts_mode = SI2168_TS_SERIAL;
+	si2168_config.ts_clock_inv = true;
+
+	dvb->i2c_client_demod[1] = dvb_module_probe("si2168", NULL,
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x64, &si2168_config);
+	if (!dvb->i2c_client_demod[1]) {
+		dev_err(&dev->intf->dev, "si2168 demod initialization failure\n");
+		dvb_module_release(dvb->i2c_client_demod[0]);
+		return -ENODEV;
+	}
+
+	dvb->fe[1]->id = 1;
+
+	/* attach tuner */
+	si2157_config.fe = dvb->fe[0];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+	si2157_config.mdev = dev->media_dev;
+#endif
+	si2157_config.if_port = 0;
+	si2157_config.inversion = true;
+	si2157_config.dont_load_firmware = 1;
+
+	dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177",
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x60, &si2157_config);
+	if (!dvb->i2c_client_tuner) {
+		dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n");
+		dvb_module_release(dvb->i2c_client_demod[1]);
+		dvb_module_release(dvb->i2c_client_demod[0]);
+		return -ENODEV;
+	}
+
+	dvb->fe[1]->tuner_priv = dvb->fe[0]->tuner_priv;
+
+	memcpy(&dvb->fe[1]->ops.tuner_ops,
+		&dvb->fe[0]->ops.tuner_ops, sizeof(struct dvb_tuner_ops));
+
+	dev->em28xx_set_analog_freq = em28xx_set_analog_freq;
+
+	return 0;
+}
+
 static int em28xx_dvb_init(struct em28xx *dev)
 {
 	int result = 0, dvb_alt = 0;
@@ -1990,6 +2178,21 @@ static int em28xx_dvb_init(struct em28xx *dev)
 		if (result)
 			goto out_free;
 		break;
+	case EM2828X_BOARD_HAUPPAUGE_935_V2:
+		result = em2828X_dvb_init_hauppauge_wintv_935_v2(dev);
+		if (result)
+			goto out_free;
+		break;
+	case EM2828X_BOARD_HAUPPAUGE_955_V2:
+		result = em2828X_dvb_init_hauppauge_wintv_955_v2(dev);
+		if (result)
+			goto out_free;
+		break;
+	case EM2828X_BOARD_HAUPPAUGE_975_V2:
+		result = em2828X_dvb_init_hauppauge_wintv_975_v2(dev);
+		if (result)
+			goto out_free;
+		break;
 	default:
 		dev_err(&dev->intf->dev,
 			"The frontend of your DVB/ATSC card isn't supported yet\n");
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index 578ba3d88d01..a172cc1e2604 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -171,7 +171,14 @@ static int em28xx_vbi_supported(struct em28xx *dev)
 
 static int em28xx_analogtv_supported(struct em28xx *dev)
 {
-	return 0;
+	switch (dev->model) {
+	case EM2828X_BOARD_HAUPPAUGE_935_V2:
+	case EM2828X_BOARD_HAUPPAUGE_955_V2:
+	case EM2828X_BOARD_HAUPPAUGE_975_V2:
+		return 1;
+	default:
+		return 0;
+	};
 }
 
 /*
@@ -2061,7 +2068,18 @@ static int vidioc_s_frequency(struct file *file, void *priv,
 	if (f->tuner != 0)
 		return -EINVAL;
 
-	v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f);
+	switch (dev->model) {
+	case EM2828X_BOARD_HAUPPAUGE_935_V2:
+	case EM2828X_BOARD_HAUPPAUGE_955_V2:
+	case EM2828X_BOARD_HAUPPAUGE_975_V2:
+		if (dev->em28xx_set_analog_freq)
+			dev->em28xx_set_analog_freq(dev, f->frequency);
+		break;
+	default:
+		v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f);
+		break;
+	}
+
 	v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, g_frequency, &new_freq);
 	v4l2->frequency = new_freq.frequency;
 
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index 9fcaebd78bae..ae4010c55623 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -145,6 +145,9 @@
 #define EM2874_BOARD_HAUPPAUGE_USB_QUADHD         106
 #define EM2860_BOARD_MYGICA_UTV3                  107
 #define EM2828X_BOARD_HAUPPAUGE_USB_LIVE2         108
+#define EM2828X_BOARD_HAUPPAUGE_935_V2            109
+#define EM2828X_BOARD_HAUPPAUGE_955_V2            110
+#define EM2828X_BOARD_HAUPPAUGE_975_V2            111
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 0/3] si2177 tuner support and new Hauppauge 9x5 devices
  2026-03-17 20:50 [PATCH 0/3] si2177 tuner support and new Hauppauge 9x5 devices Bradford Love
                   ` (2 preceding siblings ...)
  2026-03-17 20:50 ` [PATCH 3/3] media: em28xx: Add Hauppauge em2828X based 9x5 revisions Bradford Love
@ 2026-03-24 18:23 ` Bradford Love
  3 siblings, 0 replies; 8+ messages in thread
From: Bradford Love @ 2026-03-24 18:23 UTC (permalink / raw)
  To: linux-media, hverkuil+cisco; +Cc: Bradford Love

Hauppauge 9x5 devices, such as HVR-935, HVR-955Q, and HVR-975
have moved from cx231xx usb bridge to Empia em2828x bridge.

This series depends on my previous series:

media: em28xx: Add support for Empia em2828X bridge


The following USB device id's are new:

2040:0360 - HVR-935 ISOC transport
2040:8360 - HVR-935 Bulk transport

2040:0366 - HVR-955 ISOC transport
2040:8366 - HVR-955 Bulk transport

2040:036a - HVR-975 ISOC transport
2040:836a - HVR-975 Bulk transport

The new devices have all capabilities as previous generation.
- 935 - DVB-T/T2/C, analog tv, composite, s-video, stereo audio
- 955 - QAM/ATSC, analog tv, composite, s-video, stereo audio
- 975 - DVB-T/T2/C + QAM/ATSC, analog tv, composite, s-video, stereo audio

DVB demod: si2168
ATSC demod: lgdt3306a
tuner: si2177

The em28xx driver needed to be expanded slightly to accommodate
a second i2c client frontend, it already was setup for "old"
style multi frontend use already.

The si2177 tuner chip puts out decoded CVBS video, and therefore
requires additional configuration when setting up analog tuning parameters.

---
Changes since v1:
- Renamed some variables
- Fixed some comments
- Explicitly added USB id's affected


Regards,

Bradford



Bradford Love (3):
  em28xx: driver supports two frontends, but not i2c clients
  si2157: Include support for si2177 chip
  em28xx: Add Hauppauge em2828X based 9x5 revisions

 drivers/media/tuners/si2157.c           | 208 +++++++++++++----
 drivers/media/tuners/si2157_priv.h      |   3 +-
 drivers/media/usb/em28xx/em28xx-cards.c | 157 +++++++++++++
 drivers/media/usb/em28xx/em28xx-dvb.c   | 284 ++++++++++++++++++++----
 drivers/media/usb/em28xx/em28xx-video.c |  22 +-
 drivers/media/usb/em28xx/em28xx.h       |   3 +
 6 files changed, 588 insertions(+), 89 deletions(-)

-- 
2.35.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 1/3] media: em28xx: driver supports two frontends, but not i2c clients
  2026-03-17 20:50 ` [PATCH 1/3] media: em28xx: driver supports two frontends, but not i2c clients Bradford Love
@ 2026-03-24 18:24   ` Bradford Love
  0 siblings, 0 replies; 8+ messages in thread
From: Bradford Love @ 2026-03-24 18:24 UTC (permalink / raw)
  To: linux-media, hverkuil+cisco; +Cc: Bradford Love

Fix em28xx driver to accommodate a device with two i2c client demods.

The em28xx driver already had a multi frontend aware device struct,
with two fe, just not two i2c client demods. Since Hauppauge HVR975
has transitioned from cx231xx to em2828X, this is required for full
functionality of the devices.

Signed-off-by: Bradford Love <brad@nextdimension.cc>
---
Changes since v1:
- patches refreshed

 drivers/media/usb/em28xx/em28xx-dvb.c | 81 ++++++++++++++-------------
 1 file changed, 42 insertions(+), 39 deletions(-)

diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 4d632ca91e30..389c40a1ecdb 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -97,7 +97,7 @@ struct em28xx_dvb {
 	struct semaphore      pll_mutex;
 	bool			dont_attach_fe1;
 	int			lna_gpio;
-	struct i2c_client	*i2c_client_demod;
+	struct i2c_client	*i2c_client_demod[2];
 	struct i2c_client	*i2c_client_tuner;
 	struct i2c_client	*i2c_client_sec;
 };
@@ -854,14 +854,14 @@ static void px_bcud_init(struct em28xx *dev)
 	};
 	em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x46);
 	/* sleeping ISDB-T */
-	dev->dvb->i2c_client_demod->addr = 0x14;
+	dev->dvb->i2c_client_demod[0]->addr = 0x14;
 	for (i = 0; i < ARRAY_SIZE(regs1); i++)
-		i2c_master_send(dev->dvb->i2c_client_demod,
+		i2c_master_send(dev->dvb->i2c_client_demod[0],
 				regs1[i].r, regs1[i].len);
 	/* sleeping ISDB-S */
-	dev->dvb->i2c_client_demod->addr = 0x15;
+	dev->dvb->i2c_client_demod[0]->addr = 0x15;
 	for (i = 0; i < ARRAY_SIZE(regs2); i++)
-		i2c_master_send(dev->dvb->i2c_client_demod, regs2[i].r,
+		i2c_master_send(dev->dvb->i2c_client_demod[0], regs2[i].r,
 				regs2[i].len);
 	for (i = 0; i < ARRAY_SIZE(gpio); i++) {
 		em28xx_write_reg_bits(dev, gpio[i].reg, gpio[i].val,
@@ -1155,13 +1155,13 @@ static int em28174_dvb_init_pctv_460e(struct em28xx *dev)
 	tda10071_pdata.pll_multiplier = 20;
 	tda10071_pdata.tuner_i2c_addr = 0x14;
 
-	dvb->i2c_client_demod = dvb_module_probe("tda10071", "tda10071_cx24118",
+	dvb->i2c_client_demod[0] = dvb_module_probe("tda10071", "tda10071_cx24118",
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x55, &tda10071_pdata);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
-	dvb->fe[0] = tda10071_pdata.get_dvb_frontend(dvb->i2c_client_demod);
+	dvb->fe[0] = tda10071_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]);
 
 	/* attach SEC */
 	a8293_pdata.dvb_frontend = dvb->fe[0];
@@ -1170,7 +1170,7 @@ static int em28174_dvb_init_pctv_460e(struct em28xx *dev)
 					       &dev->i2c_adap[dev->def_i2c_bus],
 					       0x08, &a8293_pdata);
 	if (!dvb->i2c_client_sec) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1193,14 +1193,14 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev)
 	m88ds3103_pdata.ts_clk_pol = 1;
 	m88ds3103_pdata.agc = 0x99;
 
-	dvb->i2c_client_demod = dvb_module_probe("m88ds3103", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x68, &m88ds3103_pdata);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
-	dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod);
-	i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod);
+	dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]);
+	i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod[0]);
 
 	/* attach tuner */
 	ts2020_config.fe = dvb->fe[0];
@@ -1209,7 +1209,7 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev)
 						 i2c_adapter,
 						 0x60, &ts2020_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1230,7 +1230,7 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev)
 					       0x08, &a8293_pdata);
 	if (!dvb->i2c_client_sec) {
 		dvb_module_release(dvb->i2c_client_tuner);
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1254,15 +1254,15 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev)
 	m88ds3103_pdata.agc = 0x99;
 	m88ds3103_pdata.agc_inv = 0;
 	m88ds3103_pdata.spec_inv = 0;
-	dvb->i2c_client_demod = dvb_module_probe("m88ds3103", "m88ds3103b",
+	dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", "m88ds3103b",
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x6a, &m88ds3103_pdata);
 
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
-	dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod);
-	i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod);
+	dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod[0]);
+	i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod[0]);
 
 	/* attach tuner */
 	ts2020_config.fe = dvb->fe[0];
@@ -1270,7 +1270,7 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev)
 						 i2c_adapter,
 						 0x60, &ts2020_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1285,7 +1285,7 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev)
 					       0x08, &a8293_pdata);
 	if (!dvb->i2c_client_sec) {
 		dvb_module_release(dvb->i2c_client_tuner);
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1305,10 +1305,10 @@ static int em28178_dvb_init_pctv_292e(struct em28xx *dev)
 	si2168_config.ts_mode = SI2168_TS_PARALLEL;
 	si2168_config.spectral_inversion = true;
 
-	dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x64, &si2168_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	/* attach tuner */
@@ -1321,7 +1321,7 @@ static int em28178_dvb_init_pctv_292e(struct em28xx *dev)
 						 adapter,
 						 0x60, &si2157_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 	dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna;
@@ -1341,10 +1341,10 @@ static int em28178_dvb_init_terratec_t2_stick_hd(struct em28xx *dev)
 	si2168_config.fe = &dvb->fe[0];
 	si2168_config.ts_mode = SI2168_TS_PARALLEL;
 
-	dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x64, &si2168_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	/* attach tuner */
@@ -1358,7 +1358,7 @@ static int em28178_dvb_init_terratec_t2_stick_hd(struct em28xx *dev)
 						 adapter,
 						 0x60, &si2157_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1372,10 +1372,10 @@ static int em28178_dvb_init_plex_px_bcud(struct em28xx *dev)
 	struct qm1d1c0042_config qm1d1c0042_config = {};
 
 	/* attach demod */
-	dvb->i2c_client_demod = dvb_module_probe("tc90522", "tc90522sat",
+	dvb->i2c_client_demod[0] = dvb_module_probe("tc90522", "tc90522sat",
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 0x15, &tc90522_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	/* attach tuner */
@@ -1386,7 +1386,7 @@ static int em28178_dvb_init_plex_px_bcud(struct em28xx *dev)
 						 tc90522_config.tuner_i2c,
 						 0x61, &qm1d1c0042_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1411,10 +1411,10 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_dvb(struct em28xx *dev)
 	si2168_config.spectral_inversion = true;
 	addr = (dev->ts == PRIMARY_TS) ? 0x64 : 0x67;
 
-	dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 addr, &si2168_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	/* attach tuner */
@@ -1430,7 +1430,7 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_dvb(struct em28xx *dev)
 						 adapter,
 						 addr, &si2157_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1451,10 +1451,10 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev)
 	lgdt3306a_config.i2c_adapter = &adapter;
 	addr = (dev->ts == PRIMARY_TS) ? 0x59 : 0x0e;
 
-	dvb->i2c_client_demod = dvb_module_probe("lgdt3306a", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 addr, &lgdt3306a_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	/* attach tuner */
@@ -1470,7 +1470,7 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev)
 						 adapter,
 						 addr, &si2157_config);
 	if (!dvb->i2c_client_tuner) {
-		dvb_module_release(dvb->i2c_client_demod);
+		dvb_module_release(dvb->i2c_client_demod[0]);
 		return -ENODEV;
 	}
 
@@ -1488,10 +1488,10 @@ static int em2874_dvb_init_hauppauge_usb_quadhd(struct em28xx *dev)
 	mxl692_config.fe = &dvb->fe[0];
 	addr = (dev->ts == PRIMARY_TS) ? 0x60 : 0x63;
 
-	dvb->i2c_client_demod = dvb_module_probe("mxl692", NULL,
+	dvb->i2c_client_demod[0] = dvb_module_probe("mxl692", NULL,
 						 &dev->i2c_adap[dev->def_i2c_bus],
 						 addr, &mxl692_config);
-	if (!dvb->i2c_client_demod)
+	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
 
 	return 0;
@@ -1522,6 +1522,8 @@ static int em28xx_dvb_init(struct em28xx *dev)
 	dev->dvb = dvb;
 	dvb->fe[0] = NULL;
 	dvb->fe[1] = NULL;
+	dvb->i2c_client_demod[0] = NULL;
+	dvb->i2c_client_demod[1] = NULL;
 
 	/* pre-allocate DVB usb transfer buffers */
 	if (dev->dvb_xfer_bulk) {
@@ -2083,7 +2085,8 @@ static int em28xx_dvb_fini(struct em28xx *dev)
 	/* release I2C module bindings */
 	dvb_module_release(dvb->i2c_client_sec);
 	dvb_module_release(dvb->i2c_client_tuner);
-	dvb_module_release(dvb->i2c_client_demod);
+	dvb_module_release(dvb->i2c_client_demod[1]);
+	dvb_module_release(dvb->i2c_client_demod[0]);
 
 	kfree(dvb);
 	dev->dvb = NULL;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 2/3] media: si2157: Include support for si2177 chip
  2026-03-17 20:50 ` [PATCH 2/3] media: si2157: Include support for si2177 chip Bradford Love
@ 2026-03-24 18:25   ` Bradford Love
  0 siblings, 0 replies; 8+ messages in thread
From: Bradford Love @ 2026-03-24 18:25 UTC (permalink / raw)
  To: linux-media, hverkuil+cisco; +Cc: Bradford Love

The si2177 is very closely related to si2157, with slight differences
when doing analog operations. Digital is left as is, but analog needs
to be configured specially because the signal is internally demodulated
and CVBS video is output directly from the tuner.

Verified locked and working with all supported standards.

Signed-off-by: Bradford Love <brad@nextdimension.cc>
---
Changes since v1:
- Removed debug comment

 drivers/media/tuners/si2157.c      | 208 ++++++++++++++++++++++-------
 drivers/media/tuners/si2157_priv.h |   3 +-
 2 files changed, 163 insertions(+), 48 deletions(-)

diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
index b041cd854732..93ab8e0014ee 100644
--- a/drivers/media/tuners/si2157.c
+++ b/drivers/media/tuners/si2157.c
@@ -687,60 +687,104 @@ static int si2157_set_analog_params(struct dvb_frontend *fe,
 		params->mode, system, std, params->frequency,
 		freq, if_frequency, bandwidth);
 
-	/* set analog IF port */
-	memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6);
-	/* in using dev->if_port, we assume analog and digital IF's */
-	/*   are always on different ports */
-	/* assumes if_port definition is 0 or 1 for digital out */
-	cmd.args[4] = (dev->if_port == 1) ? 8 : 10;
-	/* Analog AGC assumed external */
-	cmd.args[5] = (dev->if_port == 1) ? 2 : 1;
-	cmd.wlen = 6;
-	cmd.rlen = 4;
-	ret = si2157_cmd_execute(client, &cmd);
-	if (ret)
-		goto err;
+	if (dev->part_id != SI2177) {
+		/* AGC speed */
+		memcpy(cmd.args, "\x14\x00\x11\x06\x00\x00", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
 
-	/* set analog IF output config */
-	memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6);
-	cmd.wlen = 6;
-	cmd.rlen = 4;
-	ret = si2157_cmd_execute(client, &cmd);
-	if (ret)
-		goto err;
+		/* set analog IF port */
+		memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6);
+		/* in using dev->if_port, we assume analog and digital IF's */
+		/*   are always on different ports */
+		/* assumes if_port definition is 0 or 1 for digital out */
+		cmd.args[4] = (dev->if_port == 1) ? 8 : 10;
+		/* Analog AGC assumed external */
+		cmd.args[5] = (dev->if_port == 1) ? 2 : 1;
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
 
-	/* make this distinct from a digital IF */
-	dev->if_frequency = if_frequency | 1;
+		/* set analog IF output config */
+		memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
 
-	/* calc and set tuner analog if center frequency */
-	if_frequency = if_frequency + 1250000 - (bandwidth / 2);
-	dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency);
 
-	memcpy(cmd.args, "\x14\x00\x0C\x06", 4);
-	cmd.args[4] = (if_frequency / 1000) & 0xff;
-	cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff;
-	cmd.wlen = 6;
-	cmd.rlen = 4;
-	ret = si2157_cmd_execute(client, &cmd);
-	if (ret)
-		goto err;
+		/* make this distinct from a digital IF */
+		dev->if_frequency = if_frequency | 1;
 
-	/* set analog AGC config */
-	memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6);
-	cmd.wlen = 6;
-	cmd.rlen = 4;
-	ret = si2157_cmd_execute(client, &cmd);
-	if (ret)
-		goto err;
+		/* calc and set tuner analog if center frequency */
+		if_frequency = if_frequency + 1250000 - (bandwidth / 2);
+		dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency);
+
+		memcpy(cmd.args, "\x14\x00\x0C\x06", 4);
+		cmd.args[4] = (if_frequency / 1000) & 0xff;
+		cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff;
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* set analog AGC config */
+		memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* set analog video mode */
+		memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6);
+		cmd.args[4] = system | color;
+		/* can use dev->inversion if assumed applies to both digital/analog */
+		if (invert_analog)
+			cmd.args[5] |= 0x02;
+		cmd.wlen = 6;
+		cmd.rlen = 1;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+	} else {
+		/* analog video equalizer - Si2177_ATV_VIDEO_EQUALIZER_PROP */
+		memcpy(cmd.args, "\x14\x00\x08\x06\xf8\x00", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* analog CVBS output properties - Si2177_ATV_CVBS_OUT_FINE_PROP */
+		memcpy(cmd.args, "\x14\x00\x14\x06\x00\x64", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
 
-	/* set analog video mode */
-	memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6);
-	cmd.args[4] = system | color;
-	/* can use dev->inversion if assumed applies to both digital/analog */
-	if (invert_analog)
-		cmd.args[5] |= 0x02;
+		dev_err(&client->dev, "%s() Settings HSYNC\n", __func__);
+		/* HSYNC output - Si2177_ATV_HSYNC_OUT_PROP */
+		memcpy(cmd.args, "\x14\x00\x27\x06\xa8\x00", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+	}
+
+	/* AFC qcuisition range 1.5MHz */
+	memcpy(cmd.args, "\x14\x00\x10\x06\xdc\x05", 6);
 	cmd.wlen = 6;
-	cmd.rlen = 1;
+	cmd.rlen = 4;
 	ret = si2157_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
@@ -757,6 +801,76 @@ static int si2157_set_analog_params(struct dvb_frontend *fe,
 	if (ret)
 		goto err;
 
+	if (dev->part_id == SI2177) {
+		/* Ref driver tunes, resets registers, then retunes, leaving steps as is */
+		/* set analog video mode - Si2158_ATV_VIDEO_MODE_PROP */
+		memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6);
+		cmd.args[4] = system | color;
+		/* can use dev->inversion if assumed applies to both digital/analog */
+		if (invert_analog)
+			cmd.args[5] |= 0x02;
+
+		cmd.wlen = 6;
+		cmd.rlen = 1;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* Si2177_ATV_AUDIO_MODE_PROP */
+		memcpy(cmd.args, "\x14\x00\x02\x06\x20\x0f", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* af out - Si2177_ATV_AF_OUT_PROP */			/* BRL */
+		memcpy(cmd.args, "\x14\x00\x0b\x06\x30\x00", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* analog CVBS output enable - Si2177_ATV_CVBS_OUT_PROP */
+		memcpy(cmd.args, "\x14\x00\x09\x06\x19\x99", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* analog video equalizer - Si2177_ATV_VIDEO_EQUALIZER_PROP */
+		memcpy(cmd.args, "\x14\x00\x08\x06\xf8\x00", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 4;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		/* ATV restart */
+		memcpy(cmd.args, "\x51\x00", 2);
+		cmd.wlen = 2;
+		cmd.rlen = 1;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		usleep_range(10000, 11000);
+
+		/* set analog frequency */
+		memcpy(cmd.args, "\x41\x01\x00\x00\x00\x00\x00\x00", 8);
+		cmd.args[4] = (freq >>  0) & 0xff;
+		cmd.args[5] = (freq >>  8) & 0xff;
+		cmd.args[6] = (freq >> 16) & 0xff;
+		cmd.args[7] = (freq >> 24) & 0xff;
+		cmd.wlen = 8;
+		cmd.rlen = 1;
+		ret = si2157_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+	}
+
 	dev->bandwidth = bandwidth;
 
 	si2157_tune_wait(client, 0); /* wait to complete, ignore any errors */
diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h
index 8579e80f7af7..aaada2bb0f21 100644
--- a/drivers/media/tuners/si2157_priv.h
+++ b/drivers/media/tuners/si2157_priv.h
@@ -72,7 +72,8 @@ struct si2157_cmd {
 			       ((dev)->part_id == SI2177))
 
 #define SUPPORTS_ATV_IF(dev) (((dev)->part_id == SI2157) || \
-			      ((dev)->part_id == SI2158))
+			      ((dev)->part_id == SI2158) || \
+			      ((dev)->part_id == SI2177))
 
 /* Old firmware namespace */
 #define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw"
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 3/3] media: em28xx: Add Hauppauge em2828X based 9x5 revisions
  2026-03-17 20:50 ` [PATCH 3/3] media: em28xx: Add Hauppauge em2828X based 9x5 revisions Bradford Love
@ 2026-03-24 18:25   ` Bradford Love
  0 siblings, 0 replies; 8+ messages in thread
From: Bradford Love @ 2026-03-24 18:25 UTC (permalink / raw)
  To: linux-media, hverkuil+cisco; +Cc: Bradford Love

The Hauppauge HVR-935, HVR-955, and HVR-975 have moved
from cx231xx bridge to em2828x bridge.

The following USB device id's are new:
2040:0360 - HVR-935 ISOC transport
2040:8360 - HVR-935 Bulk transport
2040:0366 - HVR-955 ISOC transport
2040:8366 - HVR-955 Bulk transport
2040:036a - HVR-975 ISOC transport
2040:836a - HVR-975 Bulk transport

The devices all now utilize si2177 tuner. Capabilities are:
- Digital TV
- Composite video input
- S-Video input
- Analog stereo input

HVR-935 has DVB-C/T/T2 demod (si2168).
HVR-955 has ATSC/QAM demod (lgdt3306a).
HVR-975 has both ATSC/QAM and DVB-C/T/T2 demods.

Signed-off-by: Bradford Love <brad@nextdimension.cc>
---
Changes since v1:
- Fix incorrect pid's in board profile descriptions
- Fix some variable names
- Include all usb vid:pid combos for the 9x5 devices
- Fixed listed demods in comment

 drivers/media/usb/em28xx/em28xx-cards.c | 157 ++++++++++++++++++
 drivers/media/usb/em28xx/em28xx-dvb.c   | 203 ++++++++++++++++++++++++
 drivers/media/usb/em28xx/em28xx-video.c |  22 ++-
 drivers/media/usb/em28xx/em28xx.h       |   3 +
 4 files changed, 383 insertions(+), 2 deletions(-)

diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 0c5851bf4ef0..b4bafd2fa482 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -553,6 +553,36 @@ static struct em28xx_reg_seq hauppauge_usb_quadhd_atsc_reg_seq[] = {
 	{EM2874_R5E_TS2_PKT_SIZE,      0x05, 0xff,     50},
 	{-1,                           -1,   -1,       -1},
 };
+/* Hauppauge HVR-935 \ HVR-955 / HVR-975 V2 */
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2[] = {
+	{EM2874_R80_GPIO_P0_CTRL,	0xdc,	0xff,	50},
+	{EM2874_R5F_TS_ENABLE,		0x00,	0xff,	50}, /* disable TS filters */
+	{EM2874_R5D_TS1_PKT_SIZE,	0x05,	0xff,	50},
+	{-1,				-1,	-1,	-1},
+};
+
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_comp[] = {
+	{0x0b,				0x00,	0xff,		100},
+	{0x0b,				0x96,	0xff,		100},
+	{0x0b,				0x00,	0xff,		100},
+	{EM2874_R80_GPIO_P0_CTRL,	0,	EM_GPIO_5,	10},
+	{-1,				-1,	-1,		-1},
+};
+
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_television[] = {
+	{0x0b,				0x00,		0xff,		100},
+	{0x0b,				0x96,		0xff,		100},
+	{0x0b,				0x00,		0xff,		100},
+	{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_5,	EM_GPIO_5,	10},
+	{-1,				-1,		-1,		-1},
+};
+
+static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_dvb[] = {
+	{0x0b,		0x80,	0xff,	100},
+	{0x0b,		0x96,	0xff,	100},
+	{0x0b,		0x80,	0xff,	100},
+	{-1,		-1,	-1,	-1},
+};
 
 /*
  * MyGica USB TV Box
@@ -689,6 +719,16 @@ static struct em28xx_led hauppauge_usb_quadhd_leds[] = {
 	{-1, 0, 0, 0},
 };
 
+static struct em28xx_led hauppauge_9x5_v2_leds[] = {
+	{
+		.role      = EM28XX_LED_DIGITAL_CAPTURING,
+		.gpio_reg  = EM2874_R80_GPIO_P0_CTRL,
+		.gpio_mask = EM_GPIO_0,
+		.inverted  = 0,
+	},
+	{-1, 0, 0, 0},
+};
+
 /*
  *  Board definitions
  */
@@ -2640,6 +2680,108 @@ const struct em28xx_board em28xx_boards[] = {
 			.amux     = EM28XX_AMUX_LINE_IN,
 		} },
 	},
+	/* 2040:8360 Hauppauge HVR-935
+	 * Empia EM2828X, si2168 demod, si2177 tuner
+	 * Composite input, s-video input, analog TV, stereo audio input
+	 */
+	[EM2828X_BOARD_HAUPPAUGE_935_V2] = {
+		.name         = "Hauppauge WinTV-HVR-935",
+		.def_i2c_bus  = 1,
+		.has_dvb      = 1,
+		.vchannels    = 3,
+		.tuner_type   = TUNER_ABSENT,
+		.decoder      = EM28XX_BUILTIN,
+		.i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+		.tuner_gpio   = hauppauge_hvr_9x5_v2,
+		.dvb_gpio     = hauppauge_hvr_9x5_v2_dvb,
+		.leds         = hauppauge_9x5_v2_leds,
+		.xclk         = 0x8f,
+		.input           = { {
+			.type     = EM28XX_VMUX_COMPOSITE,
+			.vmux     = 0,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_SVIDEO,
+			.vmux     = 1,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_TELEVISION,
+			.vmux     = 2,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_television,
+
+		} },
+	},
+	/* 2040:8366 Hauppauge HVR-955
+	 * Empia EM2828X, lgdt3306a demod, si2177 tuner
+	 * Composite input, s-video input, analog TV, stereo audio input
+	 */
+	[EM2828X_BOARD_HAUPPAUGE_955_V2] = {
+		.name         = "Hauppauge WinTV-HVR-955",
+		.def_i2c_bus  = 1,
+		.has_dvb      = 1,
+		.vchannels    = 3,
+		.tuner_type   = TUNER_ABSENT,
+		.decoder      = EM28XX_BUILTIN,
+		.i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+		.tuner_gpio   = hauppauge_hvr_9x5_v2,
+		.dvb_gpio     = hauppauge_hvr_9x5_v2_dvb,
+		.leds         = hauppauge_9x5_v2_leds,
+		.xclk         = 0x8f,
+		.input           = { {
+			.type     = EM28XX_VMUX_COMPOSITE,
+			.vmux     = 0,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_SVIDEO,
+			.vmux     = 1,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_TELEVISION,
+			.vmux     = 2,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_television,
+
+		} },
+	},
+	/* 2040:836a Hauppauge HVR-975
+	 * Empia EM2828X, si2168 demod, lgdt3306a demod, si2177 tuner
+	 * Composite input, s-video input, analog TV, stereo audio input
+	 */
+	[EM2828X_BOARD_HAUPPAUGE_975_V2] = {
+		.name         = "Hauppauge WinTV-HVR-975",
+		.def_i2c_bus  = 1,
+		.has_dvb      = 1,
+		.vchannels    = 3,
+		.tuner_type   = TUNER_ABSENT,
+		.decoder      = EM28XX_BUILTIN,
+		.i2c_speed    = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+		.tuner_gpio   = hauppauge_hvr_9x5_v2,
+		.dvb_gpio     = hauppauge_hvr_9x5_v2_dvb,
+		.leds         = hauppauge_9x5_v2_leds,
+		.xclk         = 0x8f,
+		.input           = { {
+			.type     = EM28XX_VMUX_COMPOSITE,
+			.vmux     = 0,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_SVIDEO,
+			.vmux     = 1,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_comp,
+		}, {
+			.type     = EM28XX_VMUX_TELEVISION,
+			.vmux     = 2,
+			.amux     = EM28XX_AMUX_LINE_IN,
+			.gpio     = hauppauge_hvr_9x5_v2_television,
+
+		} },
+	},
 };
 EXPORT_SYMBOL_GPL(em28xx_boards);
 
@@ -2789,6 +2931,18 @@ struct usb_device_id em28xx_id_table[] = {
 			.driver_info = EM2874_BOARD_HAUPPAUGE_USB_QUADHD },
 	{ USB_DEVICE(0x2040, 0xc220),
 			.driver_info = EM2828X_BOARD_HAUPPAUGE_USB_LIVE2 },
+	{ USB_DEVICE(0x2040, 0x0360),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_935_V2 },
+	{ USB_DEVICE(0x2040, 0x8360),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_935_V2 },
+	{ USB_DEVICE(0x2040, 0x0366),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_955_V2 },
+	{ USB_DEVICE(0x2040, 0x8366),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_955_V2 },
+	{ USB_DEVICE(0x2040, 0x036a),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_975_V2 },
+	{ USB_DEVICE(0x2040, 0x836a),
+			.driver_info = EM2828X_BOARD_HAUPPAUGE_975_V2 },
 	{ USB_DEVICE(0x0438, 0xb002),
 			.driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
 	{ USB_DEVICE(0x2001, 0xf112),
@@ -3280,6 +3434,9 @@ static void em28xx_card_setup(struct em28xx *dev)
 	case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB:
 	case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595:
 	case EM2828X_BOARD_HAUPPAUGE_USB_LIVE2:
+	case EM2828X_BOARD_HAUPPAUGE_935_V2:
+	case EM2828X_BOARD_HAUPPAUGE_955_V2:
+	case EM2828X_BOARD_HAUPPAUGE_975_V2:
 	{
 		struct tveeprom tv;
 
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 389c40a1ecdb..2f0972072a00 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -1497,6 +1497,194 @@ static int em2874_dvb_init_hauppauge_usb_quadhd(struct em28xx *dev)
 	return 0;
 }
 
+static int em2828X_dvb_init_hauppauge_wintv_935_v2(struct em28xx *dev)
+{
+	struct em28xx_dvb *dvb = dev->dvb;
+	struct i2c_adapter *adapter;
+	struct si2168_config si2168_config = {};
+	struct si2157_config si2157_config = {};
+
+	/* Hauppauge HVR-9x5 V2 */
+	static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_init[] = {
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	0,		EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{0x90,				EM_GPIO_5,	EM_GPIO_5,	50},
+		{	-1,			-1,		-1,	-1},
+	};
+
+	em28xx_gpio_set(dev, hauppauge_hvr_9x5_v2_init);
+
+	/* attach demod */
+	si2168_config.i2c_adapter = &adapter;
+	si2168_config.fe = &dvb->fe[0];
+	si2168_config.ts_mode = SI2168_TS_SERIAL;
+	si2168_config.ts_clock_inv = true;
+
+	dvb->i2c_client_demod[0] = dvb_module_probe("si2168", NULL,
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x64, &si2168_config);
+	if (!dvb->i2c_client_demod[0]) {
+		dev_err(&dev->intf->dev, "si2168 demod initialization failure\n");
+		return -ENODEV;
+	}
+
+	/* attach tuner */
+	si2157_config.fe = dvb->fe[0];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+	si2157_config.mdev = dev->media_dev;
+#endif
+	si2157_config.if_port = 0;
+	si2157_config.inversion = true;
+	si2157_config.dont_load_firmware = 1;
+
+	dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177",
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x60, &si2157_config);
+	if (!dvb->i2c_client_tuner) {
+		dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n");
+		dvb_module_release(dvb->i2c_client_demod[0]);
+		return -ENODEV;
+	}
+
+	dev->em28xx_set_analog_freq = em28xx_set_analog_freq;
+
+	return 0;
+}
+
+static int em2828X_dvb_init_hauppauge_wintv_955_v2(struct em28xx *dev)
+{
+	struct em28xx_dvb *dvb = dev->dvb;
+	struct i2c_adapter *adapter;
+	struct lgdt3306a_config lgdt3306a_config = {};
+	struct si2157_config si2157_config = {};
+
+	/* Hauppauge HVR-9x5 V2 */
+	static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_init[] = {
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	0,		EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{0x90,				EM_GPIO_5,	EM_GPIO_5,	50},
+		{	-1,			-1,		-1,	-1},
+	};
+
+	em28xx_gpio_set(dev, hauppauge_hvr_9x5_v2_init);
+
+	/* attach demod */
+	lgdt3306a_config = hauppauge_01595_lgdt3306a_config;
+	lgdt3306a_config.fe = &dvb->fe[0];
+	lgdt3306a_config.i2c_adapter = &adapter;
+
+	dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL,
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x59, &lgdt3306a_config);
+	if (!dvb->i2c_client_demod[0]) {
+		dev_err(&dev->intf->dev, "lgdt3306a demod initialization failure\n");
+		return -ENODEV;
+	}
+
+	/* attach tuner */
+	si2157_config.fe = dvb->fe[0];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+	si2157_config.mdev = dev->media_dev;
+#endif
+	si2157_config.if_port = 0;
+	si2157_config.inversion = true;
+	si2157_config.dont_load_firmware = 1;
+
+	dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177",
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x60, &si2157_config);
+	if (!dvb->i2c_client_tuner) {
+		dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n");
+		dvb_module_release(dvb->i2c_client_demod[0]);
+		return -ENODEV;
+	}
+
+	dev->em28xx_set_analog_freq = em28xx_set_analog_freq;
+
+	return 0;
+}
+
+static int em2828X_dvb_init_hauppauge_wintv_975_v2(struct em28xx *dev)
+{
+	struct em28xx_dvb *dvb = dev->dvb;
+	struct i2c_adapter *adapter;
+	struct i2c_adapter *adapter2;
+	struct lgdt3306a_config lgdt3306a_config = {};
+	struct si2168_config si2168_config = {};
+	struct si2157_config si2157_config = {};
+
+	/* Hauppauge HVR-9x5 V2 */
+	static const struct em28xx_reg_seq hauppauge_hvr_9x5_v2_init[] = {
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	0,		EM_GPIO_6,	50},
+		{EM2874_R80_GPIO_P0_CTRL,	EM_GPIO_6,	EM_GPIO_6,	50},
+		{0x90,				EM_GPIO_5,	EM_GPIO_5,	50},
+		{-1,				-1,		-1,		-1},
+	};
+
+	em28xx_gpio_set(dev, hauppauge_hvr_9x5_v2_init);
+
+	/* attach demod */
+	lgdt3306a_config = hauppauge_01595_lgdt3306a_config;
+	lgdt3306a_config.fe = &dvb->fe[0];
+	lgdt3306a_config.i2c_adapter = &adapter;
+
+	dvb->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL,
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x59, &lgdt3306a_config);
+	if (!dvb->i2c_client_demod[0]) {
+		dev_err(&dev->intf->dev, "lgdt3306a demod initialization failure\n");
+		return -ENODEV;
+	}
+
+	/* attach demod */
+	si2168_config.i2c_adapter = &adapter2;
+	si2168_config.fe = &dvb->fe[1];
+	si2168_config.ts_mode = SI2168_TS_SERIAL;
+	si2168_config.ts_clock_inv = true;
+
+	dvb->i2c_client_demod[1] = dvb_module_probe("si2168", NULL,
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x64, &si2168_config);
+	if (!dvb->i2c_client_demod[1]) {
+		dev_err(&dev->intf->dev, "si2168 demod initialization failure\n");
+		dvb_module_release(dvb->i2c_client_demod[0]);
+		return -ENODEV;
+	}
+
+	dvb->fe[1]->id = 1;
+
+	/* attach tuner */
+	si2157_config.fe = dvb->fe[0];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+	si2157_config.mdev = dev->media_dev;
+#endif
+	si2157_config.if_port = 0;
+	si2157_config.inversion = true;
+	si2157_config.dont_load_firmware = 1;
+
+	dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2177",
+						 &dev->i2c_adap[dev->def_i2c_bus],
+						 0x60, &si2157_config);
+	if (!dvb->i2c_client_tuner) {
+		dev_err(&dev->intf->dev, "si2157 tuner initialization failure\n");
+		dvb_module_release(dvb->i2c_client_demod[1]);
+		dvb_module_release(dvb->i2c_client_demod[0]);
+		return -ENODEV;
+	}
+
+	dvb->fe[1]->tuner_priv = dvb->fe[0]->tuner_priv;
+
+	memcpy(&dvb->fe[1]->ops.tuner_ops,
+		&dvb->fe[0]->ops.tuner_ops, sizeof(struct dvb_tuner_ops));
+
+	dev->em28xx_set_analog_freq = em28xx_set_analog_freq;
+
+	return 0;
+}
+
 static int em28xx_dvb_init(struct em28xx *dev)
 {
 	int result = 0, dvb_alt = 0;
@@ -1990,6 +2178,21 @@ static int em28xx_dvb_init(struct em28xx *dev)
 		if (result)
 			goto out_free;
 		break;
+	case EM2828X_BOARD_HAUPPAUGE_935_V2:
+		result = em2828X_dvb_init_hauppauge_wintv_935_v2(dev);
+		if (result)
+			goto out_free;
+		break;
+	case EM2828X_BOARD_HAUPPAUGE_955_V2:
+		result = em2828X_dvb_init_hauppauge_wintv_955_v2(dev);
+		if (result)
+			goto out_free;
+		break;
+	case EM2828X_BOARD_HAUPPAUGE_975_V2:
+		result = em2828X_dvb_init_hauppauge_wintv_975_v2(dev);
+		if (result)
+			goto out_free;
+		break;
 	default:
 		dev_err(&dev->intf->dev,
 			"The frontend of your DVB/ATSC card isn't supported yet\n");
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index 578ba3d88d01..a172cc1e2604 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -171,7 +171,14 @@ static int em28xx_vbi_supported(struct em28xx *dev)
 
 static int em28xx_analogtv_supported(struct em28xx *dev)
 {
-	return 0;
+	switch (dev->model) {
+	case EM2828X_BOARD_HAUPPAUGE_935_V2:
+	case EM2828X_BOARD_HAUPPAUGE_955_V2:
+	case EM2828X_BOARD_HAUPPAUGE_975_V2:
+		return 1;
+	default:
+		return 0;
+	};
 }
 
 /*
@@ -2061,7 +2068,18 @@ static int vidioc_s_frequency(struct file *file, void *priv,
 	if (f->tuner != 0)
 		return -EINVAL;
 
-	v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f);
+	switch (dev->model) {
+	case EM2828X_BOARD_HAUPPAUGE_935_V2:
+	case EM2828X_BOARD_HAUPPAUGE_955_V2:
+	case EM2828X_BOARD_HAUPPAUGE_975_V2:
+		if (dev->em28xx_set_analog_freq)
+			dev->em28xx_set_analog_freq(dev, f->frequency);
+		break;
+	default:
+		v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f);
+		break;
+	}
+
 	v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, g_frequency, &new_freq);
 	v4l2->frequency = new_freq.frequency;
 
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index 9fcaebd78bae..ae4010c55623 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -145,6 +145,9 @@
 #define EM2874_BOARD_HAUPPAUGE_USB_QUADHD         106
 #define EM2860_BOARD_MYGICA_UTV3                  107
 #define EM2828X_BOARD_HAUPPAUGE_USB_LIVE2         108
+#define EM2828X_BOARD_HAUPPAUGE_935_V2            109
+#define EM2828X_BOARD_HAUPPAUGE_955_V2            110
+#define EM2828X_BOARD_HAUPPAUGE_975_V2            111
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-03-24 18:25 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-17 20:50 [PATCH 0/3] si2177 tuner support and new Hauppauge 9x5 devices Bradford Love
2026-03-17 20:50 ` [PATCH 1/3] media: em28xx: driver supports two frontends, but not i2c clients Bradford Love
2026-03-24 18:24   ` [PATCH v2 " Bradford Love
2026-03-17 20:50 ` [PATCH 2/3] media: si2157: Include support for si2177 chip Bradford Love
2026-03-24 18:25   ` [PATCH v2 " Bradford Love
2026-03-17 20:50 ` [PATCH 3/3] media: em28xx: Add Hauppauge em2828X based 9x5 revisions Bradford Love
2026-03-24 18:25   ` [PATCH v2 " Bradford Love
2026-03-24 18:23 ` [PATCH v2 0/3] si2177 tuner support and new Hauppauge 9x5 devices Bradford Love

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox