public inbox for linux-media@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Hauppauge 461e with m88ds3103c demod
@ 2026-03-17 20:57 Bradford Love
  2026-03-17 20:57 ` [PATCH 1/2] media: m88ds3103: Implement 3103c chip support Bradford Love
  2026-03-17 20:57 ` [PATCH 2/2] media: em28xx: Add Hauppauge 461e v3 Bradford Love
  0 siblings, 2 replies; 3+ messages in thread
From: Bradford Love @ 2026-03-17 20:57 UTC (permalink / raw)
  To: linux-media; +Cc: Bradford Love

The following two patches add support for the latest
revision of Hauppauge 461e that has moved from m88ds3103b
demod to m88ds3103c demod. The demod driver was reverse
egineered from a windows driver.

The m88ds3013c demod has some clocking changes and simplified
configuration.

Bradford Love (2):
  m88ds3103: Implement 3103c chip support
  em28xx: Add Hauppauge 461e v3

 drivers/media/dvb-frontends/m88ds3103.c      | 427 ++++++++++++++++---
 drivers/media/dvb-frontends/m88ds3103_priv.h |  46 +-
 drivers/media/usb/em28xx/em28xx-cards.c      |  34 +-
 drivers/media/usb/em28xx/em28xx-dvb.c        |  25 +-
 drivers/media/usb/em28xx/em28xx.h            |   1 +
 5 files changed, 447 insertions(+), 86 deletions(-)

-- 
2.35.1


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

* [PATCH 1/2] media: m88ds3103: Implement 3103c chip support
  2026-03-17 20:57 [PATCH 0/2] Hauppauge 461e with m88ds3103c demod Bradford Love
@ 2026-03-17 20:57 ` Bradford Love
  2026-03-17 20:57 ` [PATCH 2/2] media: em28xx: Add Hauppauge 461e v3 Bradford Love
  1 sibling, 0 replies; 3+ messages in thread
From: Bradford Love @ 2026-03-17 20:57 UTC (permalink / raw)
  To: linux-media; +Cc: Bradford Love

Hauppauge 416e products have moved to using the 3103C satellite
demod. This demod behaves mostly like a 3103b, but has
different initialization, clocking, and lock operations.

This Linux code was reverse egineered entirely
using a Windows reference driver.

Signed-off-by: Bradford Love <brad@nextdimension.cc>
---
 drivers/media/dvb-frontends/m88ds3103.c      | 427 ++++++++++++++++---
 drivers/media/dvb-frontends/m88ds3103_priv.h |  46 +-
 2 files changed, 400 insertions(+), 73 deletions(-)

diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index be156180e5ef..17afa2a06569 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -78,22 +78,34 @@ static int m88ds3103b_dt_write(struct m88ds3103_dev *dev, int reg, int data)
 		.addr = dev->dt_addr, .flags = 0, .buf = buf, .len = 2
 	};
 
-	m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
+	if (dev->chip_id == M88DS3103C_CHIP_ID)
+		m88ds3103_update_bits(dev, 0x04, 0x10, 0x00);
+	else
+		m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
 
 	val = 0x11;
 	ret = regmap_write(dev->regmap, 0x03, val);
 	if (ret)
 		dev_dbg(&client->dev, "fail=%d\n", ret);
 
+
 	ret = i2c_transfer(dev->dt_client->adapter, &msg, 1);
 	if (ret != 1) {
 		dev_err(&client->dev, "0x%02x (ret=%i, reg=0x%02x, value=0x%02x)\n",
 			dev->dt_addr, ret, reg, data);
 
-		m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+		if (dev->chip_id == M88DS3103C_CHIP_ID)
+			m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
+		else
+			m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+
 		return -EREMOTEIO;
 	}
-	m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+
+	if (dev->chip_id == M88DS3103C_CHIP_ID)
+		m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
+	else
+		m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
 
 	dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n",
 		dev->dt_addr, reg, data);
@@ -127,9 +139,14 @@ static int m88ds3103b_dt_read(struct m88ds3103_dev *dev, u8 reg)
 		}
 	};
 
-	m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
+	if (dev->chip_id == M88DS3103C_CHIP_ID) {
+		m88ds3103_update_bits(dev, 0x04, 0x10, 0x00);
+		val = 0x11;
+	} else {
+		m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
+		val = 0x12;
+	}
 
-	val = 0x12;
 	ret = regmap_write(dev->regmap, 0x03, val);
 	if (ret)
 		dev_dbg(&client->dev, "fail=%d\n", ret);
@@ -139,10 +156,18 @@ static int m88ds3103b_dt_read(struct m88ds3103_dev *dev, u8 reg)
 		dev_err(&client->dev, "0x%02x (ret=%d, reg=0x%02x)\n",
 			dev->dt_addr, ret, reg);
 
-		m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+		if (dev->chip_id == M88DS3103C_CHIP_ID)
+			m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
+		else
+			m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+
 		return -EREMOTEIO;
 	}
-	m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+
+	if (dev->chip_id == M88DS3103C_CHIP_ID)
+		m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
+	else
+		m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
 
 	dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n",
 		dev->dt_addr, reg, b1[0]);
@@ -185,14 +210,25 @@ static int m88ds3103_read_status(struct dvb_frontend *fe,
 
 	switch (c->delivery_system) {
 	case SYS_DVBS:
-		ret = regmap_read(dev->regmap, 0xd1, &utmp);
-		if (ret)
-			goto err;
+		if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+			ret = regmap_read(dev->regmap, 0x0d, &utmp);
+			if (ret)
+				goto err;
 
-		if ((utmp & 0x07) == 0x07)
-			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
-					FE_HAS_VITERBI | FE_HAS_SYNC |
-					FE_HAS_LOCK;
+			if ((utmp & 0xf7) == 0xf7)
+				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+					  FE_HAS_VITERBI | FE_HAS_SYNC |
+					  FE_HAS_LOCK;
+		} else {
+			ret = regmap_read(dev->regmap, 0xd1, &utmp);
+			if (ret)
+				goto err;
+
+			if ((utmp & 0x07) == 0x07)
+				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+					  FE_HAS_VITERBI | FE_HAS_SYNC |
+					  FE_HAS_LOCK;
+		}
 		break;
 	case SYS_DVBS2:
 		ret = regmap_read(dev->regmap, 0x0d, &utmp);
@@ -201,8 +237,8 @@ static int m88ds3103_read_status(struct dvb_frontend *fe,
 
 		if ((utmp & 0x8f) == 0x8f)
 			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
-					FE_HAS_VITERBI | FE_HAS_SYNC |
-					FE_HAS_LOCK;
+				  FE_HAS_VITERBI | FE_HAS_SYNC |
+				  FE_HAS_LOCK;
 		break;
 	default:
 		dev_dbg(&client->dev, "invalid delivery_system\n");
@@ -371,6 +407,7 @@ static int m88ds3103_read_status(struct dvb_frontend *fe,
 	return 0;
 err:
 	dev_dbg(&client->dev, "failed=%d\n", ret);
+
 	return ret;
 }
 
@@ -428,8 +465,10 @@ static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev)
 
 	reg15 = m88ds3103b_dt_read(dev, 0x15);
 
-	m88ds3103b_dt_write(dev, 0x05, 0x40);
-	m88ds3103b_dt_write(dev, 0x11, 0x08);
+	if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
+		m88ds3103b_dt_write(dev, 0x05, 0x40);
+		m88ds3103b_dt_write(dev, 0x11, 0x08);
+	}
 
 	if (big_symbol)
 		reg15 |= 0x02;
@@ -441,8 +480,10 @@ static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev)
 
 	usleep_range(5000, 5500);
 
-	m88ds3103b_dt_write(dev, 0x05, 0x00);
-	m88ds3103b_dt_write(dev, 0x11, (u8)(big_symbol ? 0x0E : 0x0A));
+	if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
+		m88ds3103b_dt_write(dev, 0x05, 0x00);
+		m88ds3103b_dt_write(dev, 0x11, (u8)(big_symbol ? 0x0E : 0x0A));
+	}
 
 	usleep_range(5000, 5500);
 
@@ -583,12 +624,6 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz)
 
 	sm = N - 1;
 
-	/* Write to registers */
-	//reg15 &= 0x01;
-	//reg15 |= (pll_div_fb >> 8) & 0x01;
-
-	//reg16 = pll_div_fb & 0xFF;
-
 	reg1D &= ~0x03;
 	reg1D |= sm;
 	reg1D |= 0x80;
@@ -596,8 +631,11 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz)
 	reg1E = ((f3 << 4) + f2) & 0xFF;
 	reg1F = ((f1 << 4) + f0) & 0xFF;
 
-	m88ds3103b_dt_write(dev, 0x05, 0x40);
-	m88ds3103b_dt_write(dev, 0x11, 0x08);
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+		m88ds3103b_dt_write(dev, 0x05, 0x40);
+		m88ds3103b_dt_write(dev, 0x11, 0x08);
+	}
+
 	m88ds3103b_dt_write(dev, 0x1D, reg1D);
 	m88ds3103b_dt_write(dev, 0x1E, reg1E);
 	m88ds3103b_dt_write(dev, 0x1F, reg1F);
@@ -607,14 +645,89 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz)
 
 	usleep_range(5000, 5500);
 
-	m88ds3103b_dt_write(dev, 0x05, 0x00);
-	m88ds3103b_dt_write(dev, 0x11, 0x0A);
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+		m88ds3103b_dt_write(dev, 0x05, 0x00);
+		m88ds3103b_dt_write(dev, 0x11, 0x0A);
+	}
 
 	usleep_range(5000, 5500);
 
 	return 0;
 }
 
+static int mt_fe_dmd_ds3103c_set_ts_out_mode(struct dvb_frontend *fe, enum m88ds3103_ts_mode mode)
+{
+	struct m88ds3103_dev *dev = fe->demodulator_priv;
+	struct i2c_client *client = dev->client;
+
+	unsigned int tmp, val = 0, ret = 0;
+
+	ret = regmap_read(dev->regmap, 0x0b, &val);
+	val &= ~0x01;
+	ret = regmap_write(dev->regmap, 0x0b, val);
+
+	ret = regmap_read(dev->regmap, 0xfd, &tmp);
+	if (mode == M88DS3103_TS_PARALLEL) {
+		tmp &= ~0x01;
+		tmp &= ~0x04;
+
+		regmap_write(dev->regmap, 0xfa, 0x01);
+		regmap_write(dev->regmap, 0xf1, 0x60);
+		regmap_write(dev->regmap, 0xfa, 0x00);
+	} else if (mode == M88DS3103_TS_SERIAL) {
+		tmp &= ~0x01;
+		tmp |= 0x04;
+	} else {
+		tmp |= 0x01;
+		tmp &= ~0x04;
+
+		regmap_write(dev->regmap, 0xfa, 0x01);
+		regmap_write(dev->regmap, 0xf1, 0x60);
+		regmap_write(dev->regmap, 0xfa, 0x00);
+	}
+
+	if (dev->cfg->ts_clk_pol) {
+		tmp &= ~0xf8;
+		tmp |= 0x02;
+	} else {
+		tmp &= ~0xb8;
+		tmp |= 0x42;
+	}
+
+	tmp |= 0x80;
+	regmap_write(dev->regmap, 0xfd, tmp);
+
+	val = 0;
+	if (mode != M88DS3103_TS_SERIAL) {
+		tmp = M88DS3103_TS_CI;
+
+		val |= tmp & 0x03;
+		val |= (tmp << 2) & 0x0C;
+		val |= (tmp << 4) & 0x30;
+		val |= (tmp << 6) & 0xC0;
+	} else
+		val = 0x00;
+
+	regmap_write(dev->regmap, 0x0a, val);
+
+	regmap_read(dev->regmap, 0x0b, &tmp);
+
+	tmp &= ~0x20;
+	tmp |= 0x01;
+
+	regmap_write(dev->regmap, 0x0b, tmp);
+
+
+	ret = regmap_read(dev->regmap, 0x0c, &tmp);
+
+	regmap_write(dev->regmap, 0xf4, 0x01);
+
+	tmp &= ~0x80;
+	regmap_write(dev->regmap, 0x0c, tmp);
+
+	return 0;
+}
+
 static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 {
 	struct m88ds3103_dev *dev = fe->demodulator_priv;
@@ -627,6 +740,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	u16 u16tmp;
 	u32 tuner_frequency_khz, target_mclk, u32tmp;
 	s32 s32tmp;
+	unsigned int utmp;
 	static const struct reg_sequence reset_buf[] = {
 		{0x07, 0x80}, {0x07, 0x00}
 	};
@@ -646,9 +760,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	if (ret)
 		goto err;
 
+	/* Clear TS */
+	ret = regmap_write(dev->regmap, 0xf5, 0x00);
+
 	/* Disable demod clock path */
-	if (dev->chip_id == M88RS6000_CHIP_ID) {
-		if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+	if (dev->chip_id == M88RS6000_CHIP_ID ||
+	    dev->chip_id == M88DS3103C_CHIP_ID) {
+		if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+		    dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
 			ret = regmap_read(dev->regmap, 0xb2, &u32tmp);
 			if (ret)
 				goto err;
@@ -688,7 +807,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	}
 
 	/* set M88RS6000/DS3103B demod main mclk and ts mclk from tuner die */
-	if (dev->chip_id == M88RS6000_CHIP_ID) {
+	if (dev->chip_id == M88RS6000_CHIP_ID ||
+	    dev->chip_id == M88DS3103C_CHIP_ID) {
 		if (c->symbol_rate > 45010000)
 			dev->mclk = 110250000;
 		else
@@ -699,7 +819,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 		else
 			target_mclk = 144000000;
 
-		if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+		if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+		    dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
 			m88ds3103b_select_mclk(dev);
 			m88ds3103b_set_mclk(dev, target_mclk / 1000);
 		}
@@ -772,6 +893,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 		if (dev->chip_id == M88RS6000_CHIP_ID) {
 			len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals);
 			init = m88rs6000_dvbs_init_reg_vals;
+		} else if (dev->chip_id == M88DS3103C_CHIP_ID) {
+			len = ARRAY_SIZE(m88ds3103c_dvbs_init_reg_vals);
+			init = m88ds3103c_dvbs_init_reg_vals;
 		} else {
 			len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
 			init = m88ds3103_dvbs_init_reg_vals;
@@ -781,6 +905,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 		if (dev->chip_id == M88RS6000_CHIP_ID) {
 			len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals);
 			init = m88rs6000_dvbs2_init_reg_vals;
+		} else if (dev->chip_id == M88DS3103C_CHIP_ID) {
+			len = ARRAY_SIZE(m88ds3103c_dvbs_init_reg_vals);
+			init = m88ds3103c_dvbs_init_reg_vals;
 		} else {
 			len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
 			init = m88ds3103_dvbs2_init_reg_vals;
@@ -799,7 +926,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 			goto err;
 	}
 
-	if (dev->chip_id == M88RS6000_CHIP_ID) {
+	if (dev->chip_id == M88RS6000_CHIP_ID ||
+	    dev->chip_id == M88DS3103C_CHIP_ID) {
 		if (c->delivery_system == SYS_DVBS2 &&
 		    c->symbol_rate <= 5000000) {
 			ret = regmap_write(dev->regmap, 0xc0, 0x04);
@@ -812,11 +940,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 			if (ret)
 				goto err;
 		}
-		ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08);
-		if (ret)
-			goto err;
+		if (dev->chip_id != M88DS3103C_CHIP_ID) {
+			ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08);
+			if (ret)
+				goto err;
+		}
 
-		if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+		if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+		    dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
 			buf[0] = m88ds3103b_dt_read(dev, 0x15);
 			buf[1] = m88ds3103b_dt_read(dev, 0x16);
 
@@ -838,15 +969,22 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 			m88ds3103b_dt_write(dev, 0x16, buf[1]);
 
 			regmap_read(dev->regmap, 0x30, &u32tmp);
-			u32tmp &= ~0x80;
-			regmap_write(dev->regmap, 0x30, u32tmp & 0xff);
+			if (dev->chip_id == M88DS3103C_CHIP_ID) {
+				regmap_write(dev->regmap, 0x30, dev->config.agc_inv ? 0x18 : 0x08);
+			} else {
+				u32tmp &= ~0x80;
+				regmap_write(dev->regmap, 0x30, u32tmp & 0xff);
+			}
 		}
 
-		ret = regmap_write(dev->regmap, 0xf1, 0x01);
-		if (ret)
-			goto err;
+		if (dev->chip_id != M88DS3103C_CHIP_ID) {
+			ret = regmap_write(dev->regmap, 0xf1, 0x01);
+			if (ret)
+				goto err;
+		}
 
-		if (dev->chiptype != M88DS3103_CHIPTYPE_3103B) {
+		if (dev->chiptype != M88DS3103_CHIPTYPE_3103B &&
+		    dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
 			ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80);
 			if (ret)
 				goto err;
@@ -864,7 +1002,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 		break;
 	case M88DS3103_TS_PARALLEL:
 		u8tmp = 0x02;
-		if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+		if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+		    dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
 			u8tmp = 0x01;
 			u8tmp1 = 0x01;
 		}
@@ -881,10 +1020,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	if (dev->cfg->ts_clk_pol)
 		u8tmp |= 0x40;
 
-	/* TS mode */
-	ret = regmap_write(dev->regmap, 0xfd, u8tmp);
-	if (ret)
-		goto err;
+	if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
+		/* TS mode */
+		ret = regmap_write(dev->regmap, 0xfd, u8tmp);
+		if (ret)
+			goto err;
+	}
 
 	switch (dev->cfg->ts_mode) {
 	case M88DS3103_TS_SERIAL:
@@ -918,7 +1059,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	ret = regmap_update_bits(dev->regmap, 0xfe, 0x0f, u8tmp);
 	if (ret)
 		goto err;
-	u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
+
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
+		u8tmp = 0xcb;
+	else
+		u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
+
 	ret = regmap_write(dev->regmap, 0xea, u8tmp);
 	if (ret)
 		goto err;
@@ -930,7 +1076,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	else
 		u8tmp = 0x06;
 
-	if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+	    dev->chiptype == M88DS3103_CHIPTYPE_3103C)
 		m88ds3103b_set_mclk(dev, target_mclk / 1000);
 
 	ret = regmap_write(dev->regmap, 0xc3, 0x08);
@@ -949,9 +1096,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	if (ret)
 		goto err;
 
-	u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk);
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
+		u16tmp = DIV_ROUND_CLOSEST_ULL((((u64)c->symbol_rate << 15) + dev->mclk / 4), (dev->mclk / 2));
+	else
+		u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk);
+
 	buf[0] = (u16tmp >> 0) & 0xff;
 	buf[1] = (u16tmp >> 8) & 0xff;
+
 	ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2);
 	if (ret)
 		goto err;
@@ -960,7 +1112,15 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	if (ret)
 		goto err;
 
-	ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4);
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
+		ret = m88ds3103_update_bits(dev, 0x30, 0x08, dev->cfg->agc_inv << 3);
+	else if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+		ret = m88ds3103_update_bits(dev, 0x08, 0x43, 0x43);
+
+		ret = m88ds3103_update_bits(dev, 0x30, 0x18, dev->cfg->agc_inv ? 0x18 : 0x08);
+	} else
+		ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4);
+
 	if (ret)
 		goto err;
 
@@ -974,10 +1134,37 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 				(c->delivery_system == SYS_DVBS) ? 0x10 : 0x0);
 		if (ret)
 			goto err;
+	}
+
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+		ret = m88ds3103_update_bits(dev, 0x76, 0x80, 0x00);
+		if (ret)
+			goto err;
+
+		ret = m88ds3103_update_bits(dev, 0x22, 0x01, 0x01);
+		ret = m88ds3103_update_bits(dev, 0x23, 0x01, 0x00);
+		ret = m88ds3103_update_bits(dev, 0x24, 0x01, 0x00);
 
 		ret = m88ds3103_update_bits(dev, 0xc9, 0x08, 0x08);
 		if (ret)
 			goto err;
+
+		ret = regmap_read(dev->regmap, 0x08, &utmp);
+		if (ret)
+			goto err;
+
+		if (c->delivery_system == SYS_DVBS) {
+			utmp = (utmp & 0xfb) | 0x40;
+			regmap_write(dev->regmap, 0x08, utmp);
+			regmap_write(dev->regmap, 0xe0, 0xf8);
+		} else if (c->delivery_system == SYS_DVBS2) {
+			utmp = utmp | 0x44;
+			regmap_write(dev->regmap, 0x08, utmp);
+		} else {
+			utmp = utmp & 0xbb;
+			regmap_write(dev->regmap, 0x08, utmp);
+			regmap_write(dev->regmap, 0xe0, 0xf8);
+		}
 	}
 
 	dev_dbg(&client->dev, "carrier offset=%d\n",
@@ -986,8 +1173,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	/* Use 32-bit calc as there is no s64 version of DIV_ROUND_CLOSEST() */
 	s32tmp = 0x10000 * (tuner_frequency_khz - c->frequency);
 	s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk / 1000);
+
+	usleep_range(1000, 1200);
+
 	buf[0] = (s32tmp >> 0) & 0xff;
 	buf[1] = (s32tmp >> 8) & 0xff;
+
 	ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2);
 	if (ret)
 		goto err;
@@ -1012,6 +1203,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	return 0;
 err:
 	dev_dbg(&client->dev, "failed=%d\n", ret);
+
 	return ret;
 }
 
@@ -1025,19 +1217,72 @@ static int m88ds3103_init(struct dvb_frontend *fe)
 	const struct firmware *firmware;
 	const char *name;
 
+
 	dev_dbg(&client->dev, "\n");
 
 	/* set cold state by default */
 	dev->warm = false;
 
 	/* wake up device from sleep */
-	ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
-	if (ret)
-		goto err;
-	ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00);
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+		ret = m88ds3103_update_bits(dev, 0x0b, 0x90, 0x80);	/* set dt_addr */
+
+		m88ds3103b_dt_write(dev, 0x04, 0x01);	/* reset */
+		m88ds3103b_dt_write(dev, 0x04, 0x00);
+		usleep_range(800, 1200);
+
+		ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00);
+		if (ret)
+			goto err;
+
+		m88ds3103b_dt_write(dev, 0x10, 0x01);	/* wakeup */
+		m88ds3103b_dt_write(dev, 0x11, 0x01);	/* wakeup */
+
+		ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
+		if (ret)
+			goto err;
+
+		ret = m88ds3103_update_bits(dev, 0x0b, 0x01, 0x01);
+		if (ret)
+			goto err;
+		/* global reset, global diseqc reset, global fec reset */
+		ret = regmap_write(dev->regmap, 0x07, 0x80);
+		if (ret)
+			goto err;
+		ret = regmap_write(dev->regmap, 0x07, 0x00);
+		if (ret)
+			goto err;
+		usleep_range(800, 1200);
+
+		ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
+		if (ret)
+			goto err;
+	} else {
+		ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
+		if (ret)
+			goto err;
+		ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00);
+		if (ret)
+			goto err;
+		ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00);
+		if (ret)
+			goto err;
+	}
+
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+		m88ds3103b_dt_write(dev, 0x10, 0x01);	/* wakeup */
+		m88ds3103b_dt_write(dev, 0x11, 0x01);	/* wakeup */
+		m88ds3103b_dt_write(dev, 0x24, 0x04);
+		m88ds3103b_dt_write(dev, 0x84, 0x04);
+		m88ds3103b_dt_write(dev, 0x15, 0x6c);
+		usleep_range(800, 1200);
+	}
+
+	/* global reset, global diseqc reset, global fec reset */
+	ret = regmap_write(dev->regmap, 0x07, 0xe0);
 	if (ret)
 		goto err;
-	ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00);
+	ret = regmap_write(dev->regmap, 0x07, 0x00);
 	if (ret)
 		goto err;
 
@@ -1051,20 +1296,14 @@ static int m88ds3103_init(struct dvb_frontend *fe)
 	if (utmp)
 		goto warm;
 
-	/* global reset, global diseqc reset, global fec reset */
-	ret = regmap_write(dev->regmap, 0x07, 0xe0);
-	if (ret)
-		goto err;
-	ret = regmap_write(dev->regmap, 0x07, 0x00);
-	if (ret)
-		goto err;
-
 	/* cold state - try to download firmware */
 	dev_info(&client->dev, "found a '%s' in cold state\n",
 		 dev->fe.ops.info.name);
 
 	if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
 		name = M88DS3103B_FIRMWARE;
+	else if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
+		name = M88DS3103C_FIRMWARE;
 	else if (dev->chip_id == M88RS6000_CHIP_ID)
 		name = M88RS6000_FIRMWARE;
 	else
@@ -1116,6 +1355,18 @@ static int m88ds3103_init(struct dvb_frontend *fe)
 	dev_info(&client->dev, "firmware version: %X.%X\n",
 		 (utmp >> 4) & 0xf, (utmp >> 0 & 0xf));
 
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
+		mt_fe_dmd_ds3103c_set_ts_out_mode(fe, dev->cfg->ts_mode);
+
+		ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1);
+		if (ret)
+			goto err;
+
+		ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv ? 0x10 : 0x00);
+		if (ret)
+			goto err;
+	}
+
 	if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
 		m88ds3103b_dt_write(dev, 0x21, 0x92);
 		m88ds3103b_dt_write(dev, 0x15, 0x6C);
@@ -1139,6 +1390,7 @@ static int m88ds3103_init(struct dvb_frontend *fe)
 	release_firmware(firmware);
 err:
 	dev_dbg(&client->dev, "failed=%d\n", ret);
+
 	return ret;
 }
 
@@ -1157,6 +1409,8 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
 	/* TS Hi-Z */
 	if (dev->chip_id == M88RS6000_CHIP_ID)
 		utmp = 0x29;
+	else if (dev->chip_id == M88DS3103C_CHIP_ID)
+		utmp = 0x0b;
 	else
 		utmp = 0x27;
 	ret = m88ds3103_update_bits(dev, utmp, 0x01, 0x00);
@@ -1167,6 +1421,17 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
 	ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00);
 	if (ret)
 		goto err;
+
+	/* Internal tuner sleep */
+	if (dev->chip_id == M88DS3103C_CHIP_ID) {
+		ret = m88ds3103b_dt_write(dev, 0x10, 0x00);
+		if (ret)
+			goto err;
+		ret = m88ds3103b_dt_write(dev, 0x11, 0x00);
+		if (ret)
+			goto err;
+	}
+
 	ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01);
 	if (ret)
 		goto err;
@@ -1177,6 +1442,7 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
 	return 0;
 err:
 	dev_dbg(&client->dev, "failed=%d\n", ret);
+
 	return ret;
 }
 
@@ -1341,11 +1607,13 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe,
 	if (ret)
 		goto err;
 
+//	dev_dbg(&client->dev, "%s()  0x%X  |  0x%X\n", __func__, buf[0], buf[1]);
 	c->symbol_rate = DIV_ROUND_CLOSEST_ULL((u64)(buf[1] << 8 | buf[0] << 0) * dev->mclk, 0x10000);
 
 	return 0;
 err:
 	dev_dbg(&client->dev, "failed=%d\n", ret);
+
 	return ret;
 }
 
@@ -1378,7 +1646,7 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe,
 	int ret;
 	unsigned int utmp, tone, reg_a1_mask;
 
-	dev_dbg(&client->dev, "fe_sec_tone_mode=%d\n", fe_sec_tone_mode);
+	dev_dbg(&client->dev, "fe_sec_tone_mode=%s\n", fe_sec_tone_mode == SEC_TONE_ON ? "ON" : "OFF");
 
 	if (!dev->warm) {
 		ret = -EAGAIN;
@@ -1413,6 +1681,7 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe,
 	return 0;
 err:
 	dev_dbg(&client->dev, "failed=%d\n", ret);
+
 	return ret;
 }
 
@@ -1463,6 +1732,7 @@ static int m88ds3103_set_voltage(struct dvb_frontend *fe,
 	return 0;
 err:
 	dev_dbg(&client->dev, "failed=%d\n", ret);
+
 	return ret;
 }
 
@@ -1542,6 +1812,7 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
 	return 0;
 err:
 	dev_dbg(&client->dev, "failed=%d\n", ret);
+
 	return ret;
 }
 
@@ -1621,6 +1892,7 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
 	return 0;
 err:
 	dev_dbg(&client->dev, "failed=%d\n", ret);
+
 	return ret;
 }
 
@@ -1818,6 +2090,7 @@ static int m88ds3103_probe(struct i2c_client *client)
 	switch (dev->chip_id) {
 	case M88RS6000_CHIP_ID:
 	case M88DS3103_CHIP_ID:
+	case M88DS3103C_CHIP_ID:
 		break;
 	default:
 		ret = -ENODEV;
@@ -1847,7 +2120,8 @@ static int m88ds3103_probe(struct i2c_client *client)
 
 	/* 0x29 register is defined differently for m88rs6000. */
 	/* set internal tuner address to 0x21 */
-	if (dev->chip_id == M88RS6000_CHIP_ID)
+	if (dev->chip_id == M88RS6000_CHIP_ID ||
+	    dev->chip_id == M88DS3103C_CHIP_ID)
 		utmp = 0x00;
 
 	ret = regmap_write(dev->regmap, 0x29, utmp);
@@ -1882,6 +2156,9 @@ static int m88ds3103_probe(struct i2c_client *client)
 	if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
 		strscpy(dev->fe.ops.info.name, "Montage Technology M88DS3103B",
 			sizeof(dev->fe.ops.info.name));
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
+		strscpy(dev->fe.ops.info.name, "Montage Technology M88DS3103C",
+			sizeof(dev->fe.ops.info.name));
 	else if (dev->chip_id == M88RS6000_CHIP_ID)
 		strscpy(dev->fe.ops.info.name, "Montage Technology M88RS6000",
 			sizeof(dev->fe.ops.info.name));
@@ -1894,15 +2171,22 @@ static int m88ds3103_probe(struct i2c_client *client)
 	pdata->get_dvb_frontend = m88ds3103_get_dvb_frontend;
 	pdata->get_i2c_adapter = m88ds3103_get_i2c_adapter;
 
-	if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+	if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
+	    dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
 		/* enable i2c repeater for tuner */
-		m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+		if (dev->chip_id == M88DS3103C_CHIP_ID)
+			m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
+		else
+			m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
 
 		/* get frontend address */
 		ret = regmap_read(dev->regmap, 0x29, &utmp);
 		if (ret)
 			goto err_del_adapters;
+
 		dev->dt_addr = ((utmp & 0x80) == 0) ? 0x42 >> 1 : 0x40 >> 1;
+		if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
+			dev->dt_addr = 0x5c >> 1;
 		dev_dbg(&client->dev, "dt addr is 0x%02x\n", dev->dt_addr);
 
 		dev->dt_client = i2c_new_dummy_device(client->adapter,
@@ -1941,6 +2225,7 @@ static const struct i2c_device_id m88ds3103_id_table[] = {
 	{"m88ds3103",  M88DS3103_CHIPTYPE_3103},
 	{"m88rs6000",  M88DS3103_CHIPTYPE_RS6000},
 	{"m88ds3103b", M88DS3103_CHIPTYPE_3103B},
+	{"m88ds3103c", M88DS3103_CHIPTYPE_3103C},
 	{}
 };
 MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table);
diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
index 594ad9cbc2cc..d7d16e7904da 100644
--- a/drivers/media/dvb-frontends/m88ds3103_priv.h
+++ b/drivers/media/dvb-frontends/m88ds3103_priv.h
@@ -17,15 +17,18 @@
 #include <linux/math64.h>
 
 #define M88DS3103B_FIRMWARE "dvb-demod-m88ds3103b.fw"
+#define M88DS3103C_FIRMWARE "dvb-demod-m88ds3103c.fw"
 #define M88DS3103_FIRMWARE  "dvb-demod-m88ds3103.fw"
 #define M88RS6000_FIRMWARE  "dvb-demod-m88rs6000.fw"
 
-#define M88RS6000_CHIP_ID 0x74
-#define M88DS3103_CHIP_ID 0x70
+#define M88DS3103_CHIP_ID  0x70
+#define M88RS6000_CHIP_ID  0x74
+#define M88DS3103C_CHIP_ID 0x71
 
 #define M88DS3103_CHIPTYPE_3103   0
 #define M88DS3103_CHIPTYPE_RS6000 1
 #define M88DS3103_CHIPTYPE_3103B  2
+#define M88DS3103_CHIPTYPE_3103C  3
 
 struct m88ds3103_dev {
 	struct i2c_client *client;
@@ -399,4 +402,43 @@ static const struct m88ds3103_reg_val m88rs6000_dvbs2_init_reg_vals[] = {
 	{0xb8, 0x00},
 	{0x29, 0x01},
 };
+
+static const struct m88ds3103_reg_val m88ds3103c_dvbs_init_reg_vals[] = {
+	{0x04, 0x10},
+	{0x8a, 0x01},
+	{0x16, 0xa7},
+	{0x30, 0x08},
+	{0x32, 0x32},
+	{0x33, 0x35},
+	{0x35, 0xff},
+	{0x4a, 0x80},
+	{0x4d, 0x93},
+	{0xae, 0x09},
+	{0x22, 0x01},
+	{0x23, 0x00},
+	{0x24, 0x00},
+	{0x27, 0x07},
+	{0x9c, 0x31},
+	{0x9d, 0xc1},
+	{0xcb, 0xf4},
+	{0xca, 0x00},
+	{0x7f, 0x04},
+	{0x78, 0x0c},
+	{0x85, 0x08},
+	{0x08, 0x47},
+	{0xf0, 0x03},
+	{0xfa, 0x01},
+	{0xf2, 0x00},
+	{0xfa, 0x00},
+	{0xe6, 0x00},
+	{0xe7, 0xf3},
+	{0x08, 0x43},
+	{0xe0, 0xf8},
+	{0x00, 0x00},
+	{0xbd, 0x82},
+	{0x80, 0xa8},
+	{0x81, 0xea},
+	{0xbe, 0xa1}
+};
+
 #endif
-- 
2.35.1


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

* [PATCH 2/2] media: em28xx: Add Hauppauge 461e v3
  2026-03-17 20:57 [PATCH 0/2] Hauppauge 461e with m88ds3103c demod Bradford Love
  2026-03-17 20:57 ` [PATCH 1/2] media: m88ds3103: Implement 3103c chip support Bradford Love
@ 2026-03-17 20:57 ` Bradford Love
  1 sibling, 0 replies; 3+ messages in thread
From: Bradford Love @ 2026-03-17 20:57 UTC (permalink / raw)
  To: linux-media; +Cc: Bradford Love

Same as 461v2, but uses Montage ds3103c instead of ds3103b

Signed-off-by: Bradford Love <brad@nextdimension.cc>
---
 drivers/media/usb/em28xx/em28xx-cards.c | 34 +++++++++++++++++++------
 drivers/media/usb/em28xx/em28xx-dvb.c   | 25 ++++++++++++++----
 drivers/media/usb/em28xx/em28xx.h       |  1 +
 3 files changed, 47 insertions(+), 13 deletions(-)

diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index b4bafd2fa482..0cadf0f6d99a 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -387,14 +387,14 @@ static const struct em28xx_reg_seq c3tech_digital_duo_digital[] = {
  * GPIO 7 = P07_LED (green LED)
  */
 static const struct em28xx_reg_seq pctv_461e[] = {
-	{EM2874_R80_GPIO_P0_CTRL,      0x7f, 0xff,    0},
-	{0x0d,                 0xff, 0xff,    0},
-	{EM2874_R80_GPIO_P0_CTRL,      0x3f, 0xff,  100}, /* reset demod */
-	{EM2874_R80_GPIO_P0_CTRL,      0x7f, 0xff,  200}, /* reset demod */
-	{0x0d,                 0x42, 0xff,    0},
-	{EM2874_R80_GPIO_P0_CTRL,      0xeb, 0xff,    0},
-	{EM2874_R5F_TS_ENABLE, 0x84, 0x84,    0}, /* parallel? | null discard */
-	{                  -1,   -1,   -1,   -1},
+	{EM2874_R80_GPIO_P0_CTRL,	0x7f,	0xff,	0},
+	{0x0d,				0xff,	0xff,	0},
+	{EM2874_R80_GPIO_P0_CTRL,	0x3f,	0xff,	100}, /* reset demod */
+	{EM2874_R80_GPIO_P0_CTRL,	0x7f,	0xff,	200}, /* reset demod */
+	{0x0d,				0x42,	0xff,	0},
+	{EM2874_R80_GPIO_P0_CTRL,	0xeb,	0xff,	0},
+	{EM2874_R5F_TS_ENABLE,		0x84,	0x84,	0}, /* parallel? | null discard */
+	{-1,				-1,	-1,	-1},
 };
 
 #if 0
@@ -2496,6 +2496,20 @@ const struct em28xx_board em28xx_boards[] = {
 		.has_dvb       = 1,
 		.ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
 	},
+	/*
+	 * 2013:x462 PCTV DVB-S2 Stick (461e_v3)
+	 * Empia EM28178, Montage M88DS3103c, Montage M88TS2022, Allegro A8293
+	 */
+	[EM28178_BOARD_PCTV_461E_V3] = {
+		.def_i2c_bus   = 1,
+		.i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE |
+				 EM28XX_I2C_FREQ_400_KHZ,
+		.name          = "PCTV DVB-S2 Stick (461e v3)",
+		.tuner_type    = TUNER_ABSENT,
+		.tuner_gpio    = pctv_461e,
+		.has_dvb       = 1,
+		.ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
+	},
 	/*
 	 * 2013:025f PCTV tripleStick (292e).
 	 * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2157
@@ -3011,6 +3025,10 @@ struct usb_device_id em28xx_id_table[] = {
 			.driver_info = EM28178_BOARD_PCTV_461E_V2 },
 	{ USB_DEVICE(0x2013, 0x0259),
 			.driver_info = EM28178_BOARD_PCTV_461E_V2 },
+	{ USB_DEVICE(0x2013, 0x0462),
+			.driver_info = EM28178_BOARD_PCTV_461E_V3 },
+	{ USB_DEVICE(0x2013, 0x8462), /* Bulk transport 461e v3 */
+			.driver_info = EM28178_BOARD_PCTV_461E_V3 },
 	{ USB_DEVICE(0x2013, 0x025f),
 			.driver_info = EM28178_BOARD_PCTV_292E },
 	{ USB_DEVICE(0x2013, 0x0264), /* Hauppauge WinTV-soloHD 292e SE */
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 2f0972072a00..0c8f5b0cdcd3 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -1237,7 +1237,7 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev)
 	return 0;
 }
 
-static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev)
+static int em28178_dvb_init_pctv_461e_vX(struct em28xx *dev, int version)
 {
 	struct em28xx_dvb *dvb = dev->dvb;
 	struct i2c_adapter *i2c_adapter;
@@ -1254,9 +1254,19 @@ 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[0] = dvb_module_probe("m88ds3103", "m88ds3103b",
-						 &dev->i2c_adap[dev->def_i2c_bus],
-						 0x6a, &m88ds3103_pdata);
+
+	if (version == 2)
+		dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", "m88ds3103b",
+							    &dev->i2c_adap[dev->def_i2c_bus],
+							    0x6a, &m88ds3103_pdata);
+	else {
+		m88ds3103_pdata.lnb_hv_pol = 1;
+		m88ds3103_pdata.lnb_en_pol = 1;
+
+		dvb->i2c_client_demod[0] = dvb_module_probe("m88ds3103", "m88ds3103c",
+							    &dev->i2c_adap[dev->def_i2c_bus],
+							    0x6a, &m88ds3103_pdata);
+	}
 
 	if (!dvb->i2c_client_demod[0])
 		return -ENODEV;
@@ -2144,7 +2154,12 @@ static int em28xx_dvb_init(struct em28xx *dev)
 			goto out_free;
 		break;
 	case EM28178_BOARD_PCTV_461E_V2:
-		result = em28178_dvb_init_pctv_461e_v2(dev);
+		result = em28178_dvb_init_pctv_461e_vX(dev, 2);
+		if (result)
+			goto out_free;
+		break;
+	case EM28178_BOARD_PCTV_461E_V3:
+		result = em28178_dvb_init_pctv_461e_vX(dev, 3);
 		if (result)
 			goto out_free;
 		break;
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index ae4010c55623..e475dab25635 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -148,6 +148,7 @@
 #define EM2828X_BOARD_HAUPPAUGE_935_V2            109
 #define EM2828X_BOARD_HAUPPAUGE_955_V2            110
 #define EM2828X_BOARD_HAUPPAUGE_975_V2            111
+#define EM28178_BOARD_PCTV_461E_V3                112
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
-- 
2.35.1


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

end of thread, other threads:[~2026-03-17 20:57 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-17 20:57 [PATCH 0/2] Hauppauge 461e with m88ds3103c demod Bradford Love
2026-03-17 20:57 ` [PATCH 1/2] media: m88ds3103: Implement 3103c chip support Bradford Love
2026-03-17 20:57 ` [PATCH 2/2] media: em28xx: Add Hauppauge 461e v3 Bradford Love

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