Hi Maruro,
This patchset adds support for a LinuxDVB demux driver for the
ST STB stih407 family SoC's. It is what I spoke to you about
when we met at ELC-E in Dusseldorf last year.
One advantage of having a upstream demux driver implementation for ST
SoC's is that it will be easier to add support and maintain existing support
for the ST demodulators and tuners which are upstream. As this driver allows
ST NIM daughter boards (which typically have a tuner/demod combination)
to be used with a upstream kernel.
This initial patchset adds support for the following demux HW called c8sectpfe: -
* Input Block HW
* HW PID filtering
* memdma engine (moves TS from sram to RAM)
The driver creates one Linux DVB adapter, and each tsin channel which is
described in DT has a set of LDVB dev nodes.
Currently the driver supports 7 tsin channels. This driver has been tested with
the stih407-b2120 board and stih410-b2120 reference design boards, and currently
we support the following DVB fronend cards:
- STMicroelectronics DVB-T B2100A (STV0367 + TDA18212)
- STMicroelectronics DVB-T STV0367 PLL board (STV0367 + DTT7546X)
- STMicroelectronics DVB-S/S2 STV0903 + STV6110 + LNBP24 board
There are also some small changes to dvb-pll.c and stv0367.c to get these
NIM daughterboards working correctly.
regards,
Peter.
p.s. The series which adds pinctrl config used by this driver is
https://lkml.org/lkml/2015/6/10/377
Peter Griffin (12):
ARM: DT: STi: stihxxx-b2120: Add pulse-width properties to ssc2 & ssc3
[media] dvb-pll: Add support for THOMSON DTT7546X tuner.
[media] stv0367: Refine i2c error trace to include i2c address
[media] stv0367: Add support for 16Mhz reference clock
[media] tsin: c8sectpfe: Add DT bindings documentation for c8sectpfe
driver.
ARM: DT: STi: STiH407: Add c8sectpfe LinuxDVB DT node.
[media] tsin: c8sectpfe: STiH407/10 Linux DVB demux support
[media] tsin: c8sectpfe: Add LDVB helper functions.
[media] tsin: c8sectpfe: Add support for various ST NIM cards.
[media] tsin: c8sectpfe: Add c8sectpfe debugfs support.
[media] tsin: c8sectpfe: Add Kconfig and Makefile for the driver.
MAINTAINERS: Add c8sectpfe driver directory to STi section
.../bindings/media/stih407-c8sectpfe.txt | 90 ++
MAINTAINERS | 1 +
arch/arm/boot/dts/stihxxx-b2120.dtsi | 60 +-
drivers/media/Kconfig | 1 +
drivers/media/Makefile | 1 +
drivers/media/dvb-frontends/dvb-pll.c | 74 +-
drivers/media/dvb-frontends/dvb-pll.h | 1 +
drivers/media/dvb-frontends/stv0367.c | 17 +-
drivers/media/tsin/c8sectpfe/Kconfig | 26 +
drivers/media/tsin/c8sectpfe/Makefile | 11 +
drivers/media/tsin/c8sectpfe/c8sectpfe-common.c | 266 +++++
drivers/media/tsin/c8sectpfe/c8sectpfe-common.h | 66 ++
drivers/media/tsin/c8sectpfe/c8sectpfe-core.c | 1105 ++++++++++++++++++++
drivers/media/tsin/c8sectpfe/c8sectpfe-core.h | 288 +++++
drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.c | 271 +++++
drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.h | 26 +
drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c | 296 ++++++
drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h | 20 +
include/dt-bindings/media/c8sectpfe.h | 14 +
19 files changed, 2617 insertions(+), 17 deletions(-)
create mode 100644 Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt
create mode 100644 drivers/media/tsin/c8sectpfe/Kconfig
create mode 100644 drivers/media/tsin/c8sectpfe/Makefile
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-common.c
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-common.h
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-core.c
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-core.h
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.c
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.h
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h
create mode 100644 include/dt-bindings/media/c8sectpfe.h
--
1.9.1
Adding these properties makes the I2C bus to the demodulators much
more reliable, and we no longer suffer from I2C errors when tuning.
Signed-off-by: Peter Griffin <[email protected]>
---
arch/arm/boot/dts/stihxxx-b2120.dtsi | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/stihxxx-b2120.dtsi b/arch/arm/boot/dts/stihxxx-b2120.dtsi
index c1d8590..1f27589 100644
--- a/arch/arm/boot/dts/stihxxx-b2120.dtsi
+++ b/arch/arm/boot/dts/stihxxx-b2120.dtsi
@@ -27,12 +27,18 @@
};
};
- i2c@9842000 {
+ ssc2: i2c@9842000 {
status = "okay";
+ clock-frequency = <100000>;
+ st,i2c-min-scl-pulse-width-us = <0>;
+ st,i2c-min-sda-pulse-width-us = <5>;
};
- i2c@9843000 {
+ ssc3: i2c@9843000 {
status = "okay";
+ clock-frequency = <100000>;
+ st,i2c-min-scl-pulse-width-us = <0>;
+ st,i2c-min-sda-pulse-width-us = <5>;
};
i2c@9844000 {
--
1.9.1
This is used in conjunction with the STV0367 demodulator on
the STV0367-NIM-V1.0 NIM card which can be used with the STi
STB SoC's.
This tuner has a fifth register, so some changes have been made
to accommodate this.
Signed-off-by: Peter Griffin <[email protected]>
---
drivers/media/dvb-frontends/dvb-pll.c | 74 +++++++++++++++++++++++++++++------
drivers/media/dvb-frontends/dvb-pll.h | 1 +
2 files changed, 64 insertions(+), 11 deletions(-)
diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c
index 6d8fe88..f7381c7 100644
--- a/drivers/media/dvb-frontends/dvb-pll.c
+++ b/drivers/media/dvb-frontends/dvb-pll.c
@@ -141,6 +141,35 @@ static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
},
};
+static void thomson_dtt7546x_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ /* set CB2 reg - set ATC, XTO */
+ buf[4] = 0xc3;
+}
+
+static struct dvb_pll_desc dvb_pll_thomson_dtt7546x = {
+ .name = "Thomson dtt7546x",
+ .min = 44250000,
+ .max = 863250000,
+ .set = thomson_dtt7546x_bw,
+ .iffreq= 36166667,
+ .count = 12,
+ .entries = {
+ { 121000000, 166667, 0x88, 0x01 },
+ { 141000000, 166667, 0x88, 0x41 },
+ { 166000000, 166667, 0x88, 0x81 },
+ { 182000000, 166667, 0x88, 0xc1 },
+ { 286000000, 166667, 0x88, 0x02 },
+ { 386000000, 166667, 0x88, 0x42 },
+ { 446000000, 166667, 0x88, 0x82 },
+ { 466000000, 166667, 0x88, 0xc2 },
+ { 506000000, 166667, 0x88, 0x08 },
+ { 761000000, 166667, 0x88, 0x48 },
+ { 846000000, 166667, 0x88, 0x88 },
+ { 905000000, 166667, 0x88, 0xc8 },
+ },
+};
+
static struct dvb_pll_desc dvb_pll_lg_z201 = {
.name = "LG z201",
.min = 174000000,
@@ -537,6 +566,7 @@ static struct dvb_pll_desc dvb_pll_alps_tdee4 = {
static struct dvb_pll_desc *pll_list[] = {
[DVB_PLL_UNDEFINED] = NULL,
[DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579,
+ [DVB_PLL_THOMSON_DTT7546X] = &dvb_pll_thomson_dtt7546x,
[DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x,
[DVB_PLL_THOMSON_DTT7520X] = &dvb_pll_thomson_dtt7520x,
[DVB_PLL_LG_Z201] = &dvb_pll_lg_z201,
@@ -561,7 +591,7 @@ static struct dvb_pll_desc *pll_list[] = {
/* code */
static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf,
- const u32 frequency)
+ const u32 frequency, const u32 len)
{
struct dvb_pll_priv *priv = fe->tuner_priv;
struct dvb_pll_desc *desc = priv->pll_desc;
@@ -593,11 +623,15 @@ static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf,
if (desc->set)
desc->set(fe, buf);
- if (debug)
- printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
- desc->name, div, buf[0], buf[1], buf[2], buf[3]);
+ if (debug) {
+ printk(KERN_DEBUG "pll: %s: div=%d | buf=", desc->name, div);
+ for (i = 0; i < len; i++)
+ printk(KERN_DEBUG "0x%02x,", buf[i]);
- // calculate the frequency we set it to
+ printk(KERN_DEBUG "\n");
+ }
+
+ /* calculate the frequency we set it to */
return (div * desc->entries[i].stepsize) - desc->iffreq;
}
@@ -634,21 +668,39 @@ static int dvb_pll_sleep(struct dvb_frontend *fe)
return -EINVAL;
}
+static int dvb_pll_get_num_regs(struct dvb_pll_priv *priv)
+{
+ int num_regs = 4;
+
+ if (strncmp(priv->pll_desc->name, "Thomson dtt7546x", 16) == 0)
+ num_regs = 5;
+
+ return num_regs;
+}
+
static int dvb_pll_set_params(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct dvb_pll_priv *priv = fe->tuner_priv;
- u8 buf[4];
- struct i2c_msg msg =
- { .addr = priv->pll_i2c_address, .flags = 0,
- .buf = buf, .len = sizeof(buf) };
+ struct i2c_msg msg;
+ u8 *bufp;
int result;
u32 frequency = 0;
+ bufp = kzalloc(dvb_pll_get_num_regs(priv), GFP_KERNEL);
+
+ if (!bufp)
+ return -ENOMEM;
+
+ msg.addr = priv->pll_i2c_address;
+ msg.flags = 0;
+ msg.buf = bufp;
+ msg.len = dvb_pll_get_num_regs(priv);
+
if (priv->i2c == NULL)
return -EINVAL;
- result = dvb_pll_configure(fe, buf, c->frequency);
+ result = dvb_pll_configure(fe, bufp, c->frequency, msg.len);
if (result < 0)
return result;
else
@@ -677,7 +729,7 @@ static int dvb_pll_calc_regs(struct dvb_frontend *fe,
if (buf_len < 5)
return -EINVAL;
- result = dvb_pll_configure(fe, buf + 1, c->frequency);
+ result = dvb_pll_configure(fe, buf + 1, c->frequency, buf_len - 1);
if (result < 0)
return result;
else
diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h
index bf9602a..f523f42 100644
--- a/drivers/media/dvb-frontends/dvb-pll.h
+++ b/drivers/media/dvb-frontends/dvb-pll.h
@@ -28,6 +28,7 @@
#define DVB_PLL_SAMSUNG_TBMU24112 17
#define DVB_PLL_TDEE4 18
#define DVB_PLL_THOMSON_DTT7520X 19
+#define DVB_PLL_THOMSON_DTT7546X 20
/**
* Attach a dvb-pll to the supplied frontend structure.
--
1.9.1
When using stv0367 demodulator with STi STB platforms,
we can have easily have four or more stv0367 demods running
in the system at one time.
As typically the b2120 reference design ships with a b2004a daughter
board, which can accept two dvb NIM cards, and each b2100A NIM
has 2x stv0367 demods and 2x NXPs tuner on it.
In such circumstances it is useful to print the i2c address
on error messages to know which one is failing due to I2C issues.
Signed-off-by: Peter Griffin <[email protected]>
---
drivers/media/dvb-frontends/stv0367.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c
index b31ff26..c3b7e6c 100644
--- a/drivers/media/dvb-frontends/stv0367.c
+++ b/drivers/media/dvb-frontends/stv0367.c
@@ -791,11 +791,13 @@ int stv0367_writeregs(struct stv0367_state *state, u16 reg, u8 *data, int len)
memcpy(buf + 2, data, len);
if (i2cdebug)
- printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, buf[2]);
+ printk(KERN_DEBUG "%s: [%02x] %02x: %02x\n", __func__,
+ state->config->demod_address, reg, buf[2]);
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1)
- printk(KERN_ERR "%s: i2c write error!\n", __func__);
+ printk(KERN_ERR "%s: i2c write error! ([%02x] %02x: %02x)\n",
+ __func__, state->config->demod_address, reg, buf[2]);
return (ret != 1) ? -EREMOTEIO : 0;
}
@@ -829,10 +831,12 @@ static u8 stv0367_readreg(struct stv0367_state *state, u16 reg)
ret = i2c_transfer(state->i2c, msg, 2);
if (ret != 2)
- printk(KERN_ERR "%s: i2c read error\n", __func__);
+ printk(KERN_ERR "%s: i2c read error ([%02x] %02x: %02x)\n",
+ __func__, state->config->demod_address, reg, b1[0]);
if (i2cdebug)
- printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, b1[0]);
+ printk(KERN_DEBUG "%s: [%02x] %02x: %02x\n", __func__,
+ state->config->demod_address, reg, b1[0]);
return b1[0];
}
--
1.9.1
The B2100A dvb NIM card from ST has 2x stv0367 demodulators
and 2x TDA18212 silicon tuners, with a 16Mhz crystal. To
get this working properly with the upstream driver we need
to add support for the 16Mhz reference clock.
Signed-off-by: Peter Griffin <[email protected]>
---
drivers/media/dvb-frontends/stv0367.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c
index c3b7e6c..ad7cab7 100644
--- a/drivers/media/dvb-frontends/stv0367.c
+++ b/drivers/media/dvb-frontends/stv0367.c
@@ -1554,6 +1554,11 @@ static int stv0367ter_init(struct dvb_frontend *fe)
switch (state->config->xtal) {
/*set internal freq to 53.125MHz */
+ case 16000000:
+ stv0367_writereg(state, R367TER_PLLMDIV, 0x2);
+ stv0367_writereg(state, R367TER_PLLNDIV, 0x1b);
+ stv0367_writereg(state, R367TER_PLLSETUP, 0x18);
+ break;
case 25000000:
stv0367_writereg(state, R367TER_PLLMDIV, 0xa);
stv0367_writereg(state, R367TER_PLLNDIV, 0x55);
--
1.9.1
This patch adds the DT bindings documentation for the c8sectpfe LinuxDVB
demux driver whose IP is in the STiH407 family silicon SoC's.
Signed-off-by: Peter Griffin <[email protected]>
---
.../bindings/media/stih407-c8sectpfe.txt | 90 ++++++++++++++++++++++
include/dt-bindings/media/c8sectpfe.h | 14 ++++
2 files changed, 104 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt
create mode 100644 include/dt-bindings/media/c8sectpfe.h
diff --git a/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt b/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt
new file mode 100644
index 0000000..1ed4b12
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt
@@ -0,0 +1,90 @@
+STMicroelectronics STi c8sectpfe binding
+============================================
+
+This document describes the c8sectpfe device bindings that is used to get transport
+stream data into the SoC on the TS pins, and into DDR for further processing.
+
+It is typically used in conjunction with one or more demodulator and tuner devices
+which converts from the RF to digital domain. Demodulators and tuners are usually
+located on an external DVB frontend card connected to SoC TS input pins.
+
+Currently 7 TS input (tsin) channels are supported on the stih407 family SoC.
+
+Required properties (controller (parent) node):
+- compatible : Should be "stih407-c8sectpfe"
+
+- reg : Address and length of register sets for each device in
+ "reg-names"
+
+- reg-names : The names of the register addresses corresponding to the
+ registers filled in "reg":
+ - c8sectpfe: c8sectpfe registers
+ - c8sectpfe-ram: c8sectpfe internal sram
+
+- clocks : phandle list of c8sectpfe clocks
+- clock-names : should be "c8sectpfe"
+See: Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+- pinctrl-names : a pinctrl state named tsin%d-serial or tsin%d-parallel (where %d is tsin-num)
+ must be defined for each tsin child node.
+- pinctrl-0 : phandle referencing pin configuration for this tsin configuration
+See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+
+
+Required properties (tsin (child) node):
+
+- tsin-num : tsin id of the InputBlock (must be between 0 to 6)
+- i2c-bus : phandle to the I2C bus DT node which the demodulators & tuners on this tsin channel are connected.
+- rst-gpio : reset gpio for this tsin channel.
+
+Optional properties (tsin (child) node):
+
+- invert-ts-clk : Bool property to control sense of ts input clock (data stored on falling edge of clk).
+- serial-not-parallel : Bool property to configure input bus width (serial on ts_data<7>).
+- async-not-sync : Bool property to control if data is received in asynchronous mode
+ (all bits/bytes with ts_valid or ts_packet asserted are valid).
+
+- dvb-card : Describes the NIM card connected to this tsin channel.
+
+Example:
+
+/* stih410 SoC b2120 + b2004a + stv0367-pll(NIMB) + stv0367-tda18212 (NIMA) DT example) */
+
+ c8sectpfe@08a20000 {
+ compatible = "st,stih407-c8sectpfe";
+ status = "okay";
+ reg = <0x08a20000 0x10000>, <0x08a00000 0x4000>;
+ reg-names = "stfe", "stfe-ram";
+ interrupts = <0 34 0>, <0 35 0>;
+ interrupt-names = "stfe-error-irq", "stfe-idle-irq";
+
+ pinctrl-names = "tsin0-serial", "tsin0-parallel", "tsin3-serial",
+ "tsin4-serial", "tsin5-serial";
+
+ pinctrl-0 = <&pinctrl_tsin0_serial>;
+ pinctrl-1 = <&pinctrl_tsin0_parallel>;
+ pinctrl-2 = <&pinctrl_tsin3_serial>;
+ pinctrl-3 = <&pinctrl_tsin4_serial_alt3>;
+ pinctrl-4 = <&pinctrl_tsin5_serial_alt1>;
+
+ clocks = <&clk_s_c0_flexgen CLK_PROC_STFE>;
+ clock-names = "stfe";
+
+ /* tsin0 is TSA on NIMA */
+ tsin0: port@0 {
+ tsin-num = <0>;
+ serial-not-parallel;
+ i2c-bus = <&ssc2>;
+ rst-gpio = <&pio15 4 0>;
+ dvb-card = <STV0367_TDA18212_NIMA_1>;
+ };
+
+ /* tsin3 is TSB on NIMB */
+ tsin3: port@3 {
+ tsin-num = <3>;
+ serial-not-parallel;
+ i2c-bus = <&ssc3>;
+ rst-gpio = <&pio15 7 0>;
+ dvb-card = <STV0367_PLL_BOARD_NIMB>;
+ };
+ };
diff --git a/include/dt-bindings/media/c8sectpfe.h b/include/dt-bindings/media/c8sectpfe.h
new file mode 100644
index 0000000..45ad009
--- /dev/null
+++ b/include/dt-bindings/media/c8sectpfe.h
@@ -0,0 +1,14 @@
+#ifndef __DT_C8SECTPFE_H
+#define __DT_C8SECTPFE_H
+
+#define STV0367_PLL_BOARD_NIMA 0
+#define STV0367_PLL_BOARD_NIMB 1
+#define STV0367_TDA18212_NIMA_1 2
+#define STV0367_TDA18212_NIMA_2 3
+#define STV0367_TDA18212_NIMB_1 4
+#define STV0367_TDA18212_NIMB_2 5
+
+#define STV0903_6110_LNB24_NIMA 6
+#define STV0903_6110_LNB24_NIMB 7
+
+#endif /* __DT_C8SECTPFE_H */
--
1.9.1
This patch adds in the required DT node for the c8sectpfe
Linux DVB demux driver which allows the tsin channels
to be used on an upstream kernel.
Signed-off-by: Peter Griffin <[email protected]>
---
arch/arm/boot/dts/stihxxx-b2120.dtsi | 50 ++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/arch/arm/boot/dts/stihxxx-b2120.dtsi b/arch/arm/boot/dts/stihxxx-b2120.dtsi
index 1f27589..ece2ede 100644
--- a/arch/arm/boot/dts/stihxxx-b2120.dtsi
+++ b/arch/arm/boot/dts/stihxxx-b2120.dtsi
@@ -6,6 +6,10 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+
+#include <dt-bindings/clock/stih407-clks.h>
+#include <dt-bindings/media/c8sectpfe.h>
+
/ {
soc {
sbc_serial0: serial@9530000 {
@@ -72,5 +76,51 @@
st,osc-force-ext;
};
};
+
+ c8sectpfe@08a20000 {
+ compatible = "st,stih407-c8sectpfe";
+ status = "okay";
+ reg = <0x08a20000 0x10000>, <0x08a00000 0x4000>;
+ reg-names = "c8sectpfe", "c8sectpfe-ram";
+
+ interrupts = <0 34 0>, <0 35 0>;
+ interrupt-names = "c8sectpfe-error-irq",
+ "c8sectpfe-idle-irq";
+
+ pinctrl-names = "tsin0-serial", "tsin0-parallel",
+ "tsin3-serial", "tsin4-serial",
+ "tsin5-serial";
+
+ pinctrl-0 = <&pinctrl_tsin0_serial>;
+ pinctrl-1 = <&pinctrl_tsin0_parallel>;
+ pinctrl-2 = <&pinctrl_tsin3_serial>;
+ pinctrl-3 = <&pinctrl_tsin4_serial_alt3>;
+ pinctrl-4 = <&pinctrl_tsin5_serial_alt1>;
+
+ clocks = <&clk_s_c0_flexgen CLK_PROC_STFE>;
+ clock-names = "c8sectpfe";
+
+ /* tsin0 is TSA on NIMA */
+ tsin0: port@0 {
+
+ tsin-num = <0>;
+ serial-not-parallel;
+ i2c-bus = <&ssc2>;
+ rst-gpio = <&pio15 4 0>;
+
+ dvb-card = <STV0367_TDA18212_NIMA_1>;
+ };
+
+ /* tsin3 is TSB on NIMB */
+ tsin3: port@3 {
+
+ tsin-num = <3>;
+ serial-not-parallel;
+ i2c-bus = <&ssc3>;
+ rst-gpio = <&pio15 7 0>;
+
+ dvb-card = <STV0367_PLL_BOARD_NIMB>;
+ };
+ };
};
};
--
1.9.1
This patch adds support for the c8sectpfe input HW found on
STiH407/410 SoC's.
It currently supports the TS input block, memdma engine
and hw PID filtering blocks of the C8SECTPFE subsystem.
The driver creates one LinuxDVB adapter, and a
demux/dvr/frontend set of devices for each tsin channel
which is specificed in the DT. It has been tested with
multiple tsin channels tuned, locked, and grabbing TS
simultaneously.
Signed-off-by: Peter Griffin <[email protected]>
---
drivers/media/tsin/c8sectpfe/c8sectpfe-core.c | 1105 +++++++++++++++++++++++++
drivers/media/tsin/c8sectpfe/c8sectpfe-core.h | 288 +++++++
2 files changed, 1393 insertions(+)
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-core.c
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-core.h
diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-core.c b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.c
new file mode 100644
index 0000000..fbbe323
--- /dev/null
+++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.c
@@ -0,0 +1,1105 @@
+/*
+ * c8sectpfe-core.c - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author:Peter Bennett <[email protected]>
+ * Peter Griffin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/frontend.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/version.h>
+#include <linux/wait.h>
+
+#include "c8sectpfe-core.h"
+#include "c8sectpfe-common.h"
+#include "c8sectpfe-debugfs.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#define FIRMWARE_MEMDMA "pti_memdma_h407.elf"
+MODULE_FIRMWARE(FIRMWARE_MEMDMA);
+
+#define PID_TABLE_SIZE 1024
+#define POLL_20_HZ_DIV 20 /* poll at 20 Hz */
+
+static int load_slim_core_fw(struct c8sectpfei *fei);
+
+#define TS_PKT_SIZE 188
+#define HEADER_SIZE (4)
+#define PACKET_SIZE (TS_PKT_SIZE+HEADER_SIZE)
+
+#define FEI_ALIGNMENT (32)
+/* hw requires minimum of 8*PACKET_SIZE and padded to 8byte boundary */
+#define FEI_BUFFER_SIZE (8*PACKET_SIZE*340)
+
+#define FIFO_LEN 1024
+
+static void c8sectpfe_timer_interrupt(unsigned long ac8sectpfei)
+{
+ struct c8sectpfei *fei = (struct c8sectpfei *)ac8sectpfei;
+ struct channel_info *channel;
+ int chan_num;
+
+ /* iterate through input block channels */
+ for (chan_num = 0; chan_num < fei->tsin_count; chan_num++) {
+ channel = fei->channel_data[chan_num];
+
+ /* is this descriptor initialised and TP enabled */
+ if (channel->irec && readl((void __iomem *)
+ &channel->irec->tp_enable))
+ tasklet_schedule(&channel->tsklet);
+ }
+
+ fei->timer.expires = jiffies + (HZ / POLL_20_HZ_DIV);
+ add_timer(&fei->timer);
+}
+
+static void channel_swdemux_tsklet(unsigned long data)
+{
+ struct channel_info *channel = (struct channel_info *)data;
+ struct c8sectpfei *fei = channel->fei;
+ struct tpentry *ptrblk;
+ unsigned long wp, rp;
+ int pos, num_packets, n, size;
+ u8 *buf;
+
+ BUG_ON(!channel);
+ BUG_ON(!channel->irec);
+
+ ptrblk = &channel->irec->ptr_data[0];
+
+ wp = readl((void __iomem *)&ptrblk->dma_bus_wp);
+ rp = readl((void __iomem *)&ptrblk->dma_bus_rp);
+
+ pos = rp - channel->back_buffer_busaddr;
+
+ /* has it wrapped */
+ if (wp < rp)
+ wp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE;
+
+ size = wp - rp;
+ num_packets = size / PACKET_SIZE;
+
+ /* manage cache so data is visible to CPU */
+ dma_sync_single_for_cpu(fei->dev,
+ rp,
+ size,
+ DMA_FROM_DEVICE);
+
+ buf = (u8 *) channel->back_buffer_aligned;
+
+ dev_dbg(fei->dev,
+ "chan=%d channel=%p num_packets = %d, buf = %p, pos = 0x%x\n\t"
+ "rp=0x%lx, wp=0x%lx\n",
+ channel->tsin_id, channel, num_packets, buf, pos, rp, wp);
+
+ for (n = 0; n < num_packets; n++) {
+ dvb_dmx_swfilter_packets(
+ &fei->c8sectpfe[0]->
+ demux[channel->demux_mapping].dvb_demux,
+ &buf[pos], 1);
+
+ pos += PACKET_SIZE;
+ }
+
+ /* advance the read pointer */
+ if (wp == (channel->back_buffer_busaddr + FEI_BUFFER_SIZE))
+ writel(channel->back_buffer_busaddr,
+ (void __iomem *)&ptrblk->dma_bus_rp);
+ else
+ writel(wp, (void __iomem *)&ptrblk->dma_bus_rp);
+}
+
+static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *demux = dvbdmxfeed->demux;
+ struct stdemux *stdemux = (struct stdemux *)demux->priv;
+ struct c8sectpfei *fei = stdemux->c8sectpfei;
+ struct channel_info *channel;
+ struct tpentry *ptrblk;
+ u32 tmp;
+ unsigned long *bitmap;
+
+ switch (dvbdmxfeed->type) {
+ case DMX_TYPE_TS:
+ break;
+ case DMX_TYPE_SEC:
+ break;
+ default:
+ dev_err(fei->dev, "%s:%d Error bailing\n"
+ , __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (dvbdmxfeed->type == DMX_TYPE_TS) {
+ switch (dvbdmxfeed->pes_type) {
+ case DMX_PES_VIDEO:
+ case DMX_PES_AUDIO:
+ case DMX_PES_TELETEXT:
+ case DMX_PES_PCR:
+ case DMX_PES_OTHER:
+ break;
+ default:
+ dev_err(fei->dev, "%s:%d Error bailing\n"
+ , __func__, __LINE__);
+ return -EINVAL;
+ }
+ }
+
+ mutex_lock(&fei->lock);
+
+ channel = fei->channel_data[stdemux->tsin_index];
+
+ bitmap = (unsigned long *) channel->pid_buffer_aligned;
+
+ bitmap_set(bitmap, dvbdmxfeed->pid, 1);
+
+ /* manage cache so PID bitmap is visible to HW */
+ dma_sync_single_for_device(fei->dev,
+ channel->pid_buffer_busaddr,
+ PID_TABLE_SIZE,
+ DMA_TO_DEVICE);
+
+ channel->active = 1;
+
+ if (fei->global_feed_count == 0) {
+ fei->timer.expires = jiffies + (HZ / POLL_20_HZ_DIV);
+ add_timer(&fei->timer);
+ }
+
+ if (stdemux->running_feed_count == 0) {
+
+ dev_dbg(fei->dev, "Starting channel=%p\n", channel);
+
+ tasklet_init(&channel->tsklet, channel_swdemux_tsklet,
+ (unsigned long) channel);
+
+ /* Reset the internal inputblock sram pointers */
+ writel(channel->fifo,
+ fei->io + C8SECTPFE_IB_BUFF_STRT(channel->tsin_id));
+ writel(channel->fifo + FIFO_LEN - 1,
+ fei->io + C8SECTPFE_IB_BUFF_END(channel->tsin_id));
+
+ writel(channel->fifo,
+ fei->io + C8SECTPFE_IB_READ_PNT(channel->tsin_id));
+ writel(channel->fifo,
+ fei->io + C8SECTPFE_IB_WRT_PNT(channel->tsin_id));
+
+
+ /* reset read / write memdma ptrs for this channel */
+ ptrblk = &channel->irec->ptr_data[0];
+ writel(channel->back_buffer_busaddr,
+ (void __iomem *)&ptrblk->dma_busbase);
+
+ tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
+ writel(tmp, (void __iomem *)&ptrblk->dma_bustop);
+
+ writel(channel->back_buffer_busaddr,
+ (void __iomem *)&ptrblk->dma_bus_wp);
+
+ /* Issue a reset and enable InputBlock */
+ writel(C8SECTPFE_SYS_ENABLE | C8SECTPFE_SYS_RESET
+ , fei->io + C8SECTPFE_IB_SYS(channel->tsin_id));
+
+ /* and enable the tp */
+ writel(0x1, (void __iomem *)&channel->irec->tp_enable);
+
+ dev_dbg(fei->dev, "%s:%d Starting DMA feed on stdemux=%p\n"
+ , __func__, __LINE__, stdemux);
+ }
+
+ stdemux->running_feed_count++;
+ fei->global_feed_count++;
+
+ mutex_unlock(&fei->lock);
+
+ return 0;
+}
+
+static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+
+ struct dvb_demux *demux = dvbdmxfeed->demux;
+ struct stdemux *stdemux = (struct stdemux *)demux->priv;
+ struct c8sectpfei *fei = stdemux->c8sectpfei;
+ struct channel_info *channel;
+ struct tpentry *ptrblk;
+ int idlereq;
+ u32 tmp;
+ int ret;
+ unsigned long *bitmap;
+
+ mutex_lock(&fei->lock);
+
+ channel = fei->channel_data[stdemux->tsin_index];
+
+ bitmap = (unsigned long *) channel->pid_buffer_aligned;
+ bitmap_clear(bitmap, dvbdmxfeed->pid, 1);
+
+ /* manage cache so data is visible to HW */
+ dma_sync_single_for_device(fei->dev,
+ channel->pid_buffer_busaddr,
+ PID_TABLE_SIZE,
+ DMA_TO_DEVICE);
+
+ if (--stdemux->running_feed_count == 0) {
+
+ channel = fei->channel_data[stdemux->tsin_index];
+ ptrblk = &channel->irec->ptr_data[0];
+
+ /* TP re-configuration on page 168 */
+
+ /* disable IB (prevents more TS data going to memdma) */
+ writel(0, fei->io + C8SECTPFE_IB_SYS(channel->tsin_id));
+
+ /* disable this channels descriptor */
+ writel(0, (void __iomem *)&channel->irec->tp_enable);
+
+ tasklet_disable(&channel->tsklet);
+
+ /* now request memdma channel goes idle */
+ idlereq = (1 << channel->tsin_id) | IDLEREQ;
+ writel(idlereq, fei->io + DMA_IDLE_REQ);
+
+ /* wait for idle irq handler to signal completion */
+ ret = wait_for_completion_timeout(&channel->idle_completion,
+ msecs_to_jiffies(100));
+
+ if (ret == 0)
+ dev_warn(fei->dev,
+ "Timeout waiting for idle irq on tsin%d\n",
+ channel->tsin_id);
+
+ reinit_completion(&channel->idle_completion);
+
+ /* reset read / write ptrs for this channel */
+ writel(channel->back_buffer_busaddr,
+ (void __iomem *)&ptrblk->dma_busbase);
+
+ tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
+ writel(tmp, (void __iomem *)&ptrblk->dma_bustop);
+
+ writel(channel->back_buffer_busaddr,
+ (void __iomem *)&ptrblk->dma_bus_wp);
+
+ dev_dbg(fei->dev,
+ "%s:%d stopping DMA feed on stdemux=%p channel=%d\n",
+ __func__, __LINE__, stdemux, channel->tsin_id);
+
+ /* turn off all PIDS in the bitmap*/
+ memset((void *)channel->pid_buffer_aligned
+ , 0x00, PID_TABLE_SIZE);
+
+ /* manage cache so data is visible to HW */
+ dma_sync_single_for_device(fei->dev,
+ channel->pid_buffer_busaddr,
+ PID_TABLE_SIZE,
+ DMA_TO_DEVICE);
+
+ channel->active = 0;
+ }
+
+ if (--fei->global_feed_count == 0) {
+ dev_dbg(fei->dev, "%s:%d global_feed_count=%d\n"
+ , __func__, __LINE__, fei->global_feed_count);
+
+ del_timer(&fei->timer);
+ }
+
+ mutex_unlock(&fei->lock);
+
+ return 0;
+}
+
+static struct channel_info *find_channel(struct c8sectpfei *fei, int tsin_num)
+{
+ int i;
+
+ for (i = 0; i < C8SECTPFE_MAXCHANNEL; i++) {
+ if (!fei->channel_data[i])
+ continue;
+
+ if (fei->channel_data[i]->tsin_id == tsin_num)
+ return fei->channel_data[i];
+ }
+
+ return NULL;
+}
+
+static void c8sectpfe_getconfig(struct c8sectpfei *fei)
+{
+ struct c8sectpfe_hw *hw = &fei->hw_stats;
+
+ hw->num_ib = readl(fei->io + SYS_CFG_NUM_IB);
+ hw->num_mib = readl(fei->io + SYS_CFG_NUM_MIB);
+ hw->num_swts = readl(fei->io + SYS_CFG_NUM_SWTS);
+ hw->num_tsout = readl(fei->io + SYS_CFG_NUM_TSOUT);
+ hw->num_ccsc = readl(fei->io + SYS_CFG_NUM_CCSC);
+ hw->num_ram = readl(fei->io + SYS_CFG_NUM_RAM);
+ hw->num_tp = readl(fei->io + SYS_CFG_NUM_TP);
+
+ dev_info(fei->dev, "C8SECTPFE hw supports the following:\n");
+ dev_info(fei->dev, "Input Blocks: %d\n", hw->num_ib);
+ dev_info(fei->dev, "Merged Input Blocks: %d\n", hw->num_mib);
+ dev_info(fei->dev, "Software Transport Stream Inputs: %d\n"
+ , hw->num_swts);
+ dev_info(fei->dev, "Transport Stream Output: %d\n", hw->num_tsout);
+ dev_info(fei->dev, "Cable Card Converter: %d\n", hw->num_ccsc);
+ dev_info(fei->dev, "RAMs supported by C8SECTPFE: %d\n", hw->num_ram);
+ dev_info(fei->dev, "Tango TPs supported by C8SECTPFE: %d\n"
+ , hw->num_tp);
+}
+
+static irqreturn_t c8sectpfe_idle_irq_handler(int irq, void *priv)
+{
+ struct c8sectpfei *fei = priv;
+ struct channel_info *chan;
+ int bit;
+ unsigned long tmp = readl(fei->io + DMA_IDLE_REQ);
+
+ /* page 168 of functional spec: Clear the idle request
+ by writing 0 to the C8SECTPFE_DMA_IDLE_REQ register. */
+
+ /* signal idle completion */
+ for_each_set_bit(bit, &tmp, fei->hw_stats.num_ib) {
+
+ chan = find_channel(fei, bit);
+
+ if (chan)
+ complete(&chan->idle_completion);
+ }
+
+ writel(0, fei->io + DMA_IDLE_REQ);
+
+ return IRQ_HANDLED;
+}
+
+
+static void free_input_block(struct c8sectpfei *fei, struct channel_info *tsin)
+{
+ BUG_ON(!fei);
+ BUG_ON(!tsin);
+
+ if (tsin->back_buffer_busaddr)
+ if (!dma_mapping_error(fei->dev, tsin->back_buffer_busaddr))
+ dma_unmap_single(fei->dev, tsin->back_buffer_busaddr,
+ FEI_BUFFER_SIZE, DMA_BIDIRECTIONAL);
+
+ kfree(tsin->back_buffer_start);
+
+ if (tsin->pid_buffer_busaddr)
+ if (!dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr))
+ dma_unmap_single(fei->dev, tsin->pid_buffer_busaddr,
+ PID_TABLE_SIZE, DMA_BIDIRECTIONAL);
+
+ kfree(tsin->pid_buffer_start);
+}
+
+#define MAX_NAME 20
+
+static int configure_input_block(struct c8sectpfei *fei,
+ struct channel_info *tsin)
+{
+ int ret;
+ u32 tmp = 0;
+ struct tpentry *ptrblk;
+ char tsin_pin_name[MAX_NAME];
+
+ BUG_ON(!fei);
+ BUG_ON(!tsin);
+
+ dev_dbg(fei->dev, "%s:%d Configuring channel=%p tsin=%d\n"
+ , __func__, __LINE__, tsin, tsin->tsin_id);
+
+ init_completion(&tsin->idle_completion);
+
+ tsin->back_buffer_start = kzalloc(FEI_BUFFER_SIZE +
+ FEI_ALIGNMENT, GFP_KERNEL);
+
+ if (!tsin->back_buffer_start) {
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ /* Ensure backbuffer is 32byte aligned */
+ tsin->back_buffer_aligned = tsin->back_buffer_start
+ + FEI_ALIGNMENT;
+
+ tsin->back_buffer_aligned = (void *)
+ (((uintptr_t) tsin->back_buffer_aligned) & ~0x1F);
+
+ tsin->back_buffer_busaddr = dma_map_single(fei->dev,
+ (void *)tsin->back_buffer_aligned,
+ FEI_BUFFER_SIZE,
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) {
+ dev_err(fei->dev, "failed to map back_buffer\n");
+ ret = -EFAULT;
+ goto err_unmap;
+ }
+
+ /*
+ * The pid buffer can be configured (in hw) for byte or bit
+ * per pid. By powers of deduction we conclude stih407 family
+ * is configured (at SoC design stage) for bit per pid.
+ */
+ tsin->pid_buffer_start = kzalloc(2048, GFP_KERNEL);
+
+ if (!tsin->pid_buffer_start) {
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ /*
+ * PID buffer needs to be aligned to size of the pid table
+ * which at bit per pid is 1024 bytes (8192 pids / 8).
+ * PIDF_BASE register enforces this alignment when writing
+ * the register.
+ */
+
+ tsin->pid_buffer_aligned = tsin->pid_buffer_start +
+ PID_TABLE_SIZE;
+
+ tsin->pid_buffer_aligned = (void *)
+ (((uintptr_t) tsin->pid_buffer_aligned) & ~0x3ff);
+
+ tsin->pid_buffer_busaddr = dma_map_single(fei->dev,
+ tsin->pid_buffer_aligned,
+ PID_TABLE_SIZE,
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) {
+ dev_err(fei->dev, "failed to map pid_bitmap\n");
+ ret = -EFAULT;
+ goto err_unmap;
+ }
+
+ /* manage cache so pid bitmap is visible to HW */
+ dma_sync_single_for_device(fei->dev,
+ tsin->pid_buffer_busaddr,
+ PID_TABLE_SIZE,
+ DMA_TO_DEVICE);
+
+ snprintf(tsin_pin_name, MAX_NAME, "tsin%d-%s", tsin->tsin_id,
+ (tsin->serial_not_parallel ? "serial" : "parallel"));
+
+ tsin->pstate = pinctrl_lookup_state(fei->pinctrl, tsin_pin_name);
+ if (IS_ERR(tsin->pstate)) {
+ dev_err(fei->dev, "%s: pinctrl_lookup_state couldn't find %s state\n"
+ , __func__, tsin_pin_name);
+ ret = PTR_ERR(tsin->pstate);
+ goto err_unmap;
+ }
+
+ ret = pinctrl_select_state(fei->pinctrl, tsin->pstate);
+
+ if (ret) {
+ dev_err(fei->dev, "%s: pinctrl_select_state failed\n"
+ , __func__);
+ goto err_unmap;
+ }
+
+ /* Enable this input block */
+ tmp = readl(fei->io + SYS_INPUT_CLKEN);
+ tmp |= BIT(tsin->tsin_id);
+ writel(tmp, fei->io + SYS_INPUT_CLKEN);
+
+ if (tsin->serial_not_parallel)
+ tmp |= C8SECTPFE_SERIAL_NOT_PARALLEL;
+
+ if (tsin->invert_ts_clk)
+ tmp |= C8SECTPFE_INVERT_TSCLK;
+
+ if (tsin->async_not_sync)
+ tmp |= C8SECTPFE_ASYNC_NOT_SYNC;
+
+ tmp |= C8SECTPFE_ALIGN_BYTE_SOP | C8SECTPFE_BYTE_ENDIANNESS_MSB;
+
+ writel(tmp, fei->io + C8SECTPFE_IB_IP_FMT_CFG(tsin->tsin_id));
+
+ writel(C8SECTPFE_SYNC(0x9) |
+ C8SECTPFE_DROP(0x9) |
+ C8SECTPFE_TOKEN(0x47),
+ fei->io + C8SECTPFE_IB_SYNCLCKDRP_CFG(tsin->tsin_id));
+
+ writel(TS_PKT_SIZE, fei->io + C8SECTPFE_IB_PKT_LEN(tsin->tsin_id));
+
+ /* Place the FIFO's at the end of the irec descriptors */
+
+ tsin->fifo = (tsin->tsin_id * FIFO_LEN);
+
+ writel(tsin->fifo, fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id));
+ writel(tsin->fifo + FIFO_LEN - 1,
+ fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id));
+
+ writel(tsin->fifo, fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id));
+ writel(tsin->fifo, fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id));
+
+ writel(tsin->pid_buffer_busaddr,
+ fei->io + PIDF_BASE(tsin->tsin_id));
+
+ dev_dbg(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=0x%x\n",
+ tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)),
+ tsin->pid_buffer_busaddr);
+
+ /* Configure and enable HW PID filtering */
+
+ /*
+ * The PID value is created by assembling the first 8 bytes of
+ * the TS packet into a 64-bit word in big-endian format. A
+ * slice of that 64-bit word is taken from
+ * (PID_OFFSET+PID_NUM_BITS-1) to PID_OFFSET.
+ */
+ tmp = (C8SECTPFE_PID_ENABLE | C8SECTPFE_PID_NUMBITS(13)
+ | C8SECTPFE_PID_OFFSET(40));
+
+ writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(tsin->tsin_id));
+
+ dev_dbg(fei->dev, "chan=%d setting wp: %d, rp: %d, buf: %d-%d\n",
+ tsin->tsin_id,
+ readl(fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)),
+ readl(fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)),
+ readl(fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)),
+ readl(fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id)));
+
+ /*
+ * Base address of pointer record block relative to
+ * base address of DMEM
+ */
+ fei->irec = fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET +
+ readl(fei->io + DMA_PTRREC_BASE);
+
+ tsin->irec = fei->irec += tsin->tsin_id;
+
+ ptrblk = &tsin->irec->ptr_data[0];
+
+ writel(tsin->fifo,
+ (void __iomem *)&tsin->irec->dma_membase);
+
+ writel(tsin->fifo + FIFO_LEN - 1,
+ (void __iomem *)&tsin->irec->dma_memtop);
+
+ writel((188 + 7)&~7,
+ (void __iomem *)&tsin->irec->dma_ts_pktsize);
+
+ writel(0x1, (void __iomem *)&tsin->irec->tp_enable);
+
+ /* read/write pointers with physical bus address */
+ writel(tsin->back_buffer_busaddr,
+ (void __iomem *)&ptrblk->dma_busbase);
+
+ tmp = tsin->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
+ writel(tmp, (void __iomem *)&ptrblk->dma_bustop);
+
+ writel(tsin->back_buffer_busaddr,
+ (void __iomem *)&ptrblk->dma_bus_wp);
+
+ writel(tsin->back_buffer_busaddr,
+ (void __iomem *)&ptrblk->dma_bus_rp);
+
+ /* initialize tasklet */
+ tasklet_init(&tsin->tsklet, channel_swdemux_tsklet,
+ (unsigned long) tsin);
+
+ return 0;
+
+err_unmap:
+ free_input_block(fei, tsin);
+ return ret;
+}
+
+static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv)
+{
+ struct c8sectpfei *fei = priv;
+
+ dev_err(fei->dev, "%s: error handling not yet implemented\n"
+ , __func__);
+
+ /*
+ * TODO FIXME we should detect some error conditions here
+ * and ideally so something about them!
+ */
+
+ return IRQ_HANDLED;
+}
+
+static int c8sectpfe_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *child, *np = dev->of_node;
+ struct c8sectpfei *fei;
+ struct resource *res;
+ int ret, n;
+ struct channel_info *tsin;
+
+ /* Allocate the c8sectpfei structure */
+ fei = devm_kzalloc(dev, sizeof(struct c8sectpfei), GFP_KERNEL);
+ if (!fei)
+ return -ENOMEM;
+
+ fei->dev = dev;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "c8sectpfe");
+ fei->io = devm_ioremap_resource(dev, res);
+ if (IS_ERR(fei->io))
+ return PTR_ERR(fei->io);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "c8sectpfe-ram");
+ fei->sram = devm_ioremap_resource(dev, res);
+ if (IS_ERR(fei->sram))
+ return PTR_ERR(fei->sram);
+
+ fei->sram_size = res->end - res->start;
+
+ fei->idle_irq = platform_get_irq_byname(pdev, "c8sectpfe-idle-irq");
+ if (fei->idle_irq < 0) {
+ dev_err(dev, "Can't get c8sectpfe-idle-irq\n");
+ return fei->idle_irq;
+ }
+
+ fei->error_irq = platform_get_irq_byname(pdev, "c8sectpfe-error-irq");
+ if (fei->error_irq < 0) {
+ dev_err(dev, "Can't get c8sectpfe-error-irq\n");
+ return fei->error_irq;
+ }
+
+ platform_set_drvdata(pdev, fei);
+
+ fei->c8sectpfeclk = devm_clk_get(dev, "c8sectpfe");
+ if (IS_ERR(fei->c8sectpfeclk)) {
+ dev_err(dev, "c8sectpfe clk not found\n");
+ return PTR_ERR(fei->c8sectpfeclk);
+ }
+
+ ret = clk_prepare_enable(fei->c8sectpfeclk);
+ if (ret) {
+ dev_err(dev, "Failed to enable c8sectpfe clock\n");
+ return ret;
+ }
+
+ /* to save power disable all IP's (on by default) */
+ writel(0, fei->io + SYS_INPUT_CLKEN);
+
+ /* Enable memdma clock */
+ writel(MEMDMAENABLE, fei->io + SYS_OTHER_CLKEN);
+
+ /* clear internal sram */
+ memset_io(fei->sram, 0x0, fei->sram_size);
+
+ c8sectpfe_getconfig(fei);
+
+ ret = load_slim_core_fw(fei);
+ if (ret) {
+ dev_err(dev, "Couldn't load slim core firmware\n");
+ goto err_clk_disable;
+ }
+
+ ret = devm_request_irq(dev, fei->idle_irq, c8sectpfe_idle_irq_handler,
+ 0, "c8sectpfe-idle-irq", fei);
+ if (ret) {
+ dev_err(dev, "Can't register c8sectpfe-idle-irq IRQ.\n");
+ goto err_clk_disable;
+ }
+
+ ret = devm_request_irq(dev, fei->error_irq,
+ c8sectpfe_error_irq_handler, 0,
+ "c8sectpfe-error-irq", fei);
+ if (ret) {
+ dev_err(dev, "Can't register c8sectpfe-error-irq IRQ.\n");
+ goto err_clk_disable;
+ }
+
+ fei->tsin_count = of_get_child_count(np);
+
+ if (fei->tsin_count > C8SECTPFE_MAX_TSIN_CHAN ||
+ fei->tsin_count > fei->hw_stats.num_ib) {
+
+ dev_err(dev, "More tsin declared than exist on SoC!\n");
+ ret = -EINVAL;
+ goto err_clk_disable;
+ }
+
+ fei->pinctrl = devm_pinctrl_get(dev);
+
+ if (IS_ERR(fei->pinctrl)) {
+ dev_err(dev, "Error getting tsin pins\n");
+ ret = PTR_ERR(fei->pinctrl);
+ goto err_clk_disable;
+ }
+
+ n = 0;
+ for_each_child_of_node(np, child) {
+ struct device_node *i2c_bus;
+
+ fei->channel_data[n] = devm_kzalloc(dev,
+ sizeof(struct channel_info),
+ GFP_KERNEL);
+
+ if (!fei->channel_data[n]) {
+ ret = -ENOMEM;
+ goto err_clk_disable;
+ }
+
+ tsin = fei->channel_data[n];
+
+ tsin->fei = fei;
+
+ ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id);
+ if (ret) {
+ dev_err(&pdev->dev, "No tsin_num found\n");
+ goto err_clk_disable;
+ }
+
+ /* sanity check value */
+ if (tsin->tsin_id > fei->hw_stats.num_ib) {
+ dev_err(&pdev->dev,
+ "tsin-num %d specified greater than number\n\t"
+ "of input block hw in SoC! (%d)",
+ tsin->tsin_id, fei->hw_stats.num_ib);
+ ret = -EINVAL;
+ goto err_clk_disable;
+ }
+
+ tsin->invert_ts_clk = of_property_read_bool(child,
+ "invert-ts-clk");
+
+ tsin->serial_not_parallel = of_property_read_bool(child,
+ "serial-not-parallel");
+
+ tsin->async_not_sync = of_property_read_bool(child,
+ "async-not-sync");
+
+ ret = of_property_read_u32(child, "dvb-card",
+ &tsin->dvb_card);
+ if (ret) {
+ dev_err(&pdev->dev, "No dvb-card found\n");
+ goto err_clk_disable;
+ }
+
+ i2c_bus = of_parse_phandle(child, "i2c-bus", 0);
+ if (!i2c_bus) {
+ dev_err(&pdev->dev, "No i2c-bus found\n");
+ goto err_clk_disable;
+ }
+ tsin->i2c_adapter =
+ of_find_i2c_adapter_by_node(i2c_bus);
+ if (!tsin->i2c_adapter) {
+ dev_err(&pdev->dev, "No i2c adapter found\n");
+ of_node_put(i2c_bus);
+ goto err_clk_disable;
+ }
+ of_node_put(i2c_bus);
+
+ tsin->rst_gpio = of_get_named_gpio(child, "rst-gpio", 0);
+
+ ret = gpio_is_valid(tsin->rst_gpio);
+ if (!ret) {
+ dev_err(dev,
+ "reset gpio for tsin%d not valid (gpio=%d)\n",
+ tsin->tsin_id, tsin->rst_gpio);
+ goto err_clk_disable;
+ }
+
+ ret = devm_gpio_request_one(dev, tsin->rst_gpio,
+ GPIOF_OUT_INIT_LOW, "NIM reset");
+ if (ret && ret != -EBUSY) {
+ dev_err(dev, "Can't request tsin%d reset gpio\n"
+ , fei->channel_data[n]->tsin_id);
+ goto err_clk_disable;
+ }
+
+ if (!ret) {
+ /* toggle reset lines */
+ gpio_direction_output(tsin->rst_gpio, 0);
+ msleep(1);
+ gpio_direction_output(tsin->rst_gpio, 1);
+ msleep(1);
+ }
+
+ tsin->demux_mapping = n;
+
+ dev_dbg(fei->dev,
+ "channel=%p n=%d tsin_num=%d, invert-ts-clk=%d\n\t"
+ "serial-not-parallel=%d pkt-clk-valid=%d dvb-card=%d\n",
+ fei->channel_data[n], n,
+ tsin->tsin_id, tsin->invert_ts_clk,
+ tsin->serial_not_parallel, tsin->async_not_sync,
+ tsin->dvb_card);
+
+ ret = configure_input_block(fei, fei->channel_data[n]);
+
+ if (ret) {
+ dev_err(fei->dev,
+ "configure_input_block tsin=%d failed\n",
+ fei->channel_data[n]->tsin_id);
+ goto err_unmap;
+ }
+
+ n++;
+ }
+
+ /* Setup timer interrupt */
+ init_timer(&fei->timer);
+ fei->timer.function = c8sectpfe_timer_interrupt;
+ fei->timer.data = (unsigned long)fei;
+
+ mutex_init(&fei->lock);
+
+ /* Get the configuration information about the tuners */
+ ret = c8sectpfe_tuner_register_frontend(&fei->c8sectpfe[0],
+ (void *)fei,
+ c8sectpfe_start_feed,
+ c8sectpfe_stop_feed);
+ if (ret)
+ goto err_unmap;
+
+ /*
+ * STBus target port can access IMEM and DMEM ports
+ * without waiting for CPU
+ */
+ writel(0x1, fei->io + DMA_PER_STBUS_SYNC);
+
+ dev_info(dev, "Boot the memdma SLIM core\n");
+ writel(0x1, fei->io + DMA_CPU_RUN);
+
+ c8sectpfe_debugfs_init(fei);
+
+ return 0;
+
+err_unmap:
+ for (n = 0; n < fei->tsin_count; n++) {
+ tsin = fei->channel_data[n];
+
+ if (tsin)
+ free_input_block(fei, tsin);
+ }
+
+err_clk_disable:
+ /* TODO uncomment when upstream has taken a reference on this clk */
+ /*clk_disable_unprepare(fei->c8sectpfeclk);*/
+ return ret;
+}
+
+static int c8sectpfe_remove(struct platform_device *pdev)
+{
+ struct c8sectpfei *fei = platform_get_drvdata(pdev);
+ struct channel_info *channel;
+ unsigned int n;
+
+ c8sectpfe_tuner_unregister_frontend(fei->c8sectpfe[0], fei);
+
+ /*
+ * Now loop through and un-configure each of the InputBlock resources
+ */
+ for (n = 0; n < fei->tsin_count; n++) {
+ channel = fei->channel_data[n];
+ if (channel)
+ free_input_block(fei, channel);
+ }
+
+ c8sectpfe_debugfs_exit(fei);
+
+ dev_info(fei->dev, "Stopping memdma SLIM core\n");
+ if (readl(fei->io + DMA_CPU_RUN))
+ writel(0x0, fei->io + DMA_CPU_RUN);
+
+ /* unclock all internal IP's */
+ if (readl(fei->io + SYS_INPUT_CLKEN))
+ writel(0, fei->io + SYS_INPUT_CLKEN);
+ if (readl(fei->io + SYS_OTHER_CLKEN))
+ writel(0, fei->io + SYS_OTHER_CLKEN);
+
+ /* TODO uncomment when upstream has taken a reference on this clk */
+ /*
+ if (fei->c8sectpfeclk)
+ clk_disable_unprepare(fei->c8sectpfeclk);
+ */
+
+ return 0;
+}
+
+static int load_slim_core_fw(struct c8sectpfei *fei)
+{
+ const struct firmware *fw = NULL;
+ unsigned char *pImem = NULL;
+ unsigned char *pDmem = NULL;
+ Elf32_Ehdr *ehdr;
+ Elf32_Phdr *phdr;
+ int err = 0;
+ int i;
+
+ dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA);
+
+ err = request_firmware(&fw, FIRMWARE_MEMDMA, fei->dev);
+ if (err) {
+ dev_err(fei->dev, "Failed to load %s, %d.\n",
+ FIRMWARE_MEMDMA, err);
+ return -EINVAL;
+ }
+
+ /* Check ELF magic */
+ ehdr = (Elf32_Ehdr *)fw->data;
+ if (ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
+ ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
+ ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
+ ehdr->e_ident[EI_MAG3] != ELFMAG3) {
+ dev_err(fei->dev, "Invalid ELF magic\n");
+ err = -ENODEV;
+ goto done;
+ } else {
+ dev_dbg(fei->dev, "Valid ELF header found\n");
+ }
+
+ /* Check program headers are within firmware size */
+ if (ehdr->e_phoff + (ehdr->e_phnum * sizeof(Elf32_Phdr)) > fw->size) {
+ dev_err(fei->dev, "Program headers outside of firmware file\n");
+ err = -ENODEV;
+ goto done;
+ }
+
+ pImem = (unsigned char *) fei->io + DMA_MEMDMA_OFFSET + DMA_IMEM_OFFSET;
+ pDmem = (unsigned char *) fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET;
+
+ phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff);
+
+ /* go through the available ELF segments */
+ for (i = 0; i < ehdr->e_phnum && !err; i++, phdr++) {
+ unsigned char *dest;
+ int imem = 0;
+
+ /* Only consider LOAD segments */
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ /*
+ * Check segment is contained within the fw->data buffer
+ */
+ if (phdr->p_offset + phdr->p_filesz > fw->size) {
+ dev_err(fei->dev,
+ "Segment %d is outside of firmware file\n", i);
+ err = -EINVAL;
+ break;
+ }
+
+ /*
+ * MEMDMA IMEM has executable flag set, otherwise load
+ * this segment to DMEM.
+ */
+
+ if (phdr->p_flags & PF_X) {
+ dest = pImem;
+ imem = 1;
+ } else {
+ dest = pDmem;
+ }
+
+ /*
+ * The Slim ELF file uses 32-bit word addressing for
+ * load offsets.
+ */
+
+ dest += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int);
+
+ /*
+ * For DMEM segments copy the segment data from the ELF
+ * file and pad segment with zeroes
+ *
+ * For IMEM segments, the segment contains 24-bit
+ * instructions which must be padded to 32-bit
+ * instructions before being written. The written
+ * segment is padded with NOP instructions.
+ */
+
+ if (!imem) {
+ dev_dbg(fei->dev,
+ "Loading DMEM segment %d 0x%08x\n\t"
+ "(0x%x bytes) -> 0x%08x (0x%x bytes)\n",
+ i, phdr->p_paddr, phdr->p_filesz,
+ (unsigned int)dest, phdr->p_memsz);
+
+ memcpy((void *)dest, (void *)fw->data + phdr->p_offset,
+ phdr->p_filesz);
+
+ memset((void *)dest + phdr->p_filesz, 0,
+ phdr->p_memsz - phdr->p_filesz);
+ } else {
+ const unsigned char *imem_src = fw->data
+ + phdr->p_offset;
+ unsigned char *imem_dest = dest;
+ int j;
+
+ dev_dbg(fei->dev,
+ "Loading IMEM segment %d 0x%08x\n\t"
+ " (0x%x bytes) -> 0x%08x (0x%x bytes)\n", i,
+ phdr->p_paddr, phdr->p_filesz,
+ (unsigned int)dest,
+ phdr->p_memsz + phdr->p_memsz / 3);
+
+ for (j = 0; j < phdr->p_filesz; j++) {
+ *imem_dest = *imem_src;
+
+ /* Every 3 bytes, add an additional
+ * padding zero in destination */
+ if (j % 3 == 2) {
+ imem_dest++;
+ *imem_dest = 0x00;
+ }
+ imem_dest++;
+ imem_src++;
+ }
+ }
+ }
+
+done:
+ release_firmware(fw);
+ return err;
+}
+
+static const struct of_device_id c8sectpfe_match[] = {
+ { .compatible = "st,stih407-c8sectpfe" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, c8sectpfe_match);
+
+static struct platform_driver c8sectpfe_driver = {
+ .driver = {
+ .name = "c8sectpfe",
+ .of_match_table = of_match_ptr(c8sectpfe_match),
+ },
+ .probe = c8sectpfe_probe,
+ .remove = c8sectpfe_remove,
+};
+
+module_platform_driver(c8sectpfe_driver);
+
+MODULE_AUTHOR("Peter Bennett <[email protected]>");
+MODULE_AUTHOR("Peter Griffin <[email protected]>");
+MODULE_DESCRIPTION("C8SECTPFE STi DVB Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-core.h b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.h
new file mode 100644
index 0000000..3466303
--- /dev/null
+++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.h
@@ -0,0 +1,288 @@
+/*
+ * c8sectpfe-core.h - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author:Peter Bennett <[email protected]>
+ * Peter Griffin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+#ifndef _C8SECTPFE_CORE_H_
+#define _C8SECTPFE_CORE_H_
+
+#define C8SECTPFEI_MAXCHANNEL 16
+#define C8SECTPFEI_MAXADAPTER 3
+
+#define C8SECTPFE_MAX_TSIN_CHAN 8
+
+struct channel_info {
+
+ int tsin_id;
+ bool invert_ts_clk;
+ bool serial_not_parallel;
+ bool async_not_sync;
+ int i2c;
+ int dvb_card;
+
+ int rst_gpio;
+
+ struct i2c_adapter *i2c_adapter;
+ struct i2c_adapter *tuner_i2c;
+ struct i2c_adapter *lnb_i2c;
+ struct i2c_client *i2c_client;
+ struct dvb_frontend *frontend;
+
+ struct pinctrl_state *pstate;
+
+ int demux_mapping;
+ int active;
+
+ void *back_buffer_start;
+ void *back_buffer_aligned;
+ dma_addr_t back_buffer_busaddr;
+
+ void *pid_buffer_start;
+ void *pid_buffer_aligned;
+ dma_addr_t pid_buffer_busaddr;
+
+ unsigned long fifo;
+
+ struct completion idle_completion;
+ struct tasklet_struct tsklet;
+
+ struct c8sectpfei *fei;
+ struct c8sectpfe_input_record *irec;
+
+};
+
+struct c8sectpfe_hw {
+ int num_ib;
+ int num_mib;
+ int num_swts;
+ int num_tsout;
+ int num_ccsc;
+ int num_ram;
+ int num_tp;
+};
+
+struct c8sectpfei {
+
+ struct device *dev;
+ struct pinctrl *pinctrl;
+
+ struct dentry *root;
+ struct debugfs_regset32 *regset;
+
+ int tsin_count;
+
+ struct c8sectpfe_hw hw_stats;
+
+ struct c8sectpfe *c8sectpfe[C8SECTPFEI_MAXADAPTER];
+
+ int mapping[C8SECTPFEI_MAXCHANNEL];
+
+ struct mutex lock;
+
+ struct timer_list timer; /* timer interrupts for outputs */
+
+ void __iomem *io;
+ void __iomem *sram;
+
+ unsigned long sram_size;
+
+ struct channel_info *channel_data[C8SECTPFE_MAX_TSIN_CHAN];
+
+ struct clk *c8sectpfeclk;
+ int nima_rst_gpio;
+ int nimb_rst_gpio;
+
+ int idle_irq;
+ int error_irq;
+
+ int global_feed_count;
+
+ struct c8sectpfe_input_record *irec;
+};
+
+/* C8SECTPFE SYS Regs list */
+
+#define SYS_INPUT_ERR_STATUS 0x0
+#define SYS_OTHER_ERR_STATUS 0x8
+#define SYS_INPUT_ERR_MASK 0x10
+#define SYS_OTHER_ERR_MASK 0x18
+#define SYS_DMA_ROUTE 0x20
+#define SYS_INPUT_CLKEN 0x30
+#define IBENABLE_MASK 0x7F
+
+#define SYS_OTHER_CLKEN 0x38
+#define TSDMAENABLE BIT(1)
+#define MEMDMAENABLE BIT(0)
+
+#define SYS_CFG_NUM_IB 0x200
+#define SYS_CFG_NUM_MIB 0x204
+#define SYS_CFG_NUM_SWTS 0x208
+#define SYS_CFG_NUM_TSOUT 0x20C
+#define SYS_CFG_NUM_CCSC 0x210
+#define SYS_CFG_NUM_RAM 0x214
+#define SYS_CFG_NUM_TP 0x218
+
+/* Input Block Regs */
+
+#define C8SECTPFE_INPUTBLK_OFFSET 0x1000
+#define C8SECTPFE_CHANNEL_OFFSET(x) ((x*0x40) + C8SECTPFE_INPUTBLK_OFFSET)
+
+#define C8SECTPFE_IB_IP_FMT_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x00)
+#define C8SECTPFE_IGNORE_ERR_AT_SOP BIT(7)
+#define C8SECTPFE_IGNORE_ERR_IN_PKT BIT(6)
+#define C8SECTPFE_IGNORE_ERR_IN_BYTE BIT(5)
+#define C8SECTPFE_INVERT_TSCLK BIT(4)
+#define C8SECTPFE_ALIGN_BYTE_SOP BIT(3)
+#define C8SECTPFE_ASYNC_NOT_SYNC BIT(2)
+#define C8SECTPFE_BYTE_ENDIANNESS_MSB BIT(1)
+#define C8SECTPFE_SERIAL_NOT_PARALLEL BIT(0)
+
+#define C8SECTPFE_IB_SYNCLCKDRP_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x04)
+#define C8SECTPFE_SYNC(x) (x & 0xf)
+#define C8SECTPFE_DROP(x) ((x<<4) & 0xf)
+#define C8SECTPFE_TOKEN(x) ((x<<8) & 0xff00)
+#define C8SECTPFE_SLDENDIANNESS BIT(16)
+
+#define C8SECTPFE_IB_TAGBYTES_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x08)
+#define C8SECTPFE_TAG_HEADER(x) (x << 16)
+#define C8SECTPFE_TAG_COUNTER(x) ((x<<1) & 0x7fff)
+#define C8SECTPFE_TAG_ENABLE BIT(0)
+
+#define C8SECTPFE_IB_PID_SET(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x0C)
+#define C8SECTPFE_PID_OFFSET(x) (x & 0x3f)
+#define C8SECTPFE_PID_NUMBITS(x) ((x << 6) & 0xfff)
+#define C8SECTPFE_PID_ENABLE BIT(31)
+
+#define C8SECTPFE_IB_PKT_LEN(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x10)
+
+#define C8SECTPFE_IB_BUFF_STRT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x14)
+#define C8SECTPFE_IB_BUFF_END(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x18)
+#define C8SECTPFE_IB_READ_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x1C)
+#define C8SECTPFE_IB_WRT_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x20)
+
+#define C8SECTPFE_IB_PRI_THRLD(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x24)
+#define C8SECTPFE_PRI_VALUE(x) (x & 0x7fffff)
+#define C8SECTPFE_PRI_LOWPRI(x) ((x & 0xf) << 24)
+#define C8SECTPFE_PRI_HIGHPRI(x) ((x & 0xf) << 28)
+
+#define C8SECTPFE_IB_STAT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x28)
+#define C8SECTPFE_STAT_FIFO_OVERFLOW(x) (x & 0x1)
+#define C8SECTPFE_STAT_BUFFER_OVERFLOW(x) (x & 0x2)
+#define C8SECTPFE_STAT_OUTOFORDERRP(x) (x & 0x4)
+#define C8SECTPFE_STAT_PID_OVERFLOW(x) (x & 0x8)
+#define C8SECTPFE_STAT_PKT_OVERFLOW(x) (x & 0x10)
+#define C8SECTPFE_STAT_ERROR_PACKETS(x) ((x >> 8) & 0xf)
+#define C8SECTPFE_STAT_SHORT_PACKETS(x) ((x >> 12) & 0xf)
+
+#define C8SECTPFE_IB_MASK(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x2C)
+#define C8SECTPFE_MASK_FIFO_OVERFLOW BIT(0)
+#define C8SECTPFE_MASK_BUFFER_OVERFLOW BIT(1)
+#define C8SECTPFE_MASK_OUTOFORDERRP(x) BIT(2)
+#define C8SECTPFE_MASK_PID_OVERFLOW(x) BIT(3)
+#define C8SECTPFE_MASK_PKT_OVERFLOW(x) BIT(4)
+#define C8SECTPFE_MASK_ERROR_PACKETS(x) ((x & 0xf) << 8)
+#define C8SECTPFE_MASK_SHORT_PACKETS(x) ((x & 0xf) >> 12)
+
+#define C8SECTPFE_IB_SYS(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x30)
+#define C8SECTPFE_SYS_RESET BIT(1)
+#define C8SECTPFE_SYS_ENABLE BIT(0)
+
+/*
+ * Ponter record data structure required for each input block
+ * see Table 82 on page 167
+ */
+
+struct tpentry {
+ /* The following entries are bus addresses for memdma */
+ unsigned long dma_busbase;
+ unsigned long dma_bustop;
+ unsigned long dma_bus_wp;
+ unsigned long dma_bus_rp;
+};
+
+struct c8sectpfe_input_record {
+ unsigned long dma_membase; /* Internal sram base address */
+ unsigned long dma_memtop; /* Internal sram top address */
+
+ /*
+ * TS packet size, including tag bytes added by input block,
+ * rounded up to the next multiple of 8 bytes. The packet size,
+ * including any tagging bytes and rounded up to the nearest
+ * multiple of 8 bytes must be less than 255 bytes.
+ */
+ unsigned long dma_ts_pktsize;
+ unsigned long tp_enable;
+ struct tpentry ptr_data[1];
+};
+
+#define DMA_MEMDMA_OFFSET 0x4000
+#define DMA_IMEM_OFFSET 0x0
+#define DMA_DMEM_OFFSET 0x4000
+#define DMA_CPU 0x8000
+#define DMA_PER_OFFSET 0xb000
+
+/* XP70 Slim core regs */
+#define DMA_CPU_ID (DMA_MEMDMA_OFFSET + DMA_CPU + 0x0)
+#define DMA_CPU_VCR (DMA_MEMDMA_OFFSET + DMA_CPU + 0x4)
+#define DMA_CPU_RUN (DMA_MEMDMA_OFFSET + DMA_CPU + 0x8)
+#define DMA_CPU_CLOCKGATE (DMA_MEMDMA_OFFSET + DMA_CPU + 0xc)
+#define DMA_CPU_PC (DMA_MEMDMA_OFFSET + DMA_CPU + 0x20)
+
+/* Enable Interrupt for a IB */
+#define DMA_PER_TPn_DREQ_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd00)
+/* Ack interrupt by setting corresponding bit */
+#define DMA_PER_TPn_DACK_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd80)
+#define DMA_PER_TPn_DREQ (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe00)
+#define DMA_PER_TPn_DACK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe80)
+#define DMA_PER_DREQ_MODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf80)
+#define DMA_PER_STBUS_SYNC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf88)
+#define DMA_PER_STBUS_ACCESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf8c)
+#define DMA_PER_STBUS_ADDRESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf90)
+#define DMA_PER_IDLE_INT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfa8)
+#define DMA_PER_PRIORITY (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfac)
+#define DMA_PER_MAX_OPCODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb0)
+#define DMA_PER_MAX_CHUNK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb4)
+#define DMA_PER_PAGE_SIZE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfbc)
+#define DMA_PER_MBOX_STATUS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc0)
+#define DMA_PER_MBOX_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc8)
+#define DMA_PER_MBOX_CLEAR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd0)
+#define DMA_PER_MBOX_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd8)
+#define DMA_PER_INJECT_PKT_SRC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe0)
+#define DMA_PER_INJECT_PKT_DEST (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe4)
+#define DMA_PER_INJECT_PKT_ADDR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe8)
+#define DMA_PER_INJECT_PKT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfec)
+#define DMA_PER_PAT_PTR_INIT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff0)
+#define DMA_PER_PAT_PTR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff4)
+#define DMA_PER_SLEEP_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff8)
+#define DMA_PER_SLEEP_COUNTER (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xffc)
+/* #define DMA_RF_CPUREGn DMA_RFBASEADDR n=0 to 15) slim regsa */
+
+/* The following are from DMA_DMEM_BaseAddress */
+#define DMA_FIRMWARE_VERSION (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x0)
+#define DMA_PTRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x4)
+#define DMA_PTRREC_INPUT_OFFSET (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x8)
+#define DMA_ERRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0xc)
+#define DMA_ERROR_RECORD(n) ((n*4) + DMA_ERRREC_BASE + 0x4)
+#define DMA_IDLE_REQ (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x10)
+#define IDLEREQ BIT(31)
+
+#define DMA_FIRMWARE_CONFIG (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x14)
+
+/* Regs for PID Filter */
+
+#define PIDF_OFFSET 0x2800
+#define PIDF_BASE(n) ((n*4) + PIDF_OFFSET)
+#define PIDF_LEAK_ENABLE (PIDF_OFFSET + 0x100)
+#define PIDF_LEAK_STATUS (PIDF_OFFSET + 0x108)
+#define PIDF_LEAK_COUNT_RESET (PIDF_OFFSET + 0x110)
+#define PIDF_LEAK_COUNTER (PIDF_OFFSET + 0x114)
+
+#endif /* _C8SECTPFE_CORE_H_ */
--
1.9.1
These functions are used by the core code for creating the LDVB
devices and adapter.
Addtionally some older SoC's (and potentially newer ones) have different
frontend HW which would allow those devices to be easily supported
in the future by keeping the code specific to the IP separate from the
more generic code.
Signed-off-by: Peter Griffin <[email protected]>
---
drivers/media/tsin/c8sectpfe/c8sectpfe-common.c | 266 ++++++++++++++++++++++++
drivers/media/tsin/c8sectpfe/c8sectpfe-common.h | 66 ++++++
2 files changed, 332 insertions(+)
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-common.c
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-common.h
diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-common.c b/drivers/media/tsin/c8sectpfe/c8sectpfe-common.c
new file mode 100644
index 0000000..c8d607d
--- /dev/null
+++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-common.c
@@ -0,0 +1,266 @@
+/*
+ * c8sectpfe-common.c - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author: Peter Griffin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dvb/dmx.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "c8sectpfe-common.h"
+#include "c8sectpfe-core.h"
+#include "c8sectpfe-dvb.h"
+
+static int register_dvb(struct stdemux *demux, struct dvb_adapter *adap,
+ void *start_feed, void *stop_feed,
+ struct c8sectpfei *fei)
+{
+ int result;
+
+ demux->dvb_demux.dmx.capabilities = DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+
+ demux->dvb_demux.priv = demux;
+ demux->dvb_demux.filternum = C8SECTPFE_MAXCHANNEL;
+ demux->dvb_demux.feednum = C8SECTPFE_MAXCHANNEL;
+
+ demux->dvb_demux.start_feed = start_feed;
+ demux->dvb_demux.stop_feed = stop_feed;
+ demux->dvb_demux.write_to_decoder = NULL;
+
+ result = dvb_dmx_init(&demux->dvb_demux);
+ if (result < 0) {
+ dev_err(fei->dev, "dvb_dmx_init failed (errno = %d)\n",
+ result);
+ goto err_dmx;
+ }
+
+ demux->dmxdev.filternum = demux->dvb_demux.filternum;
+ demux->dmxdev.demux = &demux->dvb_demux.dmx;
+ demux->dmxdev.capabilities = 0;
+
+ result = dvb_dmxdev_init(&demux->dmxdev, adap);
+ if (result < 0) {
+ dev_err(fei->dev, "dvb_dmxdev_init failed (errno = %d)\n",
+ result);
+
+ goto err_dmxdev;
+ }
+
+ demux->hw_frontend.source = DMX_FRONTEND_0 + demux->tsin_index;
+
+ result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx,
+ &demux->hw_frontend);
+ if (result < 0) {
+ dev_err(fei->dev, "add_frontend failed (errno = %d)\n", result);
+ goto err_fe_hw;
+ }
+
+ demux->mem_frontend.source = DMX_MEMORY_FE;
+ result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx,
+ &demux->mem_frontend);
+ if (result < 0) {
+ dev_err(fei->dev, "add_frontend failed (%d)\n", result);
+ goto err_fe_mem;
+ }
+
+ result = demux->dvb_demux.dmx.connect_frontend(&demux->dvb_demux.dmx,
+ &demux->hw_frontend);
+ if (result < 0) {
+ dev_err(fei->dev, "connect_frontend (%d)\n", result);
+ goto err_fe_con;
+ }
+
+ return 0;
+
+err_fe_con:
+ demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
+ &demux->mem_frontend);
+err_fe_mem:
+ demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
+ &demux->hw_frontend);
+err_fe_hw:
+ dvb_dmxdev_release(&demux->dmxdev);
+err_dmxdev:
+ dvb_dmx_release(&demux->dvb_demux);
+err_dmx:
+ return result;
+
+}
+
+static void unregister_dvb(struct stdemux *demux)
+{
+
+ demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
+ &demux->mem_frontend);
+
+ demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
+ &demux->hw_frontend);
+
+ dvb_dmxdev_release(&demux->dmxdev);
+
+ dvb_dmx_release(&demux->dvb_demux);
+}
+
+static struct c8sectpfe *c8sectpfe_create(struct c8sectpfei *fei,
+ void *start_feed,
+ void *stop_feed)
+{
+ struct c8sectpfe *c8sectpfe;
+ int result;
+ int i, j;
+
+ short int ids[] = { -1 };
+
+ c8sectpfe = kzalloc(sizeof(struct c8sectpfe), GFP_KERNEL);
+ if (!c8sectpfe)
+ goto err1;
+
+ mutex_init(&c8sectpfe->lock);
+
+ c8sectpfe->device = fei->dev;
+
+ result = dvb_register_adapter(&c8sectpfe->adapter, "STi c8sectpfe",
+ THIS_MODULE, fei->dev, ids);
+ if (result < 0) {
+ dev_err(fei->dev, "dvb_register_adapter failed (errno = %d)\n",
+ result);
+ goto err2;
+ }
+
+ c8sectpfe->adapter.priv = fei;
+
+ for (i = 0; i < fei->tsin_count; i++) {
+
+ c8sectpfe->demux[i].tsin_index = i;
+ c8sectpfe->demux[i].tsm_channel = fei->channel_data[i]->tsin_id;
+ c8sectpfe->demux[i].c8sectpfei = fei;
+
+ result = register_dvb(&c8sectpfe->demux[i], &c8sectpfe->adapter,
+ start_feed, stop_feed, fei);
+ if (result < 0) {
+ dev_err(fei->dev,
+ "register_dvb feed=%d failed (errno = %d)\n",
+ result, i);
+
+ /* we take a all or nothing approach */
+ for (j = 0; j < i; j++)
+ unregister_dvb(&c8sectpfe->demux[j]);
+ goto err3;
+ }
+ }
+
+ c8sectpfe->num_feeds = fei->tsin_count;
+
+ return c8sectpfe;
+err3:
+ dvb_unregister_adapter(&c8sectpfe->adapter);
+err2:
+ kfree(c8sectpfe);
+err1:
+ return NULL;
+};
+
+static void c8sectpfe_delete(struct c8sectpfe *c8sectpfe)
+{
+ int i;
+
+ if (!c8sectpfe)
+ return;
+
+ for (i = 0; i < c8sectpfe->num_feeds; i++)
+ unregister_dvb(&c8sectpfe->demux[i]);
+
+ dvb_unregister_adapter(&c8sectpfe->adapter);
+
+ kfree(c8sectpfe);
+};
+
+void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe,
+ struct c8sectpfei *fei)
+{
+ int n;
+ struct channel_info *tsin;
+
+ for (n = 0; n < fei->tsin_count; n++) {
+
+ tsin = fei->channel_data[n];
+
+ if (tsin && tsin->frontend) {
+ dvb_unregister_frontend(tsin->frontend);
+ dvb_frontend_detach(tsin->frontend);
+ }
+
+ if (tsin && tsin->i2c_adapter)
+ i2c_put_adapter(tsin->i2c_adapter);
+
+ if (tsin && tsin->i2c_client) {
+ if (tsin->i2c_client->dev.driver->owner)
+ module_put(tsin->i2c_client->dev.driver->owner);
+ i2c_unregister_device(tsin->i2c_client);
+ }
+ }
+
+ c8sectpfe_delete(c8sectpfe);
+};
+
+int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe,
+ struct c8sectpfei *fei,
+ void *start_feed,
+ void *stop_feed)
+{
+ struct channel_info *tsin;
+ struct dvb_frontend *frontend;
+ int n, res;
+
+ *c8sectpfe = c8sectpfe_create(fei, start_feed, stop_feed);
+ if (!*c8sectpfe)
+ return -ENOMEM;
+
+ for (n = 0; n < fei->tsin_count; n++) {
+ tsin = fei->channel_data[n];
+
+ res = c8sectpfe_frontend_attach(&frontend, *c8sectpfe, tsin, n);
+ if (res)
+ goto err;
+
+ res = dvb_register_frontend(&c8sectpfe[0]->adapter, frontend);
+ if (res < 0) {
+ dev_err(fei->dev, "dvb_register_frontend failed (%d)\n",
+ res);
+ goto err;
+ }
+
+ tsin->frontend = frontend;
+ }
+
+ return 0;
+
+err:
+ c8sectpfe_tuner_unregister_frontend(*c8sectpfe, fei);
+ return res;
+}
diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-common.h b/drivers/media/tsin/c8sectpfe/c8sectpfe-common.h
new file mode 100644
index 0000000..03beed5
--- /dev/null
+++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-common.h
@@ -0,0 +1,66 @@
+/*
+ * c8sectpfe-common.h - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author: Peter Griffin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+#ifndef _C8SECTPFE_COMMON_H_
+#define _C8SECTPFE_COMMON_H_
+
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/frontend.h>
+#include <linux/gpio.h>
+#include <linux/version.h>
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+/* Maximum number of channels */
+#define C8SECTPFE_MAXADAPTER (4)
+#define C8SECTPFE_MAXCHANNEL 64
+#define STPTI_MAXCHANNEL 64
+
+#define MAX_INPUTBLOCKS 7
+
+struct c8sectpfe;
+struct stdemux;
+
+struct stdemux {
+ struct dvb_demux dvb_demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+ int tsm_channel;
+ int stream_channel;
+ int tsin_index;
+ int running_feed_count;
+ struct c8sectpfei *c8sectpfei;
+};
+
+struct c8sectpfe {
+ struct stdemux demux[MAX_INPUTBLOCKS];
+ struct mutex lock;
+ struct dvb_adapter adapter;
+ struct device *device;
+ int mapping;
+ int num_feeds;
+};
+
+/* Channel registration */
+int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe,
+ struct c8sectpfei *fei,
+ void *start_feed,
+ void *stop_feed);
+
+void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe,
+ struct c8sectpfei *fei);
+
+#endif
--
1.9.1
This patch adds support for the following 3 NIM cards: -
1) STV0367-NIM (stv0367 demod with Thompson PLL)
2) B2100A (2x stv0367 demods & 2x NXP tda18212 tuners)
3) STV0903-6110NIM (stv0903 demod + 6110 tuner, lnb24)
Signed-off-by: Peter Griffin <[email protected]>
---
drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c | 296 +++++++++++++++++++++++++++
drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h | 20 ++
2 files changed, 316 insertions(+)
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h
diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c
new file mode 100644
index 0000000..5c4ecb4
--- /dev/null
+++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c
@@ -0,0 +1,296 @@
+/*
+ * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author Peter Griffin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ */
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/version.h>
+
+#include <dt-bindings/media/c8sectpfe.h>
+
+#include "c8sectpfe-common.h"
+#include "c8sectpfe-core.h"
+#include "c8sectpfe-dvb.h"
+
+#include "dvb-pll.h"
+#include "lnbh24.h"
+#include "stv0367.h"
+#include "stv0367_priv.h"
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "tda18212.h"
+
+static inline const char *dvb_card_str(unsigned int c)
+{
+ switch (c) {
+ case STV0367_PLL_BOARD_NIMA: return "STV0367_PLL_BOARD_NIMA";
+ case STV0367_PLL_BOARD_NIMB: return "STV0367_PLL_BOARD_NIMB";
+ case STV0367_TDA18212_NIMA_1: return "STV0367_TDA18212_NIMA_1";
+ case STV0367_TDA18212_NIMA_2: return "STV0367_TDA18212_NIMA_2";
+ case STV0367_TDA18212_NIMB_1: return "STV0367_TDA18212_NIMB_1";
+ case STV0367_TDA18212_NIMB_2: return "STV0367_TDA18212_NIMB_2";
+ case STV0903_6110_LNB24_NIMA: return "STV0903_6110_LNB24_NIMA";
+ case STV0903_6110_LNB24_NIMB: return "STV0903_6110_LNB24_NIMB";
+ default: return "unknown dvb frontend card";
+ }
+}
+
+static struct stv090x_config stv090x_config = {
+ .device = STV0903,
+ .demod_mode = STV090x_SINGLE,
+ .clk_mode = STV090x_CLK_EXT,
+ .xtal = 16000000,
+ .address = 0x69,
+
+ .ts1_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
+ .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
+
+ .repeater_level = STV090x_RPTLEVEL_64,
+
+ .tuner_init = NULL,
+ .tuner_set_mode = NULL,
+ .tuner_set_frequency = NULL,
+ .tuner_get_frequency = NULL,
+ .tuner_set_bandwidth = NULL,
+ .tuner_get_bandwidth = NULL,
+ .tuner_set_bbgain = NULL,
+ .tuner_get_bbgain = NULL,
+ .tuner_set_refclk = NULL,
+ .tuner_get_status = NULL,
+};
+
+static struct stv6110x_config stv6110x_config = {
+ .addr = 0x60,
+ .refclk = 16000000,
+};
+
+#define NIMA 0
+#define NIMB 1
+
+static struct stv0367_config stv0367_pll_config[] = {
+ {
+ .demod_address = 0x1c,
+ .xtal = 27000000,
+ .if_khz = 36166,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ }, {
+ .demod_address = 0x1d,
+ .xtal = 27000000,
+ .if_khz = 36166,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ },
+};
+
+static struct stv0367_config stv0367_tda18212_config[] = {
+ {
+ .demod_address = 0x1c,
+ .xtal = 16000000,
+ .if_khz = 4500,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ }, {
+ .demod_address = 0x1d,
+ .xtal = 16000000,
+ .if_khz = 4500,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ }, {
+ .demod_address = 0x1e,
+ .xtal = 16000000,
+ .if_khz = 4500,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ },
+};
+
+static struct tda18212_config tda18212_conf = {
+ .if_dvbt_6 = 4150,
+ .if_dvbt_7 = 4150,
+ .if_dvbt_8 = 4500,
+ .if_dvbc = 5000,
+};
+
+int c8sectpfe_frontend_attach(struct dvb_frontend **fe,
+ struct c8sectpfe *c8sectpfe,
+ struct channel_info *tsin, int chan_num)
+{
+ struct tda18212_config *tda18212;
+ struct stv6110x_devctl *fe2;
+ struct i2c_client *client;
+ struct i2c_board_info tda18212_info = {
+ .type = "tda18212",
+ .addr = 0x60,
+ };
+
+ BUG_ON(!tsin);
+
+ switch (tsin->dvb_card) {
+
+ case STV0367_PLL_BOARD_NIMA:
+ case STV0367_PLL_BOARD_NIMB:
+ if (tsin->dvb_card == STV0367_PLL_BOARD_NIMA)
+ *fe = dvb_attach(stv0367ter_attach,
+ &stv0367_pll_config[NIMA], tsin->i2c_adapter);
+ else
+ *fe = dvb_attach(stv0367ter_attach,
+ &stv0367_pll_config[NIMB], tsin->i2c_adapter);
+
+ if (!*fe) {
+ dev_err(c8sectpfe->device,
+ "%s: stv0367ter_attach failed for NIM card %s\n"
+ , __func__, dvb_card_str(tsin->dvb_card));
+ return -ENODEV;
+ };
+
+ /*
+ * init the demod so that i2c gate_ctrl
+ * to the tuner works correctly
+ */
+ (*fe)->ops.init(*fe);
+
+ if (!dvb_attach(dvb_pll_attach, *fe, 0x60,
+ tsin->i2c_adapter, DVB_PLL_THOMSON_DTT7546X)) {
+
+ dev_err(c8sectpfe->device,
+ "%s: DVB_PLL_THOMSON_DTT7546X attach failed\n\t"
+ "for NIM card %s\n",
+ __func__, dvb_card_str(tsin->dvb_card));
+ return -ENODEV;
+ }
+ break;
+
+ case STV0367_TDA18212_NIMA_1:
+ case STV0367_TDA18212_NIMA_2:
+ case STV0367_TDA18212_NIMB_1:
+ case STV0367_TDA18212_NIMB_2:
+ if (tsin->dvb_card == STV0367_TDA18212_NIMA_1)
+ *fe = dvb_attach(stv0367ter_attach,
+ &stv0367_tda18212_config[0],
+ tsin->i2c_adapter);
+ else if (tsin->dvb_card == STV0367_TDA18212_NIMB_1)
+ *fe = dvb_attach(stv0367ter_attach,
+ &stv0367_tda18212_config[1],
+ tsin->i2c_adapter);
+ else
+ *fe = dvb_attach(stv0367ter_attach,
+ &stv0367_tda18212_config[2],
+ tsin->i2c_adapter);
+
+ if (!*fe) {
+ dev_err(c8sectpfe->device,
+ "%s: stv0367ter_attach failed for NIM card %s\n"
+ , __func__, dvb_card_str(tsin->dvb_card));
+ return -ENODEV;
+ };
+
+ /*
+ * init the demod so that i2c gate_ctrl
+ * to the tuner works correctly
+ */
+ (*fe)->ops.init(*fe);
+
+ /* Allocate the tda18212 structure */
+ tda18212 = devm_kzalloc(c8sectpfe->device,
+ sizeof(struct tda18212_config),
+ GFP_KERNEL);
+ if (!tda18212) {
+ dev_err(c8sectpfe->device,
+ "%s: devm_kzalloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(tda18212, &tda18212_conf,
+ sizeof(struct tda18212_config));
+
+ tda18212->fe = (*fe);
+
+ tda18212_info.platform_data = tda18212;
+
+ /* attach tuner */
+ request_module("tda18212");
+ client = i2c_new_device(tsin->i2c_adapter, &tda18212_info);
+ if (!client || !client->dev.driver) {
+ dvb_frontend_detach(*fe);
+ return -ENODEV;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ dvb_frontend_detach(*fe);
+ return -ENODEV;
+ }
+
+ tsin->i2c_client = client;
+
+ break;
+
+ case STV0903_6110_LNB24_NIMA:
+ *fe = dvb_attach(stv090x_attach, &stv090x_config,
+ tsin->i2c_adapter, STV090x_DEMODULATOR_0);
+ if (!*fe) {
+ dev_err(c8sectpfe->device, "%s: stv090x_attach failed\n"
+ "\tfor NIM card %s\n",
+ __func__, dvb_card_str(tsin->dvb_card));
+ return -ENODEV;
+ }
+
+ fe2 = dvb_attach(stv6110x_attach, *fe,
+ &stv6110x_config, tsin->i2c_adapter);
+ if (!fe2) {
+ dev_err(c8sectpfe->device,
+ "%s: stv6110x_attach failed for NIM card %s\n"
+ , __func__, dvb_card_str(tsin->dvb_card));
+ return -ENODEV;
+ };
+
+ stv090x_config.tuner_init = fe2->tuner_init;
+ stv090x_config.tuner_set_mode = fe2->tuner_set_mode;
+ stv090x_config.tuner_set_frequency = fe2->tuner_set_frequency;
+ stv090x_config.tuner_get_frequency = fe2->tuner_get_frequency;
+ stv090x_config.tuner_set_bandwidth = fe2->tuner_set_bandwidth;
+ stv090x_config.tuner_get_bandwidth = fe2->tuner_get_bandwidth;
+ stv090x_config.tuner_set_bbgain = fe2->tuner_set_bbgain;
+ stv090x_config.tuner_get_bbgain = fe2->tuner_get_bbgain;
+ stv090x_config.tuner_set_refclk = fe2->tuner_set_refclk;
+ stv090x_config.tuner_get_status = fe2->tuner_get_status;
+
+ dvb_attach(lnbh24_attach, *fe, tsin->i2c_adapter, 0, 0, 0x9);
+ break;
+
+ default:
+ dev_err(c8sectpfe->device,
+ "%s: DVB frontend card %s not yet supported\n",
+ __func__, dvb_card_str(tsin->dvb_card));
+ return -ENODEV;
+ }
+
+ (*fe)->id = chan_num;
+
+ dev_info(c8sectpfe->device,
+ "DVB frontend card %s successfully attached",
+ dvb_card_str(tsin->dvb_card));
+ return 0;
+}
diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h b/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h
new file mode 100644
index 0000000..bd366db
--- /dev/null
+++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h
@@ -0,0 +1,20 @@
+/*
+ * c8sectpfe-common.h - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author: Peter Griffin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+#ifndef _C8SECTPFE_DVB_H_
+#define _C8SECTPFE_DVB_H_
+
+int c8sectpfe_frontend_attach(struct dvb_frontend **fe,
+ struct c8sectpfe *c8sectpfe, struct channel_info *tsin,
+ int chan_num);
+
+#endif
--
1.9.1
Some basic debugfs support to dump the IP registers. Further
statistics could easily be added in the future for example for
each enabled tsin channel we could expose number of corrupt packets
received etc.
Signed-off-by: Peter Griffin <[email protected]>
---
drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.c | 271 +++++++++++++++++++++++
drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.h | 26 +++
2 files changed, 297 insertions(+)
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.c
create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.h
diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.c b/drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.c
new file mode 100644
index 0000000..e9ba13d
--- /dev/null
+++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.c
@@ -0,0 +1,271 @@
+/*
+ * c8sectpfe-debugfs.c - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author: Peter Griffin <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "c8sectpfe-debugfs.h"
+
+#define dump_register(nm ...) \
+{ \
+ .name = #nm, \
+ .offset = nm, \
+}
+
+static const struct debugfs_reg32 fei_sys_regs[] = {
+ dump_register(SYS_INPUT_ERR_STATUS),
+ dump_register(SYS_OTHER_ERR_STATUS),
+ dump_register(SYS_INPUT_ERR_MASK),
+ dump_register(SYS_DMA_ROUTE),
+ dump_register(SYS_INPUT_CLKEN),
+ dump_register(IBENABLE_MASK),
+ dump_register(SYS_OTHER_CLKEN),
+ dump_register(SYS_CFG_NUM_IB),
+ dump_register(SYS_CFG_NUM_MIB),
+ dump_register(SYS_CFG_NUM_SWTS),
+ dump_register(SYS_CFG_NUM_TSOUT),
+ dump_register(SYS_CFG_NUM_CCSC),
+ dump_register(SYS_CFG_NUM_RAM),
+ dump_register(SYS_CFG_NUM_TP),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(0)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(0)),
+ dump_register(C8SECTPFE_IB_PID_SET(0)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(0)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(0)),
+ dump_register(C8SECTPFE_IB_BUFF_END(0)),
+ dump_register(C8SECTPFE_IB_READ_PNT(0)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(0)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(0)),
+ dump_register(C8SECTPFE_IB_STAT(0)),
+ dump_register(C8SECTPFE_IB_MASK(0)),
+ dump_register(C8SECTPFE_IB_SYS(0)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(1)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(1)),
+ dump_register(C8SECTPFE_IB_PID_SET(1)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(1)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(1)),
+ dump_register(C8SECTPFE_IB_BUFF_END(1)),
+ dump_register(C8SECTPFE_IB_READ_PNT(1)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(1)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(1)),
+ dump_register(C8SECTPFE_IB_STAT(1)),
+ dump_register(C8SECTPFE_IB_MASK(1)),
+ dump_register(C8SECTPFE_IB_SYS(1)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(2)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(2)),
+ dump_register(C8SECTPFE_IB_PID_SET(2)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(2)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(2)),
+ dump_register(C8SECTPFE_IB_BUFF_END(2)),
+ dump_register(C8SECTPFE_IB_READ_PNT(2)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(2)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(2)),
+ dump_register(C8SECTPFE_IB_STAT(2)),
+ dump_register(C8SECTPFE_IB_MASK(2)),
+ dump_register(C8SECTPFE_IB_SYS(2)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(3)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(3)),
+ dump_register(C8SECTPFE_IB_PID_SET(3)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(3)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(3)),
+ dump_register(C8SECTPFE_IB_BUFF_END(3)),
+ dump_register(C8SECTPFE_IB_READ_PNT(3)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(3)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(3)),
+ dump_register(C8SECTPFE_IB_STAT(3)),
+ dump_register(C8SECTPFE_IB_MASK(3)),
+ dump_register(C8SECTPFE_IB_SYS(3)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(4)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(4)),
+ dump_register(C8SECTPFE_IB_PID_SET(4)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(4)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(4)),
+ dump_register(C8SECTPFE_IB_BUFF_END(4)),
+ dump_register(C8SECTPFE_IB_READ_PNT(4)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(4)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(4)),
+ dump_register(C8SECTPFE_IB_STAT(4)),
+ dump_register(C8SECTPFE_IB_MASK(4)),
+ dump_register(C8SECTPFE_IB_SYS(4)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(5)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(5)),
+ dump_register(C8SECTPFE_IB_PID_SET(5)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(5)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(5)),
+ dump_register(C8SECTPFE_IB_BUFF_END(5)),
+ dump_register(C8SECTPFE_IB_READ_PNT(5)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(5)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(5)),
+ dump_register(C8SECTPFE_IB_STAT(5)),
+ dump_register(C8SECTPFE_IB_MASK(5)),
+ dump_register(C8SECTPFE_IB_SYS(5)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(6)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(6)),
+ dump_register(C8SECTPFE_IB_PID_SET(6)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(6)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(6)),
+ dump_register(C8SECTPFE_IB_BUFF_END(6)),
+ dump_register(C8SECTPFE_IB_READ_PNT(6)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(6)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(6)),
+ dump_register(C8SECTPFE_IB_STAT(6)),
+ dump_register(C8SECTPFE_IB_MASK(6)),
+ dump_register(C8SECTPFE_IB_SYS(6)),
+
+ dump_register(DMA_CPU_ID),
+ dump_register(DMA_CPU_VCR),
+ dump_register(DMA_CPU_RUN),
+ dump_register(DMA_CPU_PC),
+
+ dump_register(DMA_PER_TPn_DREQ_MASK),
+ dump_register(DMA_PER_TPn_DACK_SET),
+ dump_register(DMA_PER_TPn_DREQ),
+ dump_register(DMA_PER_TPn_DACK),
+ dump_register(DMA_PER_DREQ_MODE),
+ dump_register(DMA_PER_STBUS_SYNC),
+ dump_register(DMA_PER_STBUS_ACCESS),
+ dump_register(DMA_PER_STBUS_ADDRESS),
+ dump_register(DMA_PER_IDLE_INT),
+ dump_register(DMA_PER_PRIORITY),
+ dump_register(DMA_PER_MAX_OPCODE),
+ dump_register(DMA_PER_MAX_CHUNK),
+ dump_register(DMA_PER_PAGE_SIZE),
+ dump_register(DMA_PER_MBOX_STATUS),
+ dump_register(DMA_PER_MBOX_SET),
+ dump_register(DMA_PER_MBOX_CLEAR),
+ dump_register(DMA_PER_MBOX_MASK),
+ dump_register(DMA_PER_INJECT_PKT_SRC),
+ dump_register(DMA_PER_INJECT_PKT_DEST),
+ dump_register(DMA_PER_INJECT_PKT_ADDR),
+ dump_register(DMA_PER_INJECT_PKT),
+ dump_register(DMA_PER_PAT_PTR_INIT),
+ dump_register(DMA_PER_PAT_PTR),
+ dump_register(DMA_PER_SLEEP_MASK),
+ dump_register(DMA_PER_SLEEP_COUNTER),
+
+ dump_register(DMA_FIRMWARE_VERSION),
+ dump_register(DMA_PTRREC_BASE),
+ dump_register(DMA_PTRREC_INPUT_OFFSET),
+ dump_register(DMA_ERRREC_BASE),
+
+ dump_register(DMA_ERROR_RECORD(0)),
+ dump_register(DMA_ERROR_RECORD(1)),
+ dump_register(DMA_ERROR_RECORD(2)),
+ dump_register(DMA_ERROR_RECORD(3)),
+ dump_register(DMA_ERROR_RECORD(4)),
+ dump_register(DMA_ERROR_RECORD(5)),
+ dump_register(DMA_ERROR_RECORD(6)),
+ dump_register(DMA_ERROR_RECORD(7)),
+ dump_register(DMA_ERROR_RECORD(8)),
+ dump_register(DMA_ERROR_RECORD(9)),
+ dump_register(DMA_ERROR_RECORD(10)),
+ dump_register(DMA_ERROR_RECORD(11)),
+ dump_register(DMA_ERROR_RECORD(12)),
+ dump_register(DMA_ERROR_RECORD(13)),
+ dump_register(DMA_ERROR_RECORD(14)),
+ dump_register(DMA_ERROR_RECORD(15)),
+ dump_register(DMA_ERROR_RECORD(16)),
+ dump_register(DMA_ERROR_RECORD(17)),
+ dump_register(DMA_ERROR_RECORD(18)),
+ dump_register(DMA_ERROR_RECORD(19)),
+ dump_register(DMA_ERROR_RECORD(20)),
+ dump_register(DMA_ERROR_RECORD(21)),
+ dump_register(DMA_ERROR_RECORD(22)),
+
+ dump_register(DMA_IDLE_REQ),
+ dump_register(DMA_FIRMWARE_CONFIG),
+
+ dump_register(PIDF_BASE(0)),
+ dump_register(PIDF_BASE(1)),
+ dump_register(PIDF_BASE(2)),
+ dump_register(PIDF_BASE(3)),
+ dump_register(PIDF_BASE(4)),
+ dump_register(PIDF_BASE(5)),
+ dump_register(PIDF_BASE(6)),
+ dump_register(PIDF_BASE(7)),
+ dump_register(PIDF_BASE(8)),
+ dump_register(PIDF_BASE(9)),
+ dump_register(PIDF_BASE(10)),
+ dump_register(PIDF_BASE(11)),
+ dump_register(PIDF_BASE(12)),
+ dump_register(PIDF_BASE(13)),
+ dump_register(PIDF_BASE(14)),
+ dump_register(PIDF_BASE(15)),
+ dump_register(PIDF_BASE(16)),
+ dump_register(PIDF_BASE(17)),
+ dump_register(PIDF_BASE(18)),
+ dump_register(PIDF_BASE(19)),
+ dump_register(PIDF_BASE(20)),
+ dump_register(PIDF_BASE(21)),
+ dump_register(PIDF_BASE(22)),
+ dump_register(PIDF_LEAK_ENABLE),
+ dump_register(PIDF_LEAK_STATUS),
+ dump_register(PIDF_LEAK_COUNT_RESET),
+ dump_register(PIDF_LEAK_COUNTER),
+};
+
+void c8sectpfe_debugfs_init(struct c8sectpfei *fei)
+{
+ struct dentry *root;
+ struct dentry *file;
+
+ root = debugfs_create_dir("c8sectpfe", NULL);
+ if (!root)
+ goto err;
+
+ fei->root = root;
+
+ fei->regset = devm_kzalloc(fei->dev, sizeof(*fei->regset), GFP_KERNEL);
+ if (!fei->regset)
+ goto err;
+
+ fei->regset->regs = fei_sys_regs;
+ fei->regset->nregs = ARRAY_SIZE(fei_sys_regs);
+ fei->regset->base = fei->io;
+
+ file = debugfs_create_regset32("registers", S_IRUGO, root,
+ fei->regset);
+ if (!file) {
+ dev_err(fei->dev,
+ "%s not able to create 'registers' debugfs\n"
+ , __func__);
+ goto err;
+ }
+
+ return;
+
+err:
+ debugfs_remove_recursive(root);
+}
+
+void c8sectpfe_debugfs_exit(struct c8sectpfei *fei)
+{
+ debugfs_remove_recursive(fei->root);
+ fei->root = NULL;
+}
diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.h
new file mode 100644
index 0000000..8af1ac1
--- /dev/null
+++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-debugfs.h
@@ -0,0 +1,26 @@
+/**
+ * c8sectpfe-debugfs.h - C8SECTPFE STi DVB driver debugfs header
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Authors: Peter Griffin <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __C8SECTPFE_DEBUG_H
+#define __C8SECTPFE_DEBUG_H
+
+#include "c8sectpfe-core.h"
+
+void c8sectpfe_debugfs_init(struct c8sectpfei *);
+void c8sectpfe_debugfs_exit(struct c8sectpfei *);
+
+#endif /* __C8SECTPFE_DEBUG_H */
--
1.9.1
This patch adds the Kconfig and Makefile for the c8sectpfe driver
so it will be built. It also selects additional demodulator and tuners
which are required by the supported NIM cards.
Signed-off-by: Peter Griffin <[email protected]>
---
drivers/media/Kconfig | 1 +
drivers/media/Makefile | 1 +
drivers/media/tsin/c8sectpfe/Kconfig | 26 ++++++++++++++++++++++++++
drivers/media/tsin/c8sectpfe/Makefile | 11 +++++++++++
4 files changed, 39 insertions(+)
create mode 100644 drivers/media/tsin/c8sectpfe/Kconfig
create mode 100644 drivers/media/tsin/c8sectpfe/Makefile
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 1570992..82bc1dc 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -170,6 +170,7 @@ source "drivers/media/pci/Kconfig"
source "drivers/media/platform/Kconfig"
source "drivers/media/mmc/Kconfig"
source "drivers/media/radio/Kconfig"
+source "drivers/media/tsin/c8sectpfe/Kconfig"
comment "Supported FireWire (IEEE 1394) Adapters"
depends on DVB_CORE && FIREWIRE
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index e608bbc..0a567b8 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -29,5 +29,6 @@ obj-y += rc/
#
obj-y += common/ platform/ pci/ usb/ mmc/ firewire/
+obj-$(CONFIG_DVB_C8SECTPFE) += tsin/c8sectpfe/
obj-$(CONFIG_VIDEO_DEV) += radio/
diff --git a/drivers/media/tsin/c8sectpfe/Kconfig b/drivers/media/tsin/c8sectpfe/Kconfig
new file mode 100644
index 0000000..8d99a87
--- /dev/null
+++ b/drivers/media/tsin/c8sectpfe/Kconfig
@@ -0,0 +1,26 @@
+config DVB_C8SECTPFE
+ tristate "STMicroelectronics C8SECTPFE DVB support"
+ depends on DVB_CORE && I2C && (ARCH_STI || ARCH_MULTIPLATFORM)
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
+
+ ---help---
+ This adds support for DVB front-end cards connected
+ to TS inputs of STiH407/410 SoC.
+
+ The driver currently supports C8SECTPFE's TS input block,
+ memdma engine, and HW PID filtering.
+
+ Supported DVB front-end cards are:
+ - STMicroelectronics DVB-T B2100A (STV0367 + TDA18212)
+ - STMicroelectronics DVB-T STV0367 PLL board (STV0367 + DTT7546X)
+ - STMicroelectronics DVB-S/S2 STV0903 + STV6110 + LNBP24 board
+
+ To compile this driver as a module, choose M here: the
+ module will be called c8sectpfe.
diff --git a/drivers/media/tsin/c8sectpfe/Makefile b/drivers/media/tsin/c8sectpfe/Makefile
new file mode 100644
index 0000000..777f06d
--- /dev/null
+++ b/drivers/media/tsin/c8sectpfe/Makefile
@@ -0,0 +1,11 @@
+c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o
+
+obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o
+
+ifneq ($(CONFIG_DVB_C8SECTPFE),)
+ c8sectpfe-y += c8sectpfe-debugfs.o
+endif
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/common
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ -Idrivers/media/tuners/
--
1.9.1
Add the new c8sectpfe demux driver to the STi section of the
MAINTAINERS file.
Signed-off-by: Peter Griffin <[email protected]>
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d8afd29..49c8963 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1481,6 +1481,7 @@ F: arch/arm/boot/dts/sti*
F: drivers/clocksource/arm_global_timer.c
F: drivers/i2c/busses/i2c-st.c
F: drivers/media/rc/st_rc.c
+F: drivers/media/tsin/c8sectpfe/
F: drivers/mmc/host/sdhci-st.c
F: drivers/phy/phy-miphy28lp.c
F: drivers/phy/phy-miphy365x.c
--
1.9.1
On Wed, 2015-06-24 at 16:11 +0100, Peter Griffin wrote:
> This is used in conjunction with the STV0367 demodulator on
> the STV0367-NIM-V1.0 NIM card which can be used with the STi
> STB SoC's.
Barely associated to this specific patch, but for
dvb-pll.c, another thing that seems possible is to
convert the struct dvb_pll_desc uses to const and
change the "entries" fixed array size from 12 to []
It'd save a couple KB overall and remove ~5KB of data.
$ size drivers/media/dvb-frontends/dvb-pll.o*
text data bss dec hex filename
8520 1552 2120 12192 2fa0 drivers/media/dvb-frontends/dvb-pll.o.new
5624 6363 2120 14107 371b drivers/media/dvb-frontends/dvb-pll.o.old
---
drivers/media/dvb-frontends/dvb-pll.c | 50 +++++++++++++++++------------------
1 file changed, 25 insertions(+), 25 deletions(-)
diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c
index 6d8fe88..53089e1 100644
--- a/drivers/media/dvb-frontends/dvb-pll.c
+++ b/drivers/media/dvb-frontends/dvb-pll.c
@@ -34,7 +34,7 @@ struct dvb_pll_priv {
struct i2c_adapter *i2c;
/* the PLL descriptor */
- struct dvb_pll_desc *pll_desc;
+ const struct dvb_pll_desc *pll_desc;
/* cached frequency/bandwidth */
u32 frequency;
@@ -57,7 +57,7 @@ MODULE_PARM_DESC(id, "force pll id to use (DEBUG ONLY)");
/* ----------------------------------------------------------- */
struct dvb_pll_desc {
- char *name;
+ const char *name;
u32 min;
u32 max;
u32 iffreq;
@@ -71,13 +71,13 @@ struct dvb_pll_desc {
u32 stepsize;
u8 config;
u8 cb;
- } entries[12];
+ } entries[];
};
/* ----------------------------------------------------------- */
/* descriptions */
-static struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
+static const struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
.name = "Thomson dtt7579",
.min = 177000000,
.max = 858000000,
@@ -99,7 +99,7 @@ static void thomson_dtt759x_bw(struct dvb_frontend *fe, u8 *buf)
buf[3] |= 0x10;
}
-static struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
+static const struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
.name = "Thomson dtt759x",
.min = 177000000,
.max = 896000000,
@@ -123,7 +123,7 @@ static void thomson_dtt7520x_bw(struct dvb_frontend *fe, u8 *buf)
buf[3] ^= 0x10;
}
-static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
+static const struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
.name = "Thomson dtt7520x",
.min = 185000000,
.max = 900000000,
@@ -141,7 +141,7 @@ static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
},
};
-static struct dvb_pll_desc dvb_pll_lg_z201 = {
+static const struct dvb_pll_desc dvb_pll_lg_z201 = {
.name = "LG z201",
.min = 174000000,
.max = 862000000,
@@ -157,7 +157,7 @@ static struct dvb_pll_desc dvb_pll_lg_z201 = {
},
};
-static struct dvb_pll_desc dvb_pll_unknown_1 = {
+static const struct dvb_pll_desc dvb_pll_unknown_1 = {
.name = "unknown 1", /* used by dntv live dvb-t */
.min = 174000000,
.max = 862000000,
@@ -179,7 +179,7 @@ static struct dvb_pll_desc dvb_pll_unknown_1 = {
/* Infineon TUA6010XS
* used in Thomson Cable Tuner
*/
-static struct dvb_pll_desc dvb_pll_tua6010xs = {
+static const struct dvb_pll_desc dvb_pll_tua6010xs = {
.name = "Infineon TUA6010XS",
.min = 44250000,
.max = 858000000,
@@ -193,7 +193,7 @@ static struct dvb_pll_desc dvb_pll_tua6010xs = {
};
/* Panasonic env57h1xd5 (some Philips PLL ?) */
-static struct dvb_pll_desc dvb_pll_env57h1xd5 = {
+static const struct dvb_pll_desc dvb_pll_env57h1xd5 = {
.name = "Panasonic ENV57H1XD5",
.min = 44250000,
.max = 858000000,
@@ -217,7 +217,7 @@ static void tda665x_bw(struct dvb_frontend *fe, u8 *buf)
buf[3] |= 0x08;
}
-static struct dvb_pll_desc dvb_pll_tda665x = {
+static const struct dvb_pll_desc dvb_pll_tda665x = {
.name = "Philips TDA6650/TDA6651",
.min = 44250000,
.max = 858000000,
@@ -251,7 +251,7 @@ static void tua6034_bw(struct dvb_frontend *fe, u8 *buf)
buf[3] |= 0x08;
}
-static struct dvb_pll_desc dvb_pll_tua6034 = {
+static const struct dvb_pll_desc dvb_pll_tua6034 = {
.name = "Infineon TUA6034",
.min = 44250000,
.max = 858000000,
@@ -275,7 +275,7 @@ static void tded4_bw(struct dvb_frontend *fe, u8 *buf)
buf[3] |= 0x04;
}
-static struct dvb_pll_desc dvb_pll_tded4 = {
+static const struct dvb_pll_desc dvb_pll_tded4 = {
.name = "ALPS TDED4",
.min = 47000000,
.max = 863000000,
@@ -293,7 +293,7 @@ static struct dvb_pll_desc dvb_pll_tded4 = {
/* ALPS TDHU2
* used in AverTVHD MCE A180
*/
-static struct dvb_pll_desc dvb_pll_tdhu2 = {
+static const struct dvb_pll_desc dvb_pll_tdhu2 = {
.name = "ALPS TDHU2",
.min = 54000000,
.max = 864000000,
@@ -310,7 +310,7 @@ static struct dvb_pll_desc dvb_pll_tdhu2 = {
/* Samsung TBMV30111IN / TBMV30712IN1
* used in Air2PC ATSC - 2nd generation (nxt2002)
*/
-static struct dvb_pll_desc dvb_pll_samsung_tbmv = {
+static const struct dvb_pll_desc dvb_pll_samsung_tbmv = {
.name = "Samsung TBMV30111IN / TBMV30712IN1",
.min = 54000000,
.max = 860000000,
@@ -329,7 +329,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbmv = {
/*
* Philips SD1878 Tuner.
*/
-static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
+static const struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
.name = "Philips SD1878",
.min = 950000,
.max = 2150000,
@@ -395,7 +395,7 @@ static void opera1_bw(struct dvb_frontend *fe, u8 *buf)
return;
}
-static struct dvb_pll_desc dvb_pll_opera1 = {
+static const struct dvb_pll_desc dvb_pll_opera1 = {
.name = "Opera Tuner",
.min = 900000,
.max = 2250000,
@@ -442,7 +442,7 @@ static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf)
}
/* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */
-static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
+static const struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
.name = "Samsung DTOS403IH102A",
.min = 44250000,
.max = 858000000,
@@ -462,7 +462,7 @@ static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
};
/* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */
-static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
+static const struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
.name = "Samsung TDTC9251DH0",
.min = 48000000,
.max = 863000000,
@@ -476,7 +476,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
};
/* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */
-static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
+static const struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
.name = "Samsung TBDU18132",
.min = 950000,
.max = 2150000, /* guesses */
@@ -497,7 +497,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
};
/* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */
-static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
+static const struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
.name = "Samsung TBMU24112",
.min = 950000,
.max = 2150000, /* guesses */
@@ -518,7 +518,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
* 153 - 430 0 * 0 0 0 0 1 0 0x02
* 430 - 822 0 * 0 0 1 0 0 0 0x08
* 822 - 862 1 * 0 0 1 0 0 0 0x88 */
-static struct dvb_pll_desc dvb_pll_alps_tdee4 = {
+static const struct dvb_pll_desc dvb_pll_alps_tdee4 = {
.name = "ALPS TDEE4",
.min = 47000000,
.max = 862000000,
@@ -534,7 +534,7 @@ static struct dvb_pll_desc dvb_pll_alps_tdee4 = {
/* ----------------------------------------------------------- */
-static struct dvb_pll_desc *pll_list[] = {
+static const struct dvb_pll_desc *pll_list[] = {
[DVB_PLL_UNDEFINED] = NULL,
[DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579,
[DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x,
@@ -564,7 +564,7 @@ static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf,
const u32 frequency)
{
struct dvb_pll_priv *priv = fe->tuner_priv;
- struct dvb_pll_desc *desc = priv->pll_desc;
+ const struct dvb_pll_desc *desc = priv->pll_desc;
u32 div;
int i;
@@ -758,7 +758,7 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
.buf = b1, .len = 1 };
struct dvb_pll_priv *priv = NULL;
int ret;
- struct dvb_pll_desc *desc;
+ const struct dvb_pll_desc *desc;
if ((id[dvb_pll_devcount] > DVB_PLL_UNDEFINED) &&
(id[dvb_pll_devcount] < ARRAY_SIZE(pll_list)))
On Wed, 2015-06-24 at 16:11 +0100, Peter Griffin wrote:
> --- /dev/null
> +++ b/drivers/media/tsin/c8sectpfe/Makefile
> +c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o
> +
> +obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o
> +
> +ifneq ($(CONFIG_DVB_C8SECTPFE),)
> + c8sectpfe-y += c8sectpfe-debugfs.o
> +endif
Isn't the above equivalent to
c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o c8sectpfe-debugfs.o
obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o
Or am I missing something subtle here?
Paul Bolle
Hi Paul,
Thanks for reviewing.
On Thu, 25 Jun 2015, Paul Bolle wrote:
> On Wed, 2015-06-24 at 16:11 +0100, Peter Griffin wrote:
> > --- /dev/null
> > +++ b/drivers/media/tsin/c8sectpfe/Makefile
>
> > +c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o
> > +
> > +obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o
> > +
> > +ifneq ($(CONFIG_DVB_C8SECTPFE),)
> > + c8sectpfe-y += c8sectpfe-debugfs.o
> > +endif
>
> Isn't the above equivalent to
> c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o c8sectpfe-debugfs.o
>
> obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o
>
> Or am I missing something subtle here?
No I think I just messed up. Will fix in v2.
I suspect what happened was I was starting to add a CONFIG_DVB_C8SECTPFE_DEBUGFS
Kconfig option, and then forgot ;)
In v2 I have added a "select DEBUG_FS" to Kconfig for the driver, and put it all on one
line. Also at the same time fixing some other Kconfig dependencies I noticed so
it now has 'select LIBELF_32' and 'select FW_LOADER'.
regards,
Peter.
Em Wed, 24 Jun 2015 16:11:00 +0100
Peter Griffin <[email protected]> escreveu:
> This is used in conjunction with the STV0367 demodulator on
> the STV0367-NIM-V1.0 NIM card which can be used with the STi
> STB SoC's.
>
> This tuner has a fifth register, so some changes have been made
> to accommodate this.
>
> Signed-off-by: Peter Griffin <[email protected]>
> ---
> drivers/media/dvb-frontends/dvb-pll.c | 74 +++++++++++++++++++++++++++++------
> drivers/media/dvb-frontends/dvb-pll.h | 1 +
> 2 files changed, 64 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c
> index 6d8fe88..f7381c7 100644
> --- a/drivers/media/dvb-frontends/dvb-pll.c
> +++ b/drivers/media/dvb-frontends/dvb-pll.c
> @@ -141,6 +141,35 @@ static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
> },
> };
>
> +static void thomson_dtt7546x_bw(struct dvb_frontend *fe, u8 *buf)
> +{
> + /* set CB2 reg - set ATC, XTO */
> + buf[4] = 0xc3;
> +}
> +
> +static struct dvb_pll_desc dvb_pll_thomson_dtt7546x = {
> + .name = "Thomson dtt7546x",
> + .min = 44250000,
> + .max = 863250000,
> + .set = thomson_dtt7546x_bw,
> + .iffreq= 36166667,
Whitespace is missing. Please check the patchs with scripts/checkpatch.pl.
> + .count = 12,
> + .entries = {
> + { 121000000, 166667, 0x88, 0x01 },
> + { 141000000, 166667, 0x88, 0x41 },
> + { 166000000, 166667, 0x88, 0x81 },
> + { 182000000, 166667, 0x88, 0xc1 },
> + { 286000000, 166667, 0x88, 0x02 },
> + { 386000000, 166667, 0x88, 0x42 },
> + { 446000000, 166667, 0x88, 0x82 },
> + { 466000000, 166667, 0x88, 0xc2 },
> + { 506000000, 166667, 0x88, 0x08 },
> + { 761000000, 166667, 0x88, 0x48 },
> + { 846000000, 166667, 0x88, 0x88 },
> + { 905000000, 166667, 0x88, 0xc8 },
> + },
> +};
> +
> static struct dvb_pll_desc dvb_pll_lg_z201 = {
> .name = "LG z201",
> .min = 174000000,
> @@ -537,6 +566,7 @@ static struct dvb_pll_desc dvb_pll_alps_tdee4 = {
> static struct dvb_pll_desc *pll_list[] = {
> [DVB_PLL_UNDEFINED] = NULL,
> [DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579,
> + [DVB_PLL_THOMSON_DTT7546X] = &dvb_pll_thomson_dtt7546x,
> [DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x,
> [DVB_PLL_THOMSON_DTT7520X] = &dvb_pll_thomson_dtt7520x,
> [DVB_PLL_LG_Z201] = &dvb_pll_lg_z201,
> @@ -561,7 +591,7 @@ static struct dvb_pll_desc *pll_list[] = {
> /* code */
>
> static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf,
> - const u32 frequency)
> + const u32 frequency, const u32 len)
> {
> struct dvb_pll_priv *priv = fe->tuner_priv;
> struct dvb_pll_desc *desc = priv->pll_desc;
> @@ -593,11 +623,15 @@ static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf,
> if (desc->set)
> desc->set(fe, buf);
>
> - if (debug)
> - printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
> - desc->name, div, buf[0], buf[1], buf[2], buf[3]);
> + if (debug) {
> + printk(KERN_DEBUG "pll: %s: div=%d | buf=", desc->name, div);
> + for (i = 0; i < len; i++)
> + printk(KERN_DEBUG "0x%02x,", buf[i]);
>
> - // calculate the frequency we set it to
> + printk(KERN_DEBUG "\n");
> + }
Please use, instead, the Documentation/printk-formats.txt macros to
print an hex buffer:
"Raw buffer as a hex string:
%*ph 00 01 02 ... 3f
%*phC 00:01:02: ... :3f
%*phD 00-01-02- ... -3f
%*phN 000102 ... 3f"
> +
> + /* calculate the frequency we set it to */
> return (div * desc->entries[i].stepsize) - desc->iffreq;
> }
>
> @@ -634,21 +668,39 @@ static int dvb_pll_sleep(struct dvb_frontend *fe)
> return -EINVAL;
> }
>
> +static int dvb_pll_get_num_regs(struct dvb_pll_priv *priv)
> +{
> + int num_regs = 4;
> +
> + if (strncmp(priv->pll_desc->name, "Thomson dtt7546x", 16) == 0)
> + num_regs = 5;
> +
> + return num_regs;
> +}
> +
> static int dvb_pll_set_params(struct dvb_frontend *fe)
> {
> struct dtv_frontend_properties *c = &fe->dtv_property_cache;
> struct dvb_pll_priv *priv = fe->tuner_priv;
> - u8 buf[4];
> - struct i2c_msg msg =
> - { .addr = priv->pll_i2c_address, .flags = 0,
> - .buf = buf, .len = sizeof(buf) };
> + struct i2c_msg msg;
> + u8 *bufp;
> int result;
> u32 frequency = 0;
>
> + bufp = kzalloc(dvb_pll_get_num_regs(priv), GFP_KERNEL);
> +
> + if (!bufp)
> + return -ENOMEM;
> +
> + msg.addr = priv->pll_i2c_address;
> + msg.flags = 0;
> + msg.buf = bufp;
> + msg.len = dvb_pll_get_num_regs(priv);
> +
> if (priv->i2c == NULL)
> return -EINVAL;
>
> - result = dvb_pll_configure(fe, buf, c->frequency);
> + result = dvb_pll_configure(fe, bufp, c->frequency, msg.len);
> if (result < 0)
> return result;
> else
> @@ -677,7 +729,7 @@ static int dvb_pll_calc_regs(struct dvb_frontend *fe,
> if (buf_len < 5)
> return -EINVAL;
>
> - result = dvb_pll_configure(fe, buf + 1, c->frequency);
> + result = dvb_pll_configure(fe, buf + 1, c->frequency, buf_len - 1);
> if (result < 0)
> return result;
> else
> diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h
> index bf9602a..f523f42 100644
> --- a/drivers/media/dvb-frontends/dvb-pll.h
> +++ b/drivers/media/dvb-frontends/dvb-pll.h
> @@ -28,6 +28,7 @@
> #define DVB_PLL_SAMSUNG_TBMU24112 17
> #define DVB_PLL_TDEE4 18
> #define DVB_PLL_THOMSON_DTT7520X 19
> +#define DVB_PLL_THOMSON_DTT7546X 20
>
> /**
> * Attach a dvb-pll to the supplied frontend structure.
Em Wed, 24 Jun 2015 16:11:05 +0100
Peter Griffin <[email protected]> escreveu:
> This patch adds support for the c8sectpfe input HW found on
> STiH407/410 SoC's.
>
> It currently supports the TS input block, memdma engine
> and hw PID filtering blocks of the C8SECTPFE subsystem.
>
> The driver creates one LinuxDVB adapter, and a
> demux/dvr/frontend set of devices for each tsin channel
> which is specificed in the DT. It has been tested with
> multiple tsin channels tuned, locked, and grabbing TS
> simultaneously.
>
> Signed-off-by: Peter Griffin <[email protected]>
> ---
> drivers/media/tsin/c8sectpfe/c8sectpfe-core.c | 1105 +++++++++++++++++++++++++
> drivers/media/tsin/c8sectpfe/c8sectpfe-core.h | 288 +++++++
> 2 files changed, 1393 insertions(+)
> create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-core.c
> create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-core.h
>
> diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-core.c b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.c
> new file mode 100644
> index 0000000..fbbe323
> --- /dev/null
> +++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.c
> @@ -0,0 +1,1105 @@
> +/*
> + * c8sectpfe-core.c - C8SECTPFE STi DVB driver
> + *
> + * Copyright (c) STMicroelectronics 2015
> + *
> + * Author:Peter Bennett <[email protected]>
> + * Peter Griffin <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dvb/dmx.h>
> +#include <linux/dvb/frontend.h>
> +#include <linux/errno.h>
> +#include <linux/firmware.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/usb.h>
> +#include <linux/slab.h>
> +#include <linux/time.h>
> +#include <linux/version.h>
> +#include <linux/wait.h>
> +
> +#include "c8sectpfe-core.h"
> +#include "c8sectpfe-common.h"
> +#include "c8sectpfe-debugfs.h"
> +#include "dmxdev.h"
> +#include "dvb_demux.h"
> +#include "dvb_frontend.h"
> +#include "dvb_net.h"
> +
> +#define FIRMWARE_MEMDMA "pti_memdma_h407.elf"
> +MODULE_FIRMWARE(FIRMWARE_MEMDMA);
> +
> +#define PID_TABLE_SIZE 1024
> +#define POLL_20_HZ_DIV 20 /* poll at 20 Hz */
> +
> +static int load_slim_core_fw(struct c8sectpfei *fei);
> +
> +#define TS_PKT_SIZE 188
> +#define HEADER_SIZE (4)
> +#define PACKET_SIZE (TS_PKT_SIZE+HEADER_SIZE)
> +
> +#define FEI_ALIGNMENT (32)
> +/* hw requires minimum of 8*PACKET_SIZE and padded to 8byte boundary */
> +#define FEI_BUFFER_SIZE (8*PACKET_SIZE*340)
> +
> +#define FIFO_LEN 1024
> +
> +static void c8sectpfe_timer_interrupt(unsigned long ac8sectpfei)
> +{
> + struct c8sectpfei *fei = (struct c8sectpfei *)ac8sectpfei;
> + struct channel_info *channel;
> + int chan_num;
> +
> + /* iterate through input block channels */
> + for (chan_num = 0; chan_num < fei->tsin_count; chan_num++) {
> + channel = fei->channel_data[chan_num];
> +
> + /* is this descriptor initialised and TP enabled */
> + if (channel->irec && readl((void __iomem *)
> + &channel->irec->tp_enable))
> + tasklet_schedule(&channel->tsklet);
> + }
> +
> + fei->timer.expires = jiffies + (HZ / POLL_20_HZ_DIV);
Please use the macros for jiffies conversions. In this case, I guess
you want to use ms_to_jiffies(), right?
> + add_timer(&fei->timer);
> +}
> +
> +static void channel_swdemux_tsklet(unsigned long data)
> +{
> + struct channel_info *channel = (struct channel_info *)data;
> + struct c8sectpfei *fei = channel->fei;
> + struct tpentry *ptrblk;
> + unsigned long wp, rp;
> + int pos, num_packets, n, size;
> + u8 *buf;
> +
> + BUG_ON(!channel);
> + BUG_ON(!channel->irec);
Please avoid using BUG_ON() except when the machine will be on some
unstable state. In this case, I guess you could just do:
if (unlikely(!channel || !channel->irec)
return;
> +
> + ptrblk = &channel->irec->ptr_data[0];
> +
> + wp = readl((void __iomem *)&ptrblk->dma_bus_wp);
> + rp = readl((void __iomem *)&ptrblk->dma_bus_rp);
Why do you need those typecasts? We try to avoid typecasts in the Kernel,
doing it only where really needed. Same for other usages. You should
probably declare those DMA buffers as __iomem *, and avoid the casts.
> +
> + pos = rp - channel->back_buffer_busaddr;
> +
> + /* has it wrapped */
> + if (wp < rp)
> + wp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE;
> +
> + size = wp - rp;
> + num_packets = size / PACKET_SIZE;
> +
> + /* manage cache so data is visible to CPU */
> + dma_sync_single_for_cpu(fei->dev,
> + rp,
> + size,
> + DMA_FROM_DEVICE);
> +
> + buf = (u8 *) channel->back_buffer_aligned;
> +
> + dev_dbg(fei->dev,
> + "chan=%d channel=%p num_packets = %d, buf = %p, pos = 0x%x\n\t"
> + "rp=0x%lx, wp=0x%lx\n",
> + channel->tsin_id, channel, num_packets, buf, pos, rp, wp);
> +
> + for (n = 0; n < num_packets; n++) {
> + dvb_dmx_swfilter_packets(
> + &fei->c8sectpfe[0]->
> + demux[channel->demux_mapping].dvb_demux,
> + &buf[pos], 1);
> +
> + pos += PACKET_SIZE;
> + }
Hmm... why not call it, instead, without the loop, e. g.:
dvb_dmx_swfilter_packets(
&fei->c8sectpfe[0]->
demux[channel->demux_mapping].dvb_demux,
&buf[pos], num_packets);
> +
> + /* advance the read pointer */
> + if (wp == (channel->back_buffer_busaddr + FEI_BUFFER_SIZE))
> + writel(channel->back_buffer_busaddr,
> + (void __iomem *)&ptrblk->dma_bus_rp);
> + else
> + writel(wp, (void __iomem *)&ptrblk->dma_bus_rp);
> +}
> +
> +static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed)
> +{
> + struct dvb_demux *demux = dvbdmxfeed->demux;
> + struct stdemux *stdemux = (struct stdemux *)demux->priv;
> + struct c8sectpfei *fei = stdemux->c8sectpfei;
> + struct channel_info *channel;
> + struct tpentry *ptrblk;
> + u32 tmp;
> + unsigned long *bitmap;
> +
> + switch (dvbdmxfeed->type) {
> + case DMX_TYPE_TS:
> + break;
> + case DMX_TYPE_SEC:
> + break;
> + default:
> + dev_err(fei->dev, "%s:%d Error bailing\n"
> + , __func__, __LINE__);
> + return -EINVAL;
> + }
> +
> + if (dvbdmxfeed->type == DMX_TYPE_TS) {
> + switch (dvbdmxfeed->pes_type) {
> + case DMX_PES_VIDEO:
> + case DMX_PES_AUDIO:
> + case DMX_PES_TELETEXT:
> + case DMX_PES_PCR:
> + case DMX_PES_OTHER:
> + break;
> + default:
> + dev_err(fei->dev, "%s:%d Error bailing\n"
> + , __func__, __LINE__);
> + return -EINVAL;
> + }
> + }
> +
> + mutex_lock(&fei->lock);
> +
> + channel = fei->channel_data[stdemux->tsin_index];
> +
> + bitmap = (unsigned long *) channel->pid_buffer_aligned;
> +
> + bitmap_set(bitmap, dvbdmxfeed->pid, 1);
> +
> + /* manage cache so PID bitmap is visible to HW */
> + dma_sync_single_for_device(fei->dev,
> + channel->pid_buffer_busaddr,
> + PID_TABLE_SIZE,
> + DMA_TO_DEVICE);
> +
> + channel->active = 1;
> +
> + if (fei->global_feed_count == 0) {
> + fei->timer.expires = jiffies + (HZ / POLL_20_HZ_DIV);
Please use ms_to_jiffies().
> + add_timer(&fei->timer);
> + }
> +
> + if (stdemux->running_feed_count == 0) {
> +
> + dev_dbg(fei->dev, "Starting channel=%p\n", channel);
> +
> + tasklet_init(&channel->tsklet, channel_swdemux_tsklet,
> + (unsigned long) channel);
> +
> + /* Reset the internal inputblock sram pointers */
> + writel(channel->fifo,
> + fei->io + C8SECTPFE_IB_BUFF_STRT(channel->tsin_id));
> + writel(channel->fifo + FIFO_LEN - 1,
> + fei->io + C8SECTPFE_IB_BUFF_END(channel->tsin_id));
> +
> + writel(channel->fifo,
> + fei->io + C8SECTPFE_IB_READ_PNT(channel->tsin_id));
> + writel(channel->fifo,
> + fei->io + C8SECTPFE_IB_WRT_PNT(channel->tsin_id));
> +
> +
> + /* reset read / write memdma ptrs for this channel */
> + ptrblk = &channel->irec->ptr_data[0];
> + writel(channel->back_buffer_busaddr,
> + (void __iomem *)&ptrblk->dma_busbase);
> +
> + tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
> + writel(tmp, (void __iomem *)&ptrblk->dma_bustop);
> +
> + writel(channel->back_buffer_busaddr,
> + (void __iomem *)&ptrblk->dma_bus_wp);
> +
> + /* Issue a reset and enable InputBlock */
> + writel(C8SECTPFE_SYS_ENABLE | C8SECTPFE_SYS_RESET
> + , fei->io + C8SECTPFE_IB_SYS(channel->tsin_id));
> +
> + /* and enable the tp */
> + writel(0x1, (void __iomem *)&channel->irec->tp_enable);
> +
> + dev_dbg(fei->dev, "%s:%d Starting DMA feed on stdemux=%p\n"
> + , __func__, __LINE__, stdemux);
> + }
> +
> + stdemux->running_feed_count++;
> + fei->global_feed_count++;
> +
> + mutex_unlock(&fei->lock);
> +
> + return 0;
> +}
> +
> +static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
> +{
> +
> + struct dvb_demux *demux = dvbdmxfeed->demux;
> + struct stdemux *stdemux = (struct stdemux *)demux->priv;
> + struct c8sectpfei *fei = stdemux->c8sectpfei;
> + struct channel_info *channel;
> + struct tpentry *ptrblk;
> + int idlereq;
> + u32 tmp;
> + int ret;
> + unsigned long *bitmap;
> +
> + mutex_lock(&fei->lock);
> +
> + channel = fei->channel_data[stdemux->tsin_index];
> +
> + bitmap = (unsigned long *) channel->pid_buffer_aligned;
> + bitmap_clear(bitmap, dvbdmxfeed->pid, 1);
> +
> + /* manage cache so data is visible to HW */
> + dma_sync_single_for_device(fei->dev,
> + channel->pid_buffer_busaddr,
> + PID_TABLE_SIZE,
> + DMA_TO_DEVICE);
> +
> + if (--stdemux->running_feed_count == 0) {
> +
> + channel = fei->channel_data[stdemux->tsin_index];
> + ptrblk = &channel->irec->ptr_data[0];
> +
> + /* TP re-configuration on page 168 */
Page 168 of what?
> +
> + /* disable IB (prevents more TS data going to memdma) */
> + writel(0, fei->io + C8SECTPFE_IB_SYS(channel->tsin_id));
> +
> + /* disable this channels descriptor */
> + writel(0, (void __iomem *)&channel->irec->tp_enable);
> +
> + tasklet_disable(&channel->tsklet);
> +
> + /* now request memdma channel goes idle */
> + idlereq = (1 << channel->tsin_id) | IDLEREQ;
> + writel(idlereq, fei->io + DMA_IDLE_REQ);
> +
> + /* wait for idle irq handler to signal completion */
> + ret = wait_for_completion_timeout(&channel->idle_completion,
> + msecs_to_jiffies(100));
> +
> + if (ret == 0)
> + dev_warn(fei->dev,
> + "Timeout waiting for idle irq on tsin%d\n",
> + channel->tsin_id);
> +
> + reinit_completion(&channel->idle_completion);
> +
> + /* reset read / write ptrs for this channel */
> + writel(channel->back_buffer_busaddr,
> + (void __iomem *)&ptrblk->dma_busbase);
> +
> + tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
> + writel(tmp, (void __iomem *)&ptrblk->dma_bustop);
> +
> + writel(channel->back_buffer_busaddr,
> + (void __iomem *)&ptrblk->dma_bus_wp);
> +
> + dev_dbg(fei->dev,
> + "%s:%d stopping DMA feed on stdemux=%p channel=%d\n",
> + __func__, __LINE__, stdemux, channel->tsin_id);
> +
> + /* turn off all PIDS in the bitmap*/
> + memset((void *)channel->pid_buffer_aligned
> + , 0x00, PID_TABLE_SIZE);
> +
> + /* manage cache so data is visible to HW */
> + dma_sync_single_for_device(fei->dev,
> + channel->pid_buffer_busaddr,
> + PID_TABLE_SIZE,
> + DMA_TO_DEVICE);
> +
> + channel->active = 0;
> + }
> +
> + if (--fei->global_feed_count == 0) {
> + dev_dbg(fei->dev, "%s:%d global_feed_count=%d\n"
> + , __func__, __LINE__, fei->global_feed_count);
> +
> + del_timer(&fei->timer);
> + }
> +
> + mutex_unlock(&fei->lock);
> +
> + return 0;
> +}
> +
> +static struct channel_info *find_channel(struct c8sectpfei *fei, int tsin_num)
> +{
> + int i;
> +
> + for (i = 0; i < C8SECTPFE_MAXCHANNEL; i++) {
> + if (!fei->channel_data[i])
> + continue;
> +
> + if (fei->channel_data[i]->tsin_id == tsin_num)
> + return fei->channel_data[i];
> + }
> +
> + return NULL;
> +}
> +
> +static void c8sectpfe_getconfig(struct c8sectpfei *fei)
> +{
> + struct c8sectpfe_hw *hw = &fei->hw_stats;
> +
> + hw->num_ib = readl(fei->io + SYS_CFG_NUM_IB);
> + hw->num_mib = readl(fei->io + SYS_CFG_NUM_MIB);
> + hw->num_swts = readl(fei->io + SYS_CFG_NUM_SWTS);
> + hw->num_tsout = readl(fei->io + SYS_CFG_NUM_TSOUT);
> + hw->num_ccsc = readl(fei->io + SYS_CFG_NUM_CCSC);
> + hw->num_ram = readl(fei->io + SYS_CFG_NUM_RAM);
> + hw->num_tp = readl(fei->io + SYS_CFG_NUM_TP);
> +
> + dev_info(fei->dev, "C8SECTPFE hw supports the following:\n");
> + dev_info(fei->dev, "Input Blocks: %d\n", hw->num_ib);
> + dev_info(fei->dev, "Merged Input Blocks: %d\n", hw->num_mib);
> + dev_info(fei->dev, "Software Transport Stream Inputs: %d\n"
> + , hw->num_swts);
> + dev_info(fei->dev, "Transport Stream Output: %d\n", hw->num_tsout);
> + dev_info(fei->dev, "Cable Card Converter: %d\n", hw->num_ccsc);
> + dev_info(fei->dev, "RAMs supported by C8SECTPFE: %d\n", hw->num_ram);
> + dev_info(fei->dev, "Tango TPs supported by C8SECTPFE: %d\n"
> + , hw->num_tp);
> +}
> +
> +static irqreturn_t c8sectpfe_idle_irq_handler(int irq, void *priv)
> +{
> + struct c8sectpfei *fei = priv;
> + struct channel_info *chan;
> + int bit;
> + unsigned long tmp = readl(fei->io + DMA_IDLE_REQ);
> +
> + /* page 168 of functional spec: Clear the idle request
> + by writing 0 to the C8SECTPFE_DMA_IDLE_REQ register. */
> +
> + /* signal idle completion */
> + for_each_set_bit(bit, &tmp, fei->hw_stats.num_ib) {
> +
> + chan = find_channel(fei, bit);
> +
> + if (chan)
> + complete(&chan->idle_completion);
> + }
> +
> + writel(0, fei->io + DMA_IDLE_REQ);
> +
> + return IRQ_HANDLED;
> +}
> +
> +
> +static void free_input_block(struct c8sectpfei *fei, struct channel_info *tsin)
> +{
> + BUG_ON(!fei);
> + BUG_ON(!tsin);
Again, BUG_ON() don't make much sense here. Just return if those vars are
NULL.
> +
> + if (tsin->back_buffer_busaddr)
> + if (!dma_mapping_error(fei->dev, tsin->back_buffer_busaddr))
> + dma_unmap_single(fei->dev, tsin->back_buffer_busaddr,
> + FEI_BUFFER_SIZE, DMA_BIDIRECTIONAL);
> +
> + kfree(tsin->back_buffer_start);
> +
> + if (tsin->pid_buffer_busaddr)
> + if (!dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr))
> + dma_unmap_single(fei->dev, tsin->pid_buffer_busaddr,
> + PID_TABLE_SIZE, DMA_BIDIRECTIONAL);
> +
> + kfree(tsin->pid_buffer_start);
> +}
> +
> +#define MAX_NAME 20
> +
> +static int configure_input_block(struct c8sectpfei *fei,
> + struct channel_info *tsin)
> +{
> + int ret;
> + u32 tmp = 0;
> + struct tpentry *ptrblk;
> + char tsin_pin_name[MAX_NAME];
> +
> + BUG_ON(!fei);
> + BUG_ON(!tsin);
Same here: BUG_ON() seems too severe.
> +
> + dev_dbg(fei->dev, "%s:%d Configuring channel=%p tsin=%d\n"
> + , __func__, __LINE__, tsin, tsin->tsin_id);
> +
> + init_completion(&tsin->idle_completion);
> +
> + tsin->back_buffer_start = kzalloc(FEI_BUFFER_SIZE +
> + FEI_ALIGNMENT, GFP_KERNEL);
> +
> + if (!tsin->back_buffer_start) {
> + ret = -ENOMEM;
> + goto err_unmap;
> + }
> +
> + /* Ensure backbuffer is 32byte aligned */
> + tsin->back_buffer_aligned = tsin->back_buffer_start
> + + FEI_ALIGNMENT;
> +
> + tsin->back_buffer_aligned = (void *)
> + (((uintptr_t) tsin->back_buffer_aligned) & ~0x1F);
> +
> + tsin->back_buffer_busaddr = dma_map_single(fei->dev,
> + (void *)tsin->back_buffer_aligned,
> + FEI_BUFFER_SIZE,
> + DMA_BIDIRECTIONAL);
> +
> + if (dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) {
> + dev_err(fei->dev, "failed to map back_buffer\n");
> + ret = -EFAULT;
> + goto err_unmap;
> + }
> +
> + /*
> + * The pid buffer can be configured (in hw) for byte or bit
> + * per pid. By powers of deduction we conclude stih407 family
> + * is configured (at SoC design stage) for bit per pid.
> + */
> + tsin->pid_buffer_start = kzalloc(2048, GFP_KERNEL);
> +
> + if (!tsin->pid_buffer_start) {
> + ret = -ENOMEM;
> + goto err_unmap;
> + }
> +
> + /*
> + * PID buffer needs to be aligned to size of the pid table
> + * which at bit per pid is 1024 bytes (8192 pids / 8).
> + * PIDF_BASE register enforces this alignment when writing
> + * the register.
> + */
> +
> + tsin->pid_buffer_aligned = tsin->pid_buffer_start +
> + PID_TABLE_SIZE;
> +
> + tsin->pid_buffer_aligned = (void *)
> + (((uintptr_t) tsin->pid_buffer_aligned) & ~0x3ff);
> +
> + tsin->pid_buffer_busaddr = dma_map_single(fei->dev,
> + tsin->pid_buffer_aligned,
> + PID_TABLE_SIZE,
> + DMA_BIDIRECTIONAL);
> +
> + if (dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) {
> + dev_err(fei->dev, "failed to map pid_bitmap\n");
> + ret = -EFAULT;
> + goto err_unmap;
> + }
> +
> + /* manage cache so pid bitmap is visible to HW */
> + dma_sync_single_for_device(fei->dev,
> + tsin->pid_buffer_busaddr,
> + PID_TABLE_SIZE,
> + DMA_TO_DEVICE);
> +
> + snprintf(tsin_pin_name, MAX_NAME, "tsin%d-%s", tsin->tsin_id,
> + (tsin->serial_not_parallel ? "serial" : "parallel"));
> +
> + tsin->pstate = pinctrl_lookup_state(fei->pinctrl, tsin_pin_name);
> + if (IS_ERR(tsin->pstate)) {
> + dev_err(fei->dev, "%s: pinctrl_lookup_state couldn't find %s state\n"
> + , __func__, tsin_pin_name);
> + ret = PTR_ERR(tsin->pstate);
> + goto err_unmap;
> + }
> +
> + ret = pinctrl_select_state(fei->pinctrl, tsin->pstate);
> +
> + if (ret) {
> + dev_err(fei->dev, "%s: pinctrl_select_state failed\n"
> + , __func__);
> + goto err_unmap;
> + }
> +
> + /* Enable this input block */
> + tmp = readl(fei->io + SYS_INPUT_CLKEN);
> + tmp |= BIT(tsin->tsin_id);
> + writel(tmp, fei->io + SYS_INPUT_CLKEN);
> +
> + if (tsin->serial_not_parallel)
> + tmp |= C8SECTPFE_SERIAL_NOT_PARALLEL;
> +
> + if (tsin->invert_ts_clk)
> + tmp |= C8SECTPFE_INVERT_TSCLK;
> +
> + if (tsin->async_not_sync)
> + tmp |= C8SECTPFE_ASYNC_NOT_SYNC;
> +
> + tmp |= C8SECTPFE_ALIGN_BYTE_SOP | C8SECTPFE_BYTE_ENDIANNESS_MSB;
> +
> + writel(tmp, fei->io + C8SECTPFE_IB_IP_FMT_CFG(tsin->tsin_id));
> +
> + writel(C8SECTPFE_SYNC(0x9) |
> + C8SECTPFE_DROP(0x9) |
> + C8SECTPFE_TOKEN(0x47),
> + fei->io + C8SECTPFE_IB_SYNCLCKDRP_CFG(tsin->tsin_id));
> +
> + writel(TS_PKT_SIZE, fei->io + C8SECTPFE_IB_PKT_LEN(tsin->tsin_id));
> +
> + /* Place the FIFO's at the end of the irec descriptors */
> +
> + tsin->fifo = (tsin->tsin_id * FIFO_LEN);
> +
> + writel(tsin->fifo, fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id));
> + writel(tsin->fifo + FIFO_LEN - 1,
> + fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id));
> +
> + writel(tsin->fifo, fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id));
> + writel(tsin->fifo, fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id));
> +
> + writel(tsin->pid_buffer_busaddr,
> + fei->io + PIDF_BASE(tsin->tsin_id));
> +
> + dev_dbg(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=0x%x\n",
> + tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)),
> + tsin->pid_buffer_busaddr);
> +
> + /* Configure and enable HW PID filtering */
> +
> + /*
> + * The PID value is created by assembling the first 8 bytes of
> + * the TS packet into a 64-bit word in big-endian format. A
> + * slice of that 64-bit word is taken from
> + * (PID_OFFSET+PID_NUM_BITS-1) to PID_OFFSET.
> + */
> + tmp = (C8SECTPFE_PID_ENABLE | C8SECTPFE_PID_NUMBITS(13)
> + | C8SECTPFE_PID_OFFSET(40));
> +
> + writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(tsin->tsin_id));
> +
> + dev_dbg(fei->dev, "chan=%d setting wp: %d, rp: %d, buf: %d-%d\n",
> + tsin->tsin_id,
> + readl(fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)),
> + readl(fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)),
> + readl(fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)),
> + readl(fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id)));
> +
> + /*
> + * Base address of pointer record block relative to
> + * base address of DMEM
> + */
> + fei->irec = fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET +
> + readl(fei->io + DMA_PTRREC_BASE);
> +
> + tsin->irec = fei->irec += tsin->tsin_id;
> +
> + ptrblk = &tsin->irec->ptr_data[0];
> +
> + writel(tsin->fifo,
> + (void __iomem *)&tsin->irec->dma_membase);
> +
> + writel(tsin->fifo + FIFO_LEN - 1,
> + (void __iomem *)&tsin->irec->dma_memtop);
> +
> + writel((188 + 7)&~7,
> + (void __iomem *)&tsin->irec->dma_ts_pktsize);
> +
> + writel(0x1, (void __iomem *)&tsin->irec->tp_enable);
> +
> + /* read/write pointers with physical bus address */
> + writel(tsin->back_buffer_busaddr,
> + (void __iomem *)&ptrblk->dma_busbase);
> +
> + tmp = tsin->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
> + writel(tmp, (void __iomem *)&ptrblk->dma_bustop);
> +
> + writel(tsin->back_buffer_busaddr,
> + (void __iomem *)&ptrblk->dma_bus_wp);
> +
> + writel(tsin->back_buffer_busaddr,
> + (void __iomem *)&ptrblk->dma_bus_rp);
> +
> + /* initialize tasklet */
> + tasklet_init(&tsin->tsklet, channel_swdemux_tsklet,
> + (unsigned long) tsin);
> +
> + return 0;
> +
> +err_unmap:
> + free_input_block(fei, tsin);
> + return ret;
> +}
> +
> +static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv)
> +{
> + struct c8sectpfei *fei = priv;
> +
> + dev_err(fei->dev, "%s: error handling not yet implemented\n"
> + , __func__);
> +
> + /*
> + * TODO FIXME we should detect some error conditions here
> + * and ideally so something about them!
> + */
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int c8sectpfe_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *child, *np = dev->of_node;
> + struct c8sectpfei *fei;
> + struct resource *res;
> + int ret, n;
> + struct channel_info *tsin;
> +
> + /* Allocate the c8sectpfei structure */
> + fei = devm_kzalloc(dev, sizeof(struct c8sectpfei), GFP_KERNEL);
> + if (!fei)
> + return -ENOMEM;
> +
> + fei->dev = dev;
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "c8sectpfe");
> + fei->io = devm_ioremap_resource(dev, res);
> + if (IS_ERR(fei->io))
> + return PTR_ERR(fei->io);
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> + "c8sectpfe-ram");
> + fei->sram = devm_ioremap_resource(dev, res);
> + if (IS_ERR(fei->sram))
> + return PTR_ERR(fei->sram);
> +
> + fei->sram_size = res->end - res->start;
> +
> + fei->idle_irq = platform_get_irq_byname(pdev, "c8sectpfe-idle-irq");
> + if (fei->idle_irq < 0) {
> + dev_err(dev, "Can't get c8sectpfe-idle-irq\n");
> + return fei->idle_irq;
> + }
> +
> + fei->error_irq = platform_get_irq_byname(pdev, "c8sectpfe-error-irq");
> + if (fei->error_irq < 0) {
> + dev_err(dev, "Can't get c8sectpfe-error-irq\n");
> + return fei->error_irq;
> + }
> +
> + platform_set_drvdata(pdev, fei);
> +
> + fei->c8sectpfeclk = devm_clk_get(dev, "c8sectpfe");
> + if (IS_ERR(fei->c8sectpfeclk)) {
> + dev_err(dev, "c8sectpfe clk not found\n");
> + return PTR_ERR(fei->c8sectpfeclk);
> + }
> +
> + ret = clk_prepare_enable(fei->c8sectpfeclk);
> + if (ret) {
> + dev_err(dev, "Failed to enable c8sectpfe clock\n");
> + return ret;
> + }
> +
> + /* to save power disable all IP's (on by default) */
> + writel(0, fei->io + SYS_INPUT_CLKEN);
> +
> + /* Enable memdma clock */
> + writel(MEMDMAENABLE, fei->io + SYS_OTHER_CLKEN);
> +
> + /* clear internal sram */
> + memset_io(fei->sram, 0x0, fei->sram_size);
> +
> + c8sectpfe_getconfig(fei);
> +
> + ret = load_slim_core_fw(fei);
> + if (ret) {
> + dev_err(dev, "Couldn't load slim core firmware\n");
> + goto err_clk_disable;
> + }
> +
> + ret = devm_request_irq(dev, fei->idle_irq, c8sectpfe_idle_irq_handler,
> + 0, "c8sectpfe-idle-irq", fei);
> + if (ret) {
> + dev_err(dev, "Can't register c8sectpfe-idle-irq IRQ.\n");
> + goto err_clk_disable;
> + }
> +
> + ret = devm_request_irq(dev, fei->error_irq,
> + c8sectpfe_error_irq_handler, 0,
> + "c8sectpfe-error-irq", fei);
> + if (ret) {
> + dev_err(dev, "Can't register c8sectpfe-error-irq IRQ.\n");
> + goto err_clk_disable;
> + }
> +
> + fei->tsin_count = of_get_child_count(np);
> +
> + if (fei->tsin_count > C8SECTPFE_MAX_TSIN_CHAN ||
> + fei->tsin_count > fei->hw_stats.num_ib) {
> +
> + dev_err(dev, "More tsin declared than exist on SoC!\n");
> + ret = -EINVAL;
> + goto err_clk_disable;
> + }
> +
> + fei->pinctrl = devm_pinctrl_get(dev);
> +
> + if (IS_ERR(fei->pinctrl)) {
> + dev_err(dev, "Error getting tsin pins\n");
> + ret = PTR_ERR(fei->pinctrl);
> + goto err_clk_disable;
> + }
> +
> + n = 0;
> + for_each_child_of_node(np, child) {
> + struct device_node *i2c_bus;
> +
> + fei->channel_data[n] = devm_kzalloc(dev,
> + sizeof(struct channel_info),
> + GFP_KERNEL);
> +
> + if (!fei->channel_data[n]) {
> + ret = -ENOMEM;
> + goto err_clk_disable;
> + }
> +
> + tsin = fei->channel_data[n];
> +
> + tsin->fei = fei;
> +
> + ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id);
> + if (ret) {
> + dev_err(&pdev->dev, "No tsin_num found\n");
> + goto err_clk_disable;
> + }
> +
> + /* sanity check value */
> + if (tsin->tsin_id > fei->hw_stats.num_ib) {
> + dev_err(&pdev->dev,
> + "tsin-num %d specified greater than number\n\t"
> + "of input block hw in SoC! (%d)",
> + tsin->tsin_id, fei->hw_stats.num_ib);
> + ret = -EINVAL;
> + goto err_clk_disable;
> + }
> +
> + tsin->invert_ts_clk = of_property_read_bool(child,
> + "invert-ts-clk");
> +
> + tsin->serial_not_parallel = of_property_read_bool(child,
> + "serial-not-parallel");
> +
> + tsin->async_not_sync = of_property_read_bool(child,
> + "async-not-sync");
> +
> + ret = of_property_read_u32(child, "dvb-card",
> + &tsin->dvb_card);
> + if (ret) {
> + dev_err(&pdev->dev, "No dvb-card found\n");
> + goto err_clk_disable;
> + }
> +
> + i2c_bus = of_parse_phandle(child, "i2c-bus", 0);
> + if (!i2c_bus) {
> + dev_err(&pdev->dev, "No i2c-bus found\n");
> + goto err_clk_disable;
> + }
> + tsin->i2c_adapter =
> + of_find_i2c_adapter_by_node(i2c_bus);
> + if (!tsin->i2c_adapter) {
> + dev_err(&pdev->dev, "No i2c adapter found\n");
> + of_node_put(i2c_bus);
> + goto err_clk_disable;
> + }
> + of_node_put(i2c_bus);
> +
> + tsin->rst_gpio = of_get_named_gpio(child, "rst-gpio", 0);
> +
> + ret = gpio_is_valid(tsin->rst_gpio);
> + if (!ret) {
> + dev_err(dev,
> + "reset gpio for tsin%d not valid (gpio=%d)\n",
> + tsin->tsin_id, tsin->rst_gpio);
> + goto err_clk_disable;
> + }
> +
> + ret = devm_gpio_request_one(dev, tsin->rst_gpio,
> + GPIOF_OUT_INIT_LOW, "NIM reset");
> + if (ret && ret != -EBUSY) {
> + dev_err(dev, "Can't request tsin%d reset gpio\n"
> + , fei->channel_data[n]->tsin_id);
> + goto err_clk_disable;
> + }
> +
> + if (!ret) {
> + /* toggle reset lines */
> + gpio_direction_output(tsin->rst_gpio, 0);
> + msleep(1);
> + gpio_direction_output(tsin->rst_gpio, 1);
> + msleep(1);
WARNING: msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt
msleep(1) doesn't work nice, as it may sleep up to 20 ms, depending on
how HZ is configured. Is that what you want?
> + }
> +
> + tsin->demux_mapping = n;
> +
> + dev_dbg(fei->dev,
> + "channel=%p n=%d tsin_num=%d, invert-ts-clk=%d\n\t"
> + "serial-not-parallel=%d pkt-clk-valid=%d dvb-card=%d\n",
> + fei->channel_data[n], n,
> + tsin->tsin_id, tsin->invert_ts_clk,
> + tsin->serial_not_parallel, tsin->async_not_sync,
> + tsin->dvb_card);
> +
> + ret = configure_input_block(fei, fei->channel_data[n]);
> +
> + if (ret) {
> + dev_err(fei->dev,
> + "configure_input_block tsin=%d failed\n",
> + fei->channel_data[n]->tsin_id);
> + goto err_unmap;
> + }
> +
> + n++;
> + }
> +
> + /* Setup timer interrupt */
> + init_timer(&fei->timer);
> + fei->timer.function = c8sectpfe_timer_interrupt;
> + fei->timer.data = (unsigned long)fei;
> +
> + mutex_init(&fei->lock);
> +
> + /* Get the configuration information about the tuners */
> + ret = c8sectpfe_tuner_register_frontend(&fei->c8sectpfe[0],
> + (void *)fei,
> + c8sectpfe_start_feed,
> + c8sectpfe_stop_feed);
> + if (ret)
> + goto err_unmap;
> +
> + /*
> + * STBus target port can access IMEM and DMEM ports
> + * without waiting for CPU
> + */
> + writel(0x1, fei->io + DMA_PER_STBUS_SYNC);
> +
> + dev_info(dev, "Boot the memdma SLIM core\n");
> + writel(0x1, fei->io + DMA_CPU_RUN);
> +
> + c8sectpfe_debugfs_init(fei);
> +
> + return 0;
> +
> +err_unmap:
> + for (n = 0; n < fei->tsin_count; n++) {
> + tsin = fei->channel_data[n];
> +
> + if (tsin)
> + free_input_block(fei, tsin);
> + }
> +
> +err_clk_disable:
> + /* TODO uncomment when upstream has taken a reference on this clk */
> + /*clk_disable_unprepare(fei->c8sectpfeclk);*/
Hmm... what's the above? Why is it commented?
> + return ret;
> +}
> +
> +static int c8sectpfe_remove(struct platform_device *pdev)
> +{
> + struct c8sectpfei *fei = platform_get_drvdata(pdev);
> + struct channel_info *channel;
> + unsigned int n;
> +
> + c8sectpfe_tuner_unregister_frontend(fei->c8sectpfe[0], fei);
> +
> + /*
> + * Now loop through and un-configure each of the InputBlock resources
> + */
> + for (n = 0; n < fei->tsin_count; n++) {
> + channel = fei->channel_data[n];
> + if (channel)
> + free_input_block(fei, channel);
> + }
> +
> + c8sectpfe_debugfs_exit(fei);
> +
> + dev_info(fei->dev, "Stopping memdma SLIM core\n");
> + if (readl(fei->io + DMA_CPU_RUN))
> + writel(0x0, fei->io + DMA_CPU_RUN);
> +
> + /* unclock all internal IP's */
> + if (readl(fei->io + SYS_INPUT_CLKEN))
> + writel(0, fei->io + SYS_INPUT_CLKEN);
> + if (readl(fei->io + SYS_OTHER_CLKEN))
> + writel(0, fei->io + SYS_OTHER_CLKEN);
> +
> + /* TODO uncomment when upstream has taken a reference on this clk */
> + /*
> + if (fei->c8sectpfeclk)
> + clk_disable_unprepare(fei->c8sectpfeclk);
> + */
Hmm... what's the above? Why is it commented?
> +
> + return 0;
> +}
> +
> +static int load_slim_core_fw(struct c8sectpfei *fei)
> +{
> + const struct firmware *fw = NULL;
> + unsigned char *pImem = NULL;
> + unsigned char *pDmem = NULL;
> + Elf32_Ehdr *ehdr;
> + Elf32_Phdr *phdr;
> + int err = 0;
> + int i;
> +
> + dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA);
> +
> + err = request_firmware(&fw, FIRMWARE_MEMDMA, fei->dev);
> + if (err) {
> + dev_err(fei->dev, "Failed to load %s, %d.\n",
> + FIRMWARE_MEMDMA, err);
> + return -EINVAL;
> + }
> +
> + /* Check ELF magic */
> + ehdr = (Elf32_Ehdr *)fw->data;
> + if (ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
> + ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
> + ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
> + ehdr->e_ident[EI_MAG3] != ELFMAG3) {
> + dev_err(fei->dev, "Invalid ELF magic\n");
> + err = -ENODEV;
> + goto done;
> + } else {
> + dev_dbg(fei->dev, "Valid ELF header found\n");
> + }
> +
> + /* Check program headers are within firmware size */
> + if (ehdr->e_phoff + (ehdr->e_phnum * sizeof(Elf32_Phdr)) > fw->size) {
> + dev_err(fei->dev, "Program headers outside of firmware file\n");
> + err = -ENODEV;
> + goto done;
> + }
> +
> + pImem = (unsigned char *) fei->io + DMA_MEMDMA_OFFSET + DMA_IMEM_OFFSET;
> + pDmem = (unsigned char *) fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET;
> +
> + phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff);
> +
> + /* go through the available ELF segments */
> + for (i = 0; i < ehdr->e_phnum && !err; i++, phdr++) {
> + unsigned char *dest;
> + int imem = 0;
> +
> + /* Only consider LOAD segments */
> + if (phdr->p_type != PT_LOAD)
> + continue;
> +
> + /*
> + * Check segment is contained within the fw->data buffer
> + */
> + if (phdr->p_offset + phdr->p_filesz > fw->size) {
> + dev_err(fei->dev,
> + "Segment %d is outside of firmware file\n", i);
> + err = -EINVAL;
> + break;
> + }
> +
> + /*
> + * MEMDMA IMEM has executable flag set, otherwise load
> + * this segment to DMEM.
> + */
> +
> + if (phdr->p_flags & PF_X) {
> + dest = pImem;
> + imem = 1;
> + } else {
> + dest = pDmem;
> + }
> +
> + /*
> + * The Slim ELF file uses 32-bit word addressing for
> + * load offsets.
> + */
> +
> + dest += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int);
> +
> + /*
> + * For DMEM segments copy the segment data from the ELF
> + * file and pad segment with zeroes
> + *
> + * For IMEM segments, the segment contains 24-bit
> + * instructions which must be padded to 32-bit
> + * instructions before being written. The written
> + * segment is padded with NOP instructions.
> + */
> +
> + if (!imem) {
> + dev_dbg(fei->dev,
> + "Loading DMEM segment %d 0x%08x\n\t"
> + "(0x%x bytes) -> 0x%08x (0x%x bytes)\n",
> + i, phdr->p_paddr, phdr->p_filesz,
> + (unsigned int)dest, phdr->p_memsz);
> +
> + memcpy((void *)dest, (void *)fw->data + phdr->p_offset,
> + phdr->p_filesz);
> +
> + memset((void *)dest + phdr->p_filesz, 0,
> + phdr->p_memsz - phdr->p_filesz);
> + } else {
> + const unsigned char *imem_src = fw->data
> + + phdr->p_offset;
> + unsigned char *imem_dest = dest;
> + int j;
> +
> + dev_dbg(fei->dev,
> + "Loading IMEM segment %d 0x%08x\n\t"
> + " (0x%x bytes) -> 0x%08x (0x%x bytes)\n", i,
> + phdr->p_paddr, phdr->p_filesz,
> + (unsigned int)dest,
> + phdr->p_memsz + phdr->p_memsz / 3);
> +
> + for (j = 0; j < phdr->p_filesz; j++) {
> + *imem_dest = *imem_src;
> +
> + /* Every 3 bytes, add an additional
> + * padding zero in destination */
> + if (j % 3 == 2) {
> + imem_dest++;
> + *imem_dest = 0x00;
> + }
> + imem_dest++;
> + imem_src++;
> + }
> + }
> + }
> +
> +done:
> + release_firmware(fw);
> + return err;
> +}
> +
> +static const struct of_device_id c8sectpfe_match[] = {
> + { .compatible = "st,stih407-c8sectpfe" },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, c8sectpfe_match);
> +
> +static struct platform_driver c8sectpfe_driver = {
> + .driver = {
> + .name = "c8sectpfe",
> + .of_match_table = of_match_ptr(c8sectpfe_match),
> + },
> + .probe = c8sectpfe_probe,
> + .remove = c8sectpfe_remove,
> +};
> +
> +module_platform_driver(c8sectpfe_driver);
> +
> +MODULE_AUTHOR("Peter Bennett <[email protected]>");
> +MODULE_AUTHOR("Peter Griffin <[email protected]>");
> +MODULE_DESCRIPTION("C8SECTPFE STi DVB Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-core.h b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.h
> new file mode 100644
> index 0000000..3466303
> --- /dev/null
> +++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.h
> @@ -0,0 +1,288 @@
> +/*
> + * c8sectpfe-core.h - C8SECTPFE STi DVB driver
> + *
> + * Copyright (c) STMicroelectronics 2015
> + *
> + * Author:Peter Bennett <[email protected]>
> + * Peter Griffin <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +#ifndef _C8SECTPFE_CORE_H_
> +#define _C8SECTPFE_CORE_H_
> +
> +#define C8SECTPFEI_MAXCHANNEL 16
> +#define C8SECTPFEI_MAXADAPTER 3
> +
> +#define C8SECTPFE_MAX_TSIN_CHAN 8
> +
> +struct channel_info {
> +
> + int tsin_id;
> + bool invert_ts_clk;
> + bool serial_not_parallel;
> + bool async_not_sync;
> + int i2c;
> + int dvb_card;
> +
> + int rst_gpio;
> +
> + struct i2c_adapter *i2c_adapter;
> + struct i2c_adapter *tuner_i2c;
> + struct i2c_adapter *lnb_i2c;
> + struct i2c_client *i2c_client;
> + struct dvb_frontend *frontend;
> +
> + struct pinctrl_state *pstate;
> +
> + int demux_mapping;
> + int active;
> +
> + void *back_buffer_start;
> + void *back_buffer_aligned;
> + dma_addr_t back_buffer_busaddr;
> +
> + void *pid_buffer_start;
> + void *pid_buffer_aligned;
> + dma_addr_t pid_buffer_busaddr;
> +
> + unsigned long fifo;
> +
> + struct completion idle_completion;
> + struct tasklet_struct tsklet;
> +
> + struct c8sectpfei *fei;
> + struct c8sectpfe_input_record *irec;
> +
> +};
> +
> +struct c8sectpfe_hw {
> + int num_ib;
> + int num_mib;
> + int num_swts;
> + int num_tsout;
> + int num_ccsc;
> + int num_ram;
> + int num_tp;
> +};
> +
> +struct c8sectpfei {
> +
> + struct device *dev;
> + struct pinctrl *pinctrl;
> +
> + struct dentry *root;
> + struct debugfs_regset32 *regset;
> +
> + int tsin_count;
> +
> + struct c8sectpfe_hw hw_stats;
> +
> + struct c8sectpfe *c8sectpfe[C8SECTPFEI_MAXADAPTER];
> +
> + int mapping[C8SECTPFEI_MAXCHANNEL];
> +
> + struct mutex lock;
> +
> + struct timer_list timer; /* timer interrupts for outputs */
> +
> + void __iomem *io;
> + void __iomem *sram;
> +
> + unsigned long sram_size;
> +
> + struct channel_info *channel_data[C8SECTPFE_MAX_TSIN_CHAN];
> +
> + struct clk *c8sectpfeclk;
> + int nima_rst_gpio;
> + int nimb_rst_gpio;
> +
> + int idle_irq;
> + int error_irq;
> +
> + int global_feed_count;
> +
> + struct c8sectpfe_input_record *irec;
> +};
> +
> +/* C8SECTPFE SYS Regs list */
> +
> +#define SYS_INPUT_ERR_STATUS 0x0
> +#define SYS_OTHER_ERR_STATUS 0x8
> +#define SYS_INPUT_ERR_MASK 0x10
> +#define SYS_OTHER_ERR_MASK 0x18
> +#define SYS_DMA_ROUTE 0x20
> +#define SYS_INPUT_CLKEN 0x30
> +#define IBENABLE_MASK 0x7F
> +
> +#define SYS_OTHER_CLKEN 0x38
> +#define TSDMAENABLE BIT(1)
> +#define MEMDMAENABLE BIT(0)
> +
> +#define SYS_CFG_NUM_IB 0x200
> +#define SYS_CFG_NUM_MIB 0x204
> +#define SYS_CFG_NUM_SWTS 0x208
> +#define SYS_CFG_NUM_TSOUT 0x20C
> +#define SYS_CFG_NUM_CCSC 0x210
> +#define SYS_CFG_NUM_RAM 0x214
> +#define SYS_CFG_NUM_TP 0x218
> +
> +/* Input Block Regs */
> +
> +#define C8SECTPFE_INPUTBLK_OFFSET 0x1000
> +#define C8SECTPFE_CHANNEL_OFFSET(x) ((x*0x40) + C8SECTPFE_INPUTBLK_OFFSET)
> +
> +#define C8SECTPFE_IB_IP_FMT_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x00)
> +#define C8SECTPFE_IGNORE_ERR_AT_SOP BIT(7)
> +#define C8SECTPFE_IGNORE_ERR_IN_PKT BIT(6)
> +#define C8SECTPFE_IGNORE_ERR_IN_BYTE BIT(5)
> +#define C8SECTPFE_INVERT_TSCLK BIT(4)
> +#define C8SECTPFE_ALIGN_BYTE_SOP BIT(3)
> +#define C8SECTPFE_ASYNC_NOT_SYNC BIT(2)
> +#define C8SECTPFE_BYTE_ENDIANNESS_MSB BIT(1)
> +#define C8SECTPFE_SERIAL_NOT_PARALLEL BIT(0)
> +
> +#define C8SECTPFE_IB_SYNCLCKDRP_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x04)
> +#define C8SECTPFE_SYNC(x) (x & 0xf)
> +#define C8SECTPFE_DROP(x) ((x<<4) & 0xf)
> +#define C8SECTPFE_TOKEN(x) ((x<<8) & 0xff00)
> +#define C8SECTPFE_SLDENDIANNESS BIT(16)
> +
> +#define C8SECTPFE_IB_TAGBYTES_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x08)
> +#define C8SECTPFE_TAG_HEADER(x) (x << 16)
> +#define C8SECTPFE_TAG_COUNTER(x) ((x<<1) & 0x7fff)
> +#define C8SECTPFE_TAG_ENABLE BIT(0)
> +
> +#define C8SECTPFE_IB_PID_SET(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x0C)
> +#define C8SECTPFE_PID_OFFSET(x) (x & 0x3f)
> +#define C8SECTPFE_PID_NUMBITS(x) ((x << 6) & 0xfff)
> +#define C8SECTPFE_PID_ENABLE BIT(31)
> +
> +#define C8SECTPFE_IB_PKT_LEN(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x10)
> +
> +#define C8SECTPFE_IB_BUFF_STRT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x14)
> +#define C8SECTPFE_IB_BUFF_END(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x18)
> +#define C8SECTPFE_IB_READ_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x1C)
> +#define C8SECTPFE_IB_WRT_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x20)
> +
> +#define C8SECTPFE_IB_PRI_THRLD(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x24)
> +#define C8SECTPFE_PRI_VALUE(x) (x & 0x7fffff)
> +#define C8SECTPFE_PRI_LOWPRI(x) ((x & 0xf) << 24)
> +#define C8SECTPFE_PRI_HIGHPRI(x) ((x & 0xf) << 28)
> +
> +#define C8SECTPFE_IB_STAT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x28)
> +#define C8SECTPFE_STAT_FIFO_OVERFLOW(x) (x & 0x1)
> +#define C8SECTPFE_STAT_BUFFER_OVERFLOW(x) (x & 0x2)
> +#define C8SECTPFE_STAT_OUTOFORDERRP(x) (x & 0x4)
> +#define C8SECTPFE_STAT_PID_OVERFLOW(x) (x & 0x8)
> +#define C8SECTPFE_STAT_PKT_OVERFLOW(x) (x & 0x10)
> +#define C8SECTPFE_STAT_ERROR_PACKETS(x) ((x >> 8) & 0xf)
> +#define C8SECTPFE_STAT_SHORT_PACKETS(x) ((x >> 12) & 0xf)
> +
> +#define C8SECTPFE_IB_MASK(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x2C)
> +#define C8SECTPFE_MASK_FIFO_OVERFLOW BIT(0)
> +#define C8SECTPFE_MASK_BUFFER_OVERFLOW BIT(1)
> +#define C8SECTPFE_MASK_OUTOFORDERRP(x) BIT(2)
> +#define C8SECTPFE_MASK_PID_OVERFLOW(x) BIT(3)
> +#define C8SECTPFE_MASK_PKT_OVERFLOW(x) BIT(4)
> +#define C8SECTPFE_MASK_ERROR_PACKETS(x) ((x & 0xf) << 8)
> +#define C8SECTPFE_MASK_SHORT_PACKETS(x) ((x & 0xf) >> 12)
> +
> +#define C8SECTPFE_IB_SYS(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x30)
> +#define C8SECTPFE_SYS_RESET BIT(1)
> +#define C8SECTPFE_SYS_ENABLE BIT(0)
> +
> +/*
> + * Ponter record data structure required for each input block
> + * see Table 82 on page 167
> + */
> +
> +struct tpentry {
> + /* The following entries are bus addresses for memdma */
> + unsigned long dma_busbase;
> + unsigned long dma_bustop;
> + unsigned long dma_bus_wp;
> + unsigned long dma_bus_rp;
> +};
> +
> +struct c8sectpfe_input_record {
> + unsigned long dma_membase; /* Internal sram base address */
> + unsigned long dma_memtop; /* Internal sram top address */
> +
> + /*
> + * TS packet size, including tag bytes added by input block,
> + * rounded up to the next multiple of 8 bytes. The packet size,
> + * including any tagging bytes and rounded up to the nearest
> + * multiple of 8 bytes must be less than 255 bytes.
> + */
> + unsigned long dma_ts_pktsize;
> + unsigned long tp_enable;
> + struct tpentry ptr_data[1];
> +};
> +
> +#define DMA_MEMDMA_OFFSET 0x4000
> +#define DMA_IMEM_OFFSET 0x0
> +#define DMA_DMEM_OFFSET 0x4000
> +#define DMA_CPU 0x8000
> +#define DMA_PER_OFFSET 0xb000
> +
> +/* XP70 Slim core regs */
> +#define DMA_CPU_ID (DMA_MEMDMA_OFFSET + DMA_CPU + 0x0)
> +#define DMA_CPU_VCR (DMA_MEMDMA_OFFSET + DMA_CPU + 0x4)
> +#define DMA_CPU_RUN (DMA_MEMDMA_OFFSET + DMA_CPU + 0x8)
> +#define DMA_CPU_CLOCKGATE (DMA_MEMDMA_OFFSET + DMA_CPU + 0xc)
> +#define DMA_CPU_PC (DMA_MEMDMA_OFFSET + DMA_CPU + 0x20)
> +
> +/* Enable Interrupt for a IB */
> +#define DMA_PER_TPn_DREQ_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd00)
> +/* Ack interrupt by setting corresponding bit */
> +#define DMA_PER_TPn_DACK_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd80)
> +#define DMA_PER_TPn_DREQ (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe00)
> +#define DMA_PER_TPn_DACK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe80)
> +#define DMA_PER_DREQ_MODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf80)
> +#define DMA_PER_STBUS_SYNC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf88)
> +#define DMA_PER_STBUS_ACCESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf8c)
> +#define DMA_PER_STBUS_ADDRESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf90)
> +#define DMA_PER_IDLE_INT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfa8)
> +#define DMA_PER_PRIORITY (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfac)
> +#define DMA_PER_MAX_OPCODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb0)
> +#define DMA_PER_MAX_CHUNK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb4)
> +#define DMA_PER_PAGE_SIZE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfbc)
> +#define DMA_PER_MBOX_STATUS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc0)
> +#define DMA_PER_MBOX_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc8)
> +#define DMA_PER_MBOX_CLEAR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd0)
> +#define DMA_PER_MBOX_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd8)
> +#define DMA_PER_INJECT_PKT_SRC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe0)
> +#define DMA_PER_INJECT_PKT_DEST (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe4)
> +#define DMA_PER_INJECT_PKT_ADDR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe8)
> +#define DMA_PER_INJECT_PKT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfec)
> +#define DMA_PER_PAT_PTR_INIT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff0)
> +#define DMA_PER_PAT_PTR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff4)
> +#define DMA_PER_SLEEP_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff8)
> +#define DMA_PER_SLEEP_COUNTER (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xffc)
> +/* #define DMA_RF_CPUREGn DMA_RFBASEADDR n=0 to 15) slim regsa */
> +
> +/* The following are from DMA_DMEM_BaseAddress */
> +#define DMA_FIRMWARE_VERSION (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x0)
> +#define DMA_PTRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x4)
> +#define DMA_PTRREC_INPUT_OFFSET (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x8)
> +#define DMA_ERRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0xc)
> +#define DMA_ERROR_RECORD(n) ((n*4) + DMA_ERRREC_BASE + 0x4)
> +#define DMA_IDLE_REQ (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x10)
> +#define IDLEREQ BIT(31)
> +
> +#define DMA_FIRMWARE_CONFIG (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x14)
> +
> +/* Regs for PID Filter */
> +
> +#define PIDF_OFFSET 0x2800
> +#define PIDF_BASE(n) ((n*4) + PIDF_OFFSET)
> +#define PIDF_LEAK_ENABLE (PIDF_OFFSET + 0x100)
> +#define PIDF_LEAK_STATUS (PIDF_OFFSET + 0x108)
> +#define PIDF_LEAK_COUNT_RESET (PIDF_OFFSET + 0x110)
> +#define PIDF_LEAK_COUNTER (PIDF_OFFSET + 0x114)
> +
> +#endif /* _C8SECTPFE_CORE_H_ */
Em Wed, 24 Jun 2015 16:11:07 +0100
Peter Griffin <[email protected]> escreveu:
> This patch adds support for the following 3 NIM cards: -
> 1) STV0367-NIM (stv0367 demod with Thompson PLL)
> 2) B2100A (2x stv0367 demods & 2x NXP tda18212 tuners)
> 3) STV0903-6110NIM (stv0903 demod + 6110 tuner, lnb24)
>
> Signed-off-by: Peter Griffin <[email protected]>
> ---
> drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c | 296 +++++++++++++++++++++++++++
> drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h | 20 ++
> 2 files changed, 316 insertions(+)
> create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c
> create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h
>
> diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c
> new file mode 100644
> index 0000000..5c4ecb4
> --- /dev/null
> +++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c
> @@ -0,0 +1,296 @@
> +/*
> + * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver
> + *
> + * Copyright (c) STMicroelectronics 2015
> + *
> + * Author Peter Griffin <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + *
> + * GNU General Public License for more details.
> + */
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/version.h>
> +
> +#include <dt-bindings/media/c8sectpfe.h>
> +
> +#include "c8sectpfe-common.h"
> +#include "c8sectpfe-core.h"
> +#include "c8sectpfe-dvb.h"
> +
> +#include "dvb-pll.h"
> +#include "lnbh24.h"
> +#include "stv0367.h"
> +#include "stv0367_priv.h"
> +#include "stv6110x.h"
> +#include "stv090x.h"
> +#include "tda18212.h"
> +
> +static inline const char *dvb_card_str(unsigned int c)
> +{
> + switch (c) {
> + case STV0367_PLL_BOARD_NIMA: return "STV0367_PLL_BOARD_NIMA";
> + case STV0367_PLL_BOARD_NIMB: return "STV0367_PLL_BOARD_NIMB";
> + case STV0367_TDA18212_NIMA_1: return "STV0367_TDA18212_NIMA_1";
> + case STV0367_TDA18212_NIMA_2: return "STV0367_TDA18212_NIMA_2";
> + case STV0367_TDA18212_NIMB_1: return "STV0367_TDA18212_NIMB_1";
> + case STV0367_TDA18212_NIMB_2: return "STV0367_TDA18212_NIMB_2";
> + case STV0903_6110_LNB24_NIMA: return "STV0903_6110_LNB24_NIMA";
> + case STV0903_6110_LNB24_NIMB: return "STV0903_6110_LNB24_NIMB";
> + default: return "unknown dvb frontend card";
> + }
> +}
> +
> +static struct stv090x_config stv090x_config = {
> + .device = STV0903,
> + .demod_mode = STV090x_SINGLE,
> + .clk_mode = STV090x_CLK_EXT,
> + .xtal = 16000000,
> + .address = 0x69,
> +
> + .ts1_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
> + .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
> +
> + .repeater_level = STV090x_RPTLEVEL_64,
> +
> + .tuner_init = NULL,
> + .tuner_set_mode = NULL,
> + .tuner_set_frequency = NULL,
> + .tuner_get_frequency = NULL,
> + .tuner_set_bandwidth = NULL,
> + .tuner_get_bandwidth = NULL,
> + .tuner_set_bbgain = NULL,
> + .tuner_get_bbgain = NULL,
> + .tuner_set_refclk = NULL,
> + .tuner_get_status = NULL,
> +};
> +
> +static struct stv6110x_config stv6110x_config = {
> + .addr = 0x60,
> + .refclk = 16000000,
> +};
> +
> +#define NIMA 0
> +#define NIMB 1
> +
> +static struct stv0367_config stv0367_pll_config[] = {
> + {
> + .demod_address = 0x1c,
> + .xtal = 27000000,
> + .if_khz = 36166,
> + .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
> + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
> + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
> + }, {
> + .demod_address = 0x1d,
> + .xtal = 27000000,
> + .if_khz = 36166,
> + .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
> + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
> + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
> + },
> +};
> +
> +static struct stv0367_config stv0367_tda18212_config[] = {
> + {
> + .demod_address = 0x1c,
> + .xtal = 16000000,
> + .if_khz = 4500,
> + .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
> + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
> + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
> + }, {
> + .demod_address = 0x1d,
> + .xtal = 16000000,
> + .if_khz = 4500,
> + .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
> + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
> + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
> + }, {
> + .demod_address = 0x1e,
> + .xtal = 16000000,
> + .if_khz = 4500,
> + .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
> + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
> + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
> + },
> +};
> +
> +static struct tda18212_config tda18212_conf = {
> + .if_dvbt_6 = 4150,
> + .if_dvbt_7 = 4150,
> + .if_dvbt_8 = 4500,
> + .if_dvbc = 5000,
> +};
> +
> +int c8sectpfe_frontend_attach(struct dvb_frontend **fe,
> + struct c8sectpfe *c8sectpfe,
> + struct channel_info *tsin, int chan_num)
> +{
> + struct tda18212_config *tda18212;
> + struct stv6110x_devctl *fe2;
> + struct i2c_client *client;
> + struct i2c_board_info tda18212_info = {
> + .type = "tda18212",
> + .addr = 0x60,
> + };
> +
> + BUG_ON(!tsin);
Just return an error if !tsin.
> +
> + switch (tsin->dvb_card) {
> +
> + case STV0367_PLL_BOARD_NIMA:
> + case STV0367_PLL_BOARD_NIMB:
> + if (tsin->dvb_card == STV0367_PLL_BOARD_NIMA)
> + *fe = dvb_attach(stv0367ter_attach,
> + &stv0367_pll_config[NIMA], tsin->i2c_adapter);
> + else
> + *fe = dvb_attach(stv0367ter_attach,
> + &stv0367_pll_config[NIMB], tsin->i2c_adapter);
> +
> + if (!*fe) {
> + dev_err(c8sectpfe->device,
> + "%s: stv0367ter_attach failed for NIM card %s\n"
> + , __func__, dvb_card_str(tsin->dvb_card));
> + return -ENODEV;
> + };
> +
> + /*
> + * init the demod so that i2c gate_ctrl
> + * to the tuner works correctly
> + */
> + (*fe)->ops.init(*fe);
> +
> + if (!dvb_attach(dvb_pll_attach, *fe, 0x60,
> + tsin->i2c_adapter, DVB_PLL_THOMSON_DTT7546X)) {
> +
> + dev_err(c8sectpfe->device,
> + "%s: DVB_PLL_THOMSON_DTT7546X attach failed\n\t"
> + "for NIM card %s\n",
> + __func__, dvb_card_str(tsin->dvb_card));
> + return -ENODEV;
> + }
> + break;
> +
> + case STV0367_TDA18212_NIMA_1:
> + case STV0367_TDA18212_NIMA_2:
> + case STV0367_TDA18212_NIMB_1:
> + case STV0367_TDA18212_NIMB_2:
> + if (tsin->dvb_card == STV0367_TDA18212_NIMA_1)
> + *fe = dvb_attach(stv0367ter_attach,
> + &stv0367_tda18212_config[0],
> + tsin->i2c_adapter);
> + else if (tsin->dvb_card == STV0367_TDA18212_NIMB_1)
> + *fe = dvb_attach(stv0367ter_attach,
> + &stv0367_tda18212_config[1],
> + tsin->i2c_adapter);
> + else
> + *fe = dvb_attach(stv0367ter_attach,
> + &stv0367_tda18212_config[2],
> + tsin->i2c_adapter);
> +
> + if (!*fe) {
> + dev_err(c8sectpfe->device,
> + "%s: stv0367ter_attach failed for NIM card %s\n"
> + , __func__, dvb_card_str(tsin->dvb_card));
> + return -ENODEV;
> + };
> +
> + /*
> + * init the demod so that i2c gate_ctrl
> + * to the tuner works correctly
> + */
> + (*fe)->ops.init(*fe);
> +
> + /* Allocate the tda18212 structure */
> + tda18212 = devm_kzalloc(c8sectpfe->device,
> + sizeof(struct tda18212_config),
> + GFP_KERNEL);
> + if (!tda18212) {
> + dev_err(c8sectpfe->device,
> + "%s: devm_kzalloc failed\n", __func__);
> + return -ENOMEM;
> + }
> +
> + memcpy(tda18212, &tda18212_conf,
> + sizeof(struct tda18212_config));
> +
> + tda18212->fe = (*fe);
> +
> + tda18212_info.platform_data = tda18212;
> +
> + /* attach tuner */
> + request_module("tda18212");
> + client = i2c_new_device(tsin->i2c_adapter, &tda18212_info);
> + if (!client || !client->dev.driver) {
> + dvb_frontend_detach(*fe);
> + return -ENODEV;
> + }
> +
> + if (!try_module_get(client->dev.driver->owner)) {
> + i2c_unregister_device(client);
> + dvb_frontend_detach(*fe);
> + return -ENODEV;
> + }
> +
> + tsin->i2c_client = client;
> +
> + break;
> +
> + case STV0903_6110_LNB24_NIMA:
> + *fe = dvb_attach(stv090x_attach, &stv090x_config,
> + tsin->i2c_adapter, STV090x_DEMODULATOR_0);
> + if (!*fe) {
> + dev_err(c8sectpfe->device, "%s: stv090x_attach failed\n"
> + "\tfor NIM card %s\n",
> + __func__, dvb_card_str(tsin->dvb_card));
> + return -ENODEV;
> + }
> +
> + fe2 = dvb_attach(stv6110x_attach, *fe,
> + &stv6110x_config, tsin->i2c_adapter);
> + if (!fe2) {
> + dev_err(c8sectpfe->device,
> + "%s: stv6110x_attach failed for NIM card %s\n"
> + , __func__, dvb_card_str(tsin->dvb_card));
> + return -ENODEV;
> + };
> +
> + stv090x_config.tuner_init = fe2->tuner_init;
> + stv090x_config.tuner_set_mode = fe2->tuner_set_mode;
> + stv090x_config.tuner_set_frequency = fe2->tuner_set_frequency;
> + stv090x_config.tuner_get_frequency = fe2->tuner_get_frequency;
> + stv090x_config.tuner_set_bandwidth = fe2->tuner_set_bandwidth;
> + stv090x_config.tuner_get_bandwidth = fe2->tuner_get_bandwidth;
> + stv090x_config.tuner_set_bbgain = fe2->tuner_set_bbgain;
> + stv090x_config.tuner_get_bbgain = fe2->tuner_get_bbgain;
> + stv090x_config.tuner_set_refclk = fe2->tuner_set_refclk;
> + stv090x_config.tuner_get_status = fe2->tuner_get_status;
> +
> + dvb_attach(lnbh24_attach, *fe, tsin->i2c_adapter, 0, 0, 0x9);
> + break;
> +
> + default:
> + dev_err(c8sectpfe->device,
> + "%s: DVB frontend card %s not yet supported\n",
> + __func__, dvb_card_str(tsin->dvb_card));
> + return -ENODEV;
> + }
> +
> + (*fe)->id = chan_num;
> +
> + dev_info(c8sectpfe->device,
> + "DVB frontend card %s successfully attached",
> + dvb_card_str(tsin->dvb_card));
> + return 0;
> +}
> diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h b/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h
> new file mode 100644
> index 0000000..bd366db
> --- /dev/null
> +++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h
> @@ -0,0 +1,20 @@
> +/*
> + * c8sectpfe-common.h - C8SECTPFE STi DVB driver
> + *
> + * Copyright (c) STMicroelectronics 2015
> + *
> + * Author: Peter Griffin <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +#ifndef _C8SECTPFE_DVB_H_
> +#define _C8SECTPFE_DVB_H_
> +
> +int c8sectpfe_frontend_attach(struct dvb_frontend **fe,
> + struct c8sectpfe *c8sectpfe, struct channel_info *tsin,
> + int chan_num);
> +
> +#endif
Em Wed, 24 Jun 2015 16:11:09 +0100
Peter Griffin <[email protected]> escreveu:
> This patch adds the Kconfig and Makefile for the c8sectpfe driver
> so it will be built. It also selects additional demodulator and tuners
> which are required by the supported NIM cards.
>
> Signed-off-by: Peter Griffin <[email protected]>
> ---
> drivers/media/Kconfig | 1 +
> drivers/media/Makefile | 1 +
> drivers/media/tsin/c8sectpfe/Kconfig | 26 ++++++++++++++++++++++++++
> drivers/media/tsin/c8sectpfe/Makefile | 11 +++++++++++
> 4 files changed, 39 insertions(+)
> create mode 100644 drivers/media/tsin/c8sectpfe/Kconfig
> create mode 100644 drivers/media/tsin/c8sectpfe/Makefile
>
> diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
> index 1570992..82bc1dc 100644
> --- a/drivers/media/Kconfig
> +++ b/drivers/media/Kconfig
> @@ -170,6 +170,7 @@ source "drivers/media/pci/Kconfig"
> source "drivers/media/platform/Kconfig"
> source "drivers/media/mmc/Kconfig"
> source "drivers/media/radio/Kconfig"
> +source "drivers/media/tsin/c8sectpfe/Kconfig"
>
> comment "Supported FireWire (IEEE 1394) Adapters"
> depends on DVB_CORE && FIREWIRE
> diff --git a/drivers/media/Makefile b/drivers/media/Makefile
> index e608bbc..0a567b8 100644
> --- a/drivers/media/Makefile
> +++ b/drivers/media/Makefile
> @@ -29,5 +29,6 @@ obj-y += rc/
> #
>
> obj-y += common/ platform/ pci/ usb/ mmc/ firewire/
> +obj-$(CONFIG_DVB_C8SECTPFE) += tsin/c8sectpfe/
Hmm... why are you adding it at a new "tsin" directory? We're putting
those SoC platform drivers under platform/.
> obj-$(CONFIG_VIDEO_DEV) += radio/
>
> diff --git a/drivers/media/tsin/c8sectpfe/Kconfig b/drivers/media/tsin/c8sectpfe/Kconfig
> new file mode 100644
> index 0000000..8d99a87
> --- /dev/null
> +++ b/drivers/media/tsin/c8sectpfe/Kconfig
> @@ -0,0 +1,26 @@
> +config DVB_C8SECTPFE
> + tristate "STMicroelectronics C8SECTPFE DVB support"
> + depends on DVB_CORE && I2C && (ARCH_STI || ARCH_MULTIPLATFORM)
> + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
> + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
> + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
> + select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
> + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
> + select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
> + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
> + select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
> +
> + ---help---
> + This adds support for DVB front-end cards connected
> + to TS inputs of STiH407/410 SoC.
> +
> + The driver currently supports C8SECTPFE's TS input block,
> + memdma engine, and HW PID filtering.
> +
> + Supported DVB front-end cards are:
> + - STMicroelectronics DVB-T B2100A (STV0367 + TDA18212)
> + - STMicroelectronics DVB-T STV0367 PLL board (STV0367 + DTT7546X)
> + - STMicroelectronics DVB-S/S2 STV0903 + STV6110 + LNBP24 board
> +
> + To compile this driver as a module, choose M here: the
> + module will be called c8sectpfe.
> diff --git a/drivers/media/tsin/c8sectpfe/Makefile b/drivers/media/tsin/c8sectpfe/Makefile
> new file mode 100644
> index 0000000..777f06d
> --- /dev/null
> +++ b/drivers/media/tsin/c8sectpfe/Makefile
> @@ -0,0 +1,11 @@
> +c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o
> +
> +obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o
> +
> +ifneq ($(CONFIG_DVB_C8SECTPFE),)
> + c8sectpfe-y += c8sectpfe-debugfs.o
> +endif
> +
> +ccflags-y += -Idrivers/media/i2c
> +ccflags-y += -Idrivers/media/common
> +ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ -Idrivers/media/tuners/
Em Wed, 24 Jun 2015 18:17:37 -0700
Joe Perches <[email protected]> escreveu:
> On Wed, 2015-06-24 at 16:11 +0100, Peter Griffin wrote:
> > This is used in conjunction with the STV0367 demodulator on
> > the STV0367-NIM-V1.0 NIM card which can be used with the STi
> > STB SoC's.
>
> Barely associated to this specific patch, but for
> dvb-pll.c, another thing that seems possible is to
> convert the struct dvb_pll_desc uses to const and
> change the "entries" fixed array size from 12 to []
>
> It'd save a couple KB overall and remove ~5KB of data.
>
> $ size drivers/media/dvb-frontends/dvb-pll.o*
> text data bss dec hex filename
> 8520 1552 2120 12192 2fa0 drivers/media/dvb-frontends/dvb-pll.o.new
> 5624 6363 2120 14107 371b drivers/media/dvb-frontends/dvb-pll.o.old
Peter,
Please add this patch on the next patch series you submit.
Regards,
Mauro
> ---
> drivers/media/dvb-frontends/dvb-pll.c | 50 +++++++++++++++++------------------
> 1 file changed, 25 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c
> index 6d8fe88..53089e1 100644
> --- a/drivers/media/dvb-frontends/dvb-pll.c
> +++ b/drivers/media/dvb-frontends/dvb-pll.c
> @@ -34,7 +34,7 @@ struct dvb_pll_priv {
> struct i2c_adapter *i2c;
>
> /* the PLL descriptor */
> - struct dvb_pll_desc *pll_desc;
> + const struct dvb_pll_desc *pll_desc;
>
> /* cached frequency/bandwidth */
> u32 frequency;
> @@ -57,7 +57,7 @@ MODULE_PARM_DESC(id, "force pll id to use (DEBUG ONLY)");
> /* ----------------------------------------------------------- */
>
> struct dvb_pll_desc {
> - char *name;
> + const char *name;
> u32 min;
> u32 max;
> u32 iffreq;
> @@ -71,13 +71,13 @@ struct dvb_pll_desc {
> u32 stepsize;
> u8 config;
> u8 cb;
> - } entries[12];
> + } entries[];
> };
>
> /* ----------------------------------------------------------- */
> /* descriptions */
>
> -static struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
> +static const struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
> .name = "Thomson dtt7579",
> .min = 177000000,
> .max = 858000000,
> @@ -99,7 +99,7 @@ static void thomson_dtt759x_bw(struct dvb_frontend *fe, u8 *buf)
> buf[3] |= 0x10;
> }
>
> -static struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
> +static const struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
> .name = "Thomson dtt759x",
> .min = 177000000,
> .max = 896000000,
> @@ -123,7 +123,7 @@ static void thomson_dtt7520x_bw(struct dvb_frontend *fe, u8 *buf)
> buf[3] ^= 0x10;
> }
>
> -static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
> +static const struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
> .name = "Thomson dtt7520x",
> .min = 185000000,
> .max = 900000000,
> @@ -141,7 +141,7 @@ static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
> },
> };
>
> -static struct dvb_pll_desc dvb_pll_lg_z201 = {
> +static const struct dvb_pll_desc dvb_pll_lg_z201 = {
> .name = "LG z201",
> .min = 174000000,
> .max = 862000000,
> @@ -157,7 +157,7 @@ static struct dvb_pll_desc dvb_pll_lg_z201 = {
> },
> };
>
> -static struct dvb_pll_desc dvb_pll_unknown_1 = {
> +static const struct dvb_pll_desc dvb_pll_unknown_1 = {
> .name = "unknown 1", /* used by dntv live dvb-t */
> .min = 174000000,
> .max = 862000000,
> @@ -179,7 +179,7 @@ static struct dvb_pll_desc dvb_pll_unknown_1 = {
> /* Infineon TUA6010XS
> * used in Thomson Cable Tuner
> */
> -static struct dvb_pll_desc dvb_pll_tua6010xs = {
> +static const struct dvb_pll_desc dvb_pll_tua6010xs = {
> .name = "Infineon TUA6010XS",
> .min = 44250000,
> .max = 858000000,
> @@ -193,7 +193,7 @@ static struct dvb_pll_desc dvb_pll_tua6010xs = {
> };
>
> /* Panasonic env57h1xd5 (some Philips PLL ?) */
> -static struct dvb_pll_desc dvb_pll_env57h1xd5 = {
> +static const struct dvb_pll_desc dvb_pll_env57h1xd5 = {
> .name = "Panasonic ENV57H1XD5",
> .min = 44250000,
> .max = 858000000,
> @@ -217,7 +217,7 @@ static void tda665x_bw(struct dvb_frontend *fe, u8 *buf)
> buf[3] |= 0x08;
> }
>
> -static struct dvb_pll_desc dvb_pll_tda665x = {
> +static const struct dvb_pll_desc dvb_pll_tda665x = {
> .name = "Philips TDA6650/TDA6651",
> .min = 44250000,
> .max = 858000000,
> @@ -251,7 +251,7 @@ static void tua6034_bw(struct dvb_frontend *fe, u8 *buf)
> buf[3] |= 0x08;
> }
>
> -static struct dvb_pll_desc dvb_pll_tua6034 = {
> +static const struct dvb_pll_desc dvb_pll_tua6034 = {
> .name = "Infineon TUA6034",
> .min = 44250000,
> .max = 858000000,
> @@ -275,7 +275,7 @@ static void tded4_bw(struct dvb_frontend *fe, u8 *buf)
> buf[3] |= 0x04;
> }
>
> -static struct dvb_pll_desc dvb_pll_tded4 = {
> +static const struct dvb_pll_desc dvb_pll_tded4 = {
> .name = "ALPS TDED4",
> .min = 47000000,
> .max = 863000000,
> @@ -293,7 +293,7 @@ static struct dvb_pll_desc dvb_pll_tded4 = {
> /* ALPS TDHU2
> * used in AverTVHD MCE A180
> */
> -static struct dvb_pll_desc dvb_pll_tdhu2 = {
> +static const struct dvb_pll_desc dvb_pll_tdhu2 = {
> .name = "ALPS TDHU2",
> .min = 54000000,
> .max = 864000000,
> @@ -310,7 +310,7 @@ static struct dvb_pll_desc dvb_pll_tdhu2 = {
> /* Samsung TBMV30111IN / TBMV30712IN1
> * used in Air2PC ATSC - 2nd generation (nxt2002)
> */
> -static struct dvb_pll_desc dvb_pll_samsung_tbmv = {
> +static const struct dvb_pll_desc dvb_pll_samsung_tbmv = {
> .name = "Samsung TBMV30111IN / TBMV30712IN1",
> .min = 54000000,
> .max = 860000000,
> @@ -329,7 +329,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbmv = {
> /*
> * Philips SD1878 Tuner.
> */
> -static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
> +static const struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
> .name = "Philips SD1878",
> .min = 950000,
> .max = 2150000,
> @@ -395,7 +395,7 @@ static void opera1_bw(struct dvb_frontend *fe, u8 *buf)
> return;
> }
>
> -static struct dvb_pll_desc dvb_pll_opera1 = {
> +static const struct dvb_pll_desc dvb_pll_opera1 = {
> .name = "Opera Tuner",
> .min = 900000,
> .max = 2250000,
> @@ -442,7 +442,7 @@ static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf)
> }
>
> /* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */
> -static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
> +static const struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
> .name = "Samsung DTOS403IH102A",
> .min = 44250000,
> .max = 858000000,
> @@ -462,7 +462,7 @@ static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
> };
>
> /* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */
> -static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
> +static const struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
> .name = "Samsung TDTC9251DH0",
> .min = 48000000,
> .max = 863000000,
> @@ -476,7 +476,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
> };
>
> /* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */
> -static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
> +static const struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
> .name = "Samsung TBDU18132",
> .min = 950000,
> .max = 2150000, /* guesses */
> @@ -497,7 +497,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
> };
>
> /* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */
> -static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
> +static const struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
> .name = "Samsung TBMU24112",
> .min = 950000,
> .max = 2150000, /* guesses */
> @@ -518,7 +518,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
> * 153 - 430 0 * 0 0 0 0 1 0 0x02
> * 430 - 822 0 * 0 0 1 0 0 0 0x08
> * 822 - 862 1 * 0 0 1 0 0 0 0x88 */
> -static struct dvb_pll_desc dvb_pll_alps_tdee4 = {
> +static const struct dvb_pll_desc dvb_pll_alps_tdee4 = {
> .name = "ALPS TDEE4",
> .min = 47000000,
> .max = 862000000,
> @@ -534,7 +534,7 @@ static struct dvb_pll_desc dvb_pll_alps_tdee4 = {
>
> /* ----------------------------------------------------------- */
>
> -static struct dvb_pll_desc *pll_list[] = {
> +static const struct dvb_pll_desc *pll_list[] = {
> [DVB_PLL_UNDEFINED] = NULL,
> [DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579,
> [DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x,
> @@ -564,7 +564,7 @@ static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf,
> const u32 frequency)
> {
> struct dvb_pll_priv *priv = fe->tuner_priv;
> - struct dvb_pll_desc *desc = priv->pll_desc;
> + const struct dvb_pll_desc *desc = priv->pll_desc;
> u32 div;
> int i;
>
> @@ -758,7 +758,7 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
> .buf = b1, .len = 1 };
> struct dvb_pll_priv *priv = NULL;
> int ret;
> - struct dvb_pll_desc *desc;
> + const struct dvb_pll_desc *desc;
>
> if ((id[dvb_pll_devcount] > DVB_PLL_UNDEFINED) &&
> (id[dvb_pll_devcount] < ARRAY_SIZE(pll_list)))
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Mauro,
Thanks for reviewing. Sending my reply again, as it looks like I dropped the
CC list on my first reply, and my second reply bounced on the mailing lists :-(
On Wed, 22 Jul 2015, Mauro Carvalho Chehab wrote:
> Em Wed, 24 Jun 2015 16:11:09 +0100
> Peter Griffin <[email protected]> escreveu:
>
> > This patch adds the Kconfig and Makefile for the c8sectpfe driver
> > so it will be built. It also selects additional demodulator and tuners
> > which are required by the supported NIM cards.
> >
> > Signed-off-by: Peter Griffin <[email protected]>
> > ---
> > drivers/media/Kconfig | 1 +
> > drivers/media/Makefile | 1 +
> > drivers/media/tsin/c8sectpfe/Kconfig | 26 ++++++++++++++++++++++++++
> > drivers/media/tsin/c8sectpfe/Makefile | 11 +++++++++++
> > 4 files changed, 39 insertions(+)
> > create mode 100644 drivers/media/tsin/c8sectpfe/Kconfig
> > create mode 100644 drivers/media/tsin/c8sectpfe/Makefile
> >
> > diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
> > index 1570992..82bc1dc 100644
> > --- a/drivers/media/Kconfig
> > +++ b/drivers/media/Kconfig
> > @@ -170,6 +170,7 @@ source "drivers/media/pci/Kconfig"
> > source "drivers/media/platform/Kconfig"
> > source "drivers/media/mmc/Kconfig"
> > source "drivers/media/radio/Kconfig"
> > +source "drivers/media/tsin/c8sectpfe/Kconfig"
> >
> > comment "Supported FireWire (IEEE 1394) Adapters"
> > depends on DVB_CORE && FIREWIRE
> > diff --git a/drivers/media/Makefile b/drivers/media/Makefile
> > index e608bbc..0a567b8 100644
> > --- a/drivers/media/Makefile
> > +++ b/drivers/media/Makefile
> > @@ -29,5 +29,6 @@ obj-y += rc/
> > #
> >
> > obj-y += common/ platform/ pci/ usb/ mmc/ firewire/
> > +obj-$(CONFIG_DVB_C8SECTPFE) += tsin/c8sectpfe/
>
> Hmm... why are you adding it at a new "tsin" directory? We're putting
> those SoC platform drivers under platform/.
I didn't realise that. I will move this under there in the V2 patchset then?
The rationale behind a new 'tsin' directory was that all the current DVB
drivers seemed to be grouped by the underlying bus on which TS
data enters the system (e.g. pci / usb).
As this didn't fit in with that scheme I created a new tsin directory for SoC's
which have dedicated hardware for Transport Stream INput (tsin) into the SoC.
regards,
Peter.
p.s. Mauro - appologies again for spaming you
Hi Mauro,
On Wed, 22 Jul 2015, Mauro Carvalho Chehab wrote:
> Em Wed, 24 Jun 2015 16:11:07 +0100
> Peter Griffin <[email protected]> escreveu:
>
> > This patch adds support for the following 3 NIM cards: -
> > 1) STV0367-NIM (stv0367 demod with Thompson PLL)
> > 2) B2100A (2x stv0367 demods & 2x NXP tda18212 tuners)
> > 3) STV0903-6110NIM (stv0903 demod + 6110 tuner, lnb24)
> >
> > Signed-off-by: Peter Griffin <[email protected]>
> > ---
> > drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c | 296 +++++++++++++++++++++++++++
> > drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h | 20 ++
> > 2 files changed, 316 insertions(+)
> > create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c
> > create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.h
> >
> > diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c
> > new file mode 100644
> > index 0000000..5c4ecb4
> > --- /dev/null
> > +++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-dvb.c
> > @@ -0,0 +1,296 @@
> > +/*
> > + * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver
> > + *
> > + * Copyright (c) STMicroelectronics 2015
> > + *
> > + * Author Peter Griffin <[email protected]>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + *
> > + * GNU General Public License for more details.
> > + */
> > +#include <linux/completion.h>
> > +#include <linux/delay.h>
> > +#include <linux/i2c.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/version.h>
> > +
> > +#include <dt-bindings/media/c8sectpfe.h>
> > +
> > +#include "c8sectpfe-common.h"
> > +#include "c8sectpfe-core.h"
> > +#include "c8sectpfe-dvb.h"
> > +
> > +#include "dvb-pll.h"
> > +#include "lnbh24.h"
> > +#include "stv0367.h"
> > +#include "stv0367_priv.h"
> > +#include "stv6110x.h"
> > +#include "stv090x.h"
> > +#include "tda18212.h"
> > +
> > +static inline const char *dvb_card_str(unsigned int c)
> > +{
> > + switch (c) {
> > + case STV0367_PLL_BOARD_NIMA: return "STV0367_PLL_BOARD_NIMA";
> > + case STV0367_PLL_BOARD_NIMB: return "STV0367_PLL_BOARD_NIMB";
> > + case STV0367_TDA18212_NIMA_1: return "STV0367_TDA18212_NIMA_1";
> > + case STV0367_TDA18212_NIMA_2: return "STV0367_TDA18212_NIMA_2";
> > + case STV0367_TDA18212_NIMB_1: return "STV0367_TDA18212_NIMB_1";
> > + case STV0367_TDA18212_NIMB_2: return "STV0367_TDA18212_NIMB_2";
> > + case STV0903_6110_LNB24_NIMA: return "STV0903_6110_LNB24_NIMA";
> > + case STV0903_6110_LNB24_NIMB: return "STV0903_6110_LNB24_NIMB";
> > + default: return "unknown dvb frontend card";
> > + }
> > +}
> > +
> > +static struct stv090x_config stv090x_config = {
> > + .device = STV0903,
> > + .demod_mode = STV090x_SINGLE,
> > + .clk_mode = STV090x_CLK_EXT,
> > + .xtal = 16000000,
> > + .address = 0x69,
> > +
> > + .ts1_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
> > + .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
> > +
> > + .repeater_level = STV090x_RPTLEVEL_64,
> > +
> > + .tuner_init = NULL,
> > + .tuner_set_mode = NULL,
> > + .tuner_set_frequency = NULL,
> > + .tuner_get_frequency = NULL,
> > + .tuner_set_bandwidth = NULL,
> > + .tuner_get_bandwidth = NULL,
> > + .tuner_set_bbgain = NULL,
> > + .tuner_get_bbgain = NULL,
> > + .tuner_set_refclk = NULL,
> > + .tuner_get_status = NULL,
> > +};
> > +
> > +static struct stv6110x_config stv6110x_config = {
> > + .addr = 0x60,
> > + .refclk = 16000000,
> > +};
> > +
> > +#define NIMA 0
> > +#define NIMB 1
> > +
> > +static struct stv0367_config stv0367_pll_config[] = {
> > + {
> > + .demod_address = 0x1c,
> > + .xtal = 27000000,
> > + .if_khz = 36166,
> > + .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
> > + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
> > + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
> > + }, {
> > + .demod_address = 0x1d,
> > + .xtal = 27000000,
> > + .if_khz = 36166,
> > + .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
> > + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
> > + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
> > + },
> > +};
> > +
> > +static struct stv0367_config stv0367_tda18212_config[] = {
> > + {
> > + .demod_address = 0x1c,
> > + .xtal = 16000000,
> > + .if_khz = 4500,
> > + .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
> > + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
> > + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
> > + }, {
> > + .demod_address = 0x1d,
> > + .xtal = 16000000,
> > + .if_khz = 4500,
> > + .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
> > + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
> > + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
> > + }, {
> > + .demod_address = 0x1e,
> > + .xtal = 16000000,
> > + .if_khz = 4500,
> > + .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
> > + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
> > + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
> > + },
> > +};
> > +
> > +static struct tda18212_config tda18212_conf = {
> > + .if_dvbt_6 = 4150,
> > + .if_dvbt_7 = 4150,
> > + .if_dvbt_8 = 4500,
> > + .if_dvbc = 5000,
> > +};
> > +
> > +int c8sectpfe_frontend_attach(struct dvb_frontend **fe,
> > + struct c8sectpfe *c8sectpfe,
> > + struct channel_info *tsin, int chan_num)
> > +{
> > + struct tda18212_config *tda18212;
> > + struct stv6110x_devctl *fe2;
> > + struct i2c_client *client;
> > + struct i2c_board_info tda18212_info = {
> > + .type = "tda18212",
> > + .addr = 0x60,
> > + };
> > +
> > + BUG_ON(!tsin);
>
> Just return an error if !tsin.
Ok will fix in V2
regards,
Peter.
Hi Mauro,
On Wed, 22 Jul 2015, Mauro Carvalho Chehab wrote:
> Em Wed, 24 Jun 2015 16:11:00 +0100
> Peter Griffin <[email protected]> escreveu:
>
> > This is used in conjunction with the STV0367 demodulator on
> > the STV0367-NIM-V1.0 NIM card which can be used with the STi
> > STB SoC's.
> >
> > This tuner has a fifth register, so some changes have been made
> > to accommodate this.
> >
> > Signed-off-by: Peter Griffin <[email protected]>
> > ---
> > drivers/media/dvb-frontends/dvb-pll.c | 74 +++++++++++++++++++++++++++++------
> > drivers/media/dvb-frontends/dvb-pll.h | 1 +
> > 2 files changed, 64 insertions(+), 11 deletions(-)
> >
> > diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c
> > index 6d8fe88..f7381c7 100644
> > --- a/drivers/media/dvb-frontends/dvb-pll.c
> > +++ b/drivers/media/dvb-frontends/dvb-pll.c
> > @@ -141,6 +141,35 @@ static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
> > },
> > };
> >
> > +static void thomson_dtt7546x_bw(struct dvb_frontend *fe, u8 *buf)
> > +{
> > + /* set CB2 reg - set ATC, XTO */
> > + buf[4] = 0xc3;
> > +}
> > +
> > +static struct dvb_pll_desc dvb_pll_thomson_dtt7546x = {
> > + .name = "Thomson dtt7546x",
> > + .min = 44250000,
> > + .max = 863250000,
> > + .set = thomson_dtt7546x_bw,
> > + .iffreq= 36166667,
>
> Whitespace is missing. Please check the patchs with scripts/checkpatch.pl.
Will fix in V2.
regards,
Peter.
Hi Mauro / Joe,
On Wed, 22 Jul 2015, Mauro Carvalho Chehab wrote:
> Em Wed, 24 Jun 2015 18:17:37 -0700
> Joe Perches <[email protected]> escreveu:
>
> > On Wed, 2015-06-24 at 16:11 +0100, Peter Griffin wrote:
> > > This is used in conjunction with the STV0367 demodulator on
> > > the STV0367-NIM-V1.0 NIM card which can be used with the STi
> > > STB SoC's.
> >
> > Barely associated to this specific patch, but for
> > dvb-pll.c, another thing that seems possible is to
> > convert the struct dvb_pll_desc uses to const and
> > change the "entries" fixed array size from 12 to []
> >
> > It'd save a couple KB overall and remove ~5KB of data.
> >
> > $ size drivers/media/dvb-frontends/dvb-pll.o*
> > text data bss dec hex filename
> > 8520 1552 2120 12192 2fa0 drivers/media/dvb-frontends/dvb-pll.o.new
> > 5624 6363 2120 14107 371b drivers/media/dvb-frontends/dvb-pll.o.old
>
> Peter,
>
> Please add this patch on the next patch series you submit.
Ok will do, I've added this patch with a slightly updated commit message
to my series.
Joe - Can I add your signed-off-by?
regards,
Peter.
On Thu, 2015-07-30 at 10:47 +0100, Peter Griffin wrote:
> Hi Mauro / Joe,
>
> On Wed, 22 Jul 2015, Mauro Carvalho Chehab wrote:
>
> > Em Wed, 24 Jun 2015 18:17:37 -0700
> > Joe Perches <[email protected]> escreveu:
> >
> > > On Wed, 2015-06-24 at 16:11 +0100, Peter Griffin wrote:
> > > > This is used in conjunction with the STV0367 demodulator on
> > > > the STV0367-NIM-V1.0 NIM card which can be used with the STi
> > > > STB SoC's.
> > >
> > > Barely associated to this specific patch, but for
> > > dvb-pll.c, another thing that seems possible is to
> > > convert the struct dvb_pll_desc uses to const and
> > > change the "entries" fixed array size from 12 to []
> > >
> > > It'd save a couple KB overall and remove ~5KB of data.
> > >
> > > $ size drivers/media/dvb-frontends/dvb-pll.o*
> > > text data bss dec hex filename
> > > 8520 1552 2120 12192 2fa0 drivers/media/dvb-frontends/dvb-pll.o.new
> > > 5624 6363 2120 14107 371b drivers/media/dvb-frontends/dvb-pll.o.old
> >
> > Peter,
> >
> > Please add this patch on the next patch series you submit.
>
> Ok will do, I've added this patch with a slightly updated commit message
> to my series.
>
> Joe - Can I add your signed-off-by?
Signed-off-by: Joe Perches <[email protected]>
Hi Mauro,
Thanks for reviewing.
On Wed, 22 Jul 2015, Mauro Carvalho Chehab wrote:
> Em Wed, 24 Jun 2015 16:11:05 +0100
> Peter Griffin <[email protected]> escreveu:
>
> > This patch adds support for the c8sectpfe input HW found on
> > STiH407/410 SoC's.
> >
> > It currently supports the TS input block, memdma engine
> > and hw PID filtering blocks of the C8SECTPFE subsystem.
> >
> > The driver creates one LinuxDVB adapter, and a
> > demux/dvr/frontend set of devices for each tsin channel
> > which is specificed in the DT. It has been tested with
> > multiple tsin channels tuned, locked, and grabbing TS
> > simultaneously.
> >
> > Signed-off-by: Peter Griffin <[email protected]>
> > ---
> > drivers/media/tsin/c8sectpfe/c8sectpfe-core.c | 1105 +++++++++++++++++++++++++
> > drivers/media/tsin/c8sectpfe/c8sectpfe-core.h | 288 +++++++
> > 2 files changed, 1393 insertions(+)
> > create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-core.c
> > create mode 100644 drivers/media/tsin/c8sectpfe/c8sectpfe-core.h
> >
> > diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-core.c b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.c
> > new file mode 100644
> > index 0000000..fbbe323
> > --- /dev/null
> > +++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.c
> > @@ -0,0 +1,1105 @@
> > +/*
> > + * c8sectpfe-core.c - C8SECTPFE STi DVB driver
> > + *
> > + * Copyright (c) STMicroelectronics 2015
> > + *
> > + * Author:Peter Bennett <[email protected]>
> > + * Peter Griffin <[email protected]>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License as
> > + * published by the Free Software Foundation; either version 2 of
> > + * the License, or (at your option) any later version.
> > + */
> > +#include <linux/clk.h>
> > +#include <linux/completion.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/dvb/dmx.h>
> > +#include <linux/dvb/frontend.h>
> > +#include <linux/errno.h>
> > +#include <linux/firmware.h>
> > +#include <linux/init.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of_gpio.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/usb.h>
> > +#include <linux/slab.h>
> > +#include <linux/time.h>
> > +#include <linux/version.h>
> > +#include <linux/wait.h>
> > +
> > +#include "c8sectpfe-core.h"
> > +#include "c8sectpfe-common.h"
> > +#include "c8sectpfe-debugfs.h"
> > +#include "dmxdev.h"
> > +#include "dvb_demux.h"
> > +#include "dvb_frontend.h"
> > +#include "dvb_net.h"
> > +
> > +#define FIRMWARE_MEMDMA "pti_memdma_h407.elf"
> > +MODULE_FIRMWARE(FIRMWARE_MEMDMA);
> > +
> > +#define PID_TABLE_SIZE 1024
> > +#define POLL_20_HZ_DIV 20 /* poll at 20 Hz */
> > +
> > +static int load_slim_core_fw(struct c8sectpfei *fei);
> > +
> > +#define TS_PKT_SIZE 188
> > +#define HEADER_SIZE (4)
> > +#define PACKET_SIZE (TS_PKT_SIZE+HEADER_SIZE)
> > +
> > +#define FEI_ALIGNMENT (32)
> > +/* hw requires minimum of 8*PACKET_SIZE and padded to 8byte boundary */
> > +#define FEI_BUFFER_SIZE (8*PACKET_SIZE*340)
> > +
> > +#define FIFO_LEN 1024
> > +
> > +static void c8sectpfe_timer_interrupt(unsigned long ac8sectpfei)
> > +{
> > + struct c8sectpfei *fei = (struct c8sectpfei *)ac8sectpfei;
> > + struct channel_info *channel;
> > + int chan_num;
> > +
> > + /* iterate through input block channels */
> > + for (chan_num = 0; chan_num < fei->tsin_count; chan_num++) {
> > + channel = fei->channel_data[chan_num];
> > +
> > + /* is this descriptor initialised and TP enabled */
> > + if (channel->irec && readl((void __iomem *)
> > + &channel->irec->tp_enable))
> > + tasklet_schedule(&channel->tsklet);
> > + }
> > +
> > + fei->timer.expires = jiffies + (HZ / POLL_20_HZ_DIV);
>
> Please use the macros for jiffies conversions. In this case, I guess
> you want to use ms_to_jiffies(), right?
Fixed in V2.
>
> > + add_timer(&fei->timer);
> > +}
> > +
> > +static void channel_swdemux_tsklet(unsigned long data)
> > +{
> > + struct channel_info *channel = (struct channel_info *)data;
> > + struct c8sectpfei *fei = channel->fei;
> > + struct tpentry *ptrblk;
> > + unsigned long wp, rp;
> > + int pos, num_packets, n, size;
> > + u8 *buf;
> > +
> > + BUG_ON(!channel);
> > + BUG_ON(!channel->irec);
>
> Please avoid using BUG_ON() except when the machine will be on some
> unstable state. In this case, I guess you could just do:
>
> if (unlikely(!channel || !channel->irec)
> return;
Yes ok, I have removed the BUG_ON's in v2
>
> > +
> > + ptrblk = &channel->irec->ptr_data[0];
> > +
> > + wp = readl((void __iomem *)&ptrblk->dma_bus_wp);
> > + rp = readl((void __iomem *)&ptrblk->dma_bus_rp);
>
> Why do you need those typecasts? We try to avoid typecasts in the Kernel,
> doing it only where really needed. Same for other usages. You should
> probably declare those DMA buffers as __iomem *, and avoid the casts.
Ok, I've fixed this in V2.
>
> > +
> > + pos = rp - channel->back_buffer_busaddr;
> > +
> > + /* has it wrapped */
> > + if (wp < rp)
> > + wp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE;
> > +
> > + size = wp - rp;
> > + num_packets = size / PACKET_SIZE;
> > +
> > + /* manage cache so data is visible to CPU */
> > + dma_sync_single_for_cpu(fei->dev,
> > + rp,
> > + size,
> > + DMA_FROM_DEVICE);
> > +
> > + buf = (u8 *) channel->back_buffer_aligned;
> > +
> > + dev_dbg(fei->dev,
> > + "chan=%d channel=%p num_packets = %d, buf = %p, pos = 0x%x\n\t"
> > + "rp=0x%lx, wp=0x%lx\n",
> > + channel->tsin_id, channel, num_packets, buf, pos, rp, wp);
> > +
> > + for (n = 0; n < num_packets; n++) {
> > + dvb_dmx_swfilter_packets(
> > + &fei->c8sectpfe[0]->
> > + demux[channel->demux_mapping].dvb_demux,
> > + &buf[pos], 1);
> > +
> > + pos += PACKET_SIZE;
> > + }
>
> Hmm... why not call it, instead, without the loop, e. g.:
>
> dvb_dmx_swfilter_packets(
> &fei->c8sectpfe[0]->
> demux[channel->demux_mapping].dvb_demux,
> &buf[pos], num_packets);
The InputBlock hardware outputs in multiples of 8 bytes, so we are actually
incrementing by 192 bytes in this loop, for each 188 byte TS packet.
These additional bytes can be used for timestamping TS packets on arrival
and then using this for clock recovery, although that isn't enabled atm.
I notice in dvb_demux.c you have a dvb_dmx_swfilter_204 as well as
dvb_dmx_swfilter, so we could maybe add a dvb_dmx_swfilter_192?
Although I thought this was most likely a ST specific thing, so didn't
change the core code, and handled it within the driver.
>
>
> > +
> > + /* advance the read pointer */
> > + if (wp == (channel->back_buffer_busaddr + FEI_BUFFER_SIZE))
> > + writel(channel->back_buffer_busaddr,
> > + (void __iomem *)&ptrblk->dma_bus_rp);
> > + else
> > + writel(wp, (void __iomem *)&ptrblk->dma_bus_rp);
> > +}
> > +
> > +static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed)
> > +{
> > + struct dvb_demux *demux = dvbdmxfeed->demux;
> > + struct stdemux *stdemux = (struct stdemux *)demux->priv;
> > + struct c8sectpfei *fei = stdemux->c8sectpfei;
> > + struct channel_info *channel;
> > + struct tpentry *ptrblk;
> > + u32 tmp;
> > + unsigned long *bitmap;
> > +
> > + switch (dvbdmxfeed->type) {
> > + case DMX_TYPE_TS:
> > + break;
> > + case DMX_TYPE_SEC:
> > + break;
> > + default:
> > + dev_err(fei->dev, "%s:%d Error bailing\n"
> > + , __func__, __LINE__);
> > + return -EINVAL;
> > + }
> > +
> > + if (dvbdmxfeed->type == DMX_TYPE_TS) {
> > + switch (dvbdmxfeed->pes_type) {
> > + case DMX_PES_VIDEO:
> > + case DMX_PES_AUDIO:
> > + case DMX_PES_TELETEXT:
> > + case DMX_PES_PCR:
> > + case DMX_PES_OTHER:
> > + break;
> > + default:
> > + dev_err(fei->dev, "%s:%d Error bailing\n"
> > + , __func__, __LINE__);
> > + return -EINVAL;
> > + }
> > + }
> > +
> > + mutex_lock(&fei->lock);
> > +
> > + channel = fei->channel_data[stdemux->tsin_index];
> > +
> > + bitmap = (unsigned long *) channel->pid_buffer_aligned;
> > +
> > + bitmap_set(bitmap, dvbdmxfeed->pid, 1);
> > +
> > + /* manage cache so PID bitmap is visible to HW */
> > + dma_sync_single_for_device(fei->dev,
> > + channel->pid_buffer_busaddr,
> > + PID_TABLE_SIZE,
> > + DMA_TO_DEVICE);
> > +
> > + channel->active = 1;
> > +
> > + if (fei->global_feed_count == 0) {
> > + fei->timer.expires = jiffies + (HZ / POLL_20_HZ_DIV);
>
> Please use ms_to_jiffies().
Fixed in v2.
>
> > + add_timer(&fei->timer);
> > + }
> > +
> > + if (stdemux->running_feed_count == 0) {
> > +
> > + dev_dbg(fei->dev, "Starting channel=%p\n", channel);
> > +
> > + tasklet_init(&channel->tsklet, channel_swdemux_tsklet,
> > + (unsigned long) channel);
> > +
> > + /* Reset the internal inputblock sram pointers */
> > + writel(channel->fifo,
> > + fei->io + C8SECTPFE_IB_BUFF_STRT(channel->tsin_id));
> > + writel(channel->fifo + FIFO_LEN - 1,
> > + fei->io + C8SECTPFE_IB_BUFF_END(channel->tsin_id));
> > +
> > + writel(channel->fifo,
> > + fei->io + C8SECTPFE_IB_READ_PNT(channel->tsin_id));
> > + writel(channel->fifo,
> > + fei->io + C8SECTPFE_IB_WRT_PNT(channel->tsin_id));
> > +
> > +
> > + /* reset read / write memdma ptrs for this channel */
> > + ptrblk = &channel->irec->ptr_data[0];
> > + writel(channel->back_buffer_busaddr,
> > + (void __iomem *)&ptrblk->dma_busbase);
> > +
> > + tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
> > + writel(tmp, (void __iomem *)&ptrblk->dma_bustop);
> > +
> > + writel(channel->back_buffer_busaddr,
> > + (void __iomem *)&ptrblk->dma_bus_wp);
> > +
> > + /* Issue a reset and enable InputBlock */
> > + writel(C8SECTPFE_SYS_ENABLE | C8SECTPFE_SYS_RESET
> > + , fei->io + C8SECTPFE_IB_SYS(channel->tsin_id));
> > +
> > + /* and enable the tp */
> > + writel(0x1, (void __iomem *)&channel->irec->tp_enable);
> > +
> > + dev_dbg(fei->dev, "%s:%d Starting DMA feed on stdemux=%p\n"
> > + , __func__, __LINE__, stdemux);
> > + }
> > +
> > + stdemux->running_feed_count++;
> > + fei->global_feed_count++;
> > +
> > + mutex_unlock(&fei->lock);
> > +
> > + return 0;
> > +}
> > +
> > +static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
> > +{
> > +
> > + struct dvb_demux *demux = dvbdmxfeed->demux;
> > + struct stdemux *stdemux = (struct stdemux *)demux->priv;
> > + struct c8sectpfei *fei = stdemux->c8sectpfei;
> > + struct channel_info *channel;
> > + struct tpentry *ptrblk;
> > + int idlereq;
> > + u32 tmp;
> > + int ret;
> > + unsigned long *bitmap;
> > +
> > + mutex_lock(&fei->lock);
> > +
> > + channel = fei->channel_data[stdemux->tsin_index];
> > +
> > + bitmap = (unsigned long *) channel->pid_buffer_aligned;
> > + bitmap_clear(bitmap, dvbdmxfeed->pid, 1);
> > +
> > + /* manage cache so data is visible to HW */
> > + dma_sync_single_for_device(fei->dev,
> > + channel->pid_buffer_busaddr,
> > + PID_TABLE_SIZE,
> > + DMA_TO_DEVICE);
> > +
> > + if (--stdemux->running_feed_count == 0) {
> > +
> > + channel = fei->channel_data[stdemux->tsin_index];
> > + ptrblk = &channel->irec->ptr_data[0];
> > +
> > + /* TP re-configuration on page 168 */
>
> Page 168 of what?
functional spec. I've updated the comment in V2.
>
> > +
> > + /* disable IB (prevents more TS data going to memdma) */
> > + writel(0, fei->io + C8SECTPFE_IB_SYS(channel->tsin_id));
> > +
> > + /* disable this channels descriptor */
> > + writel(0, (void __iomem *)&channel->irec->tp_enable);
> > +
> > + tasklet_disable(&channel->tsklet);
> > +
> > + /* now request memdma channel goes idle */
> > + idlereq = (1 << channel->tsin_id) | IDLEREQ;
> > + writel(idlereq, fei->io + DMA_IDLE_REQ);
> > +
> > + /* wait for idle irq handler to signal completion */
> > + ret = wait_for_completion_timeout(&channel->idle_completion,
> > + msecs_to_jiffies(100));
> > +
> > + if (ret == 0)
> > + dev_warn(fei->dev,
> > + "Timeout waiting for idle irq on tsin%d\n",
> > + channel->tsin_id);
> > +
> > + reinit_completion(&channel->idle_completion);
> > +
> > + /* reset read / write ptrs for this channel */
> > + writel(channel->back_buffer_busaddr,
> > + (void __iomem *)&ptrblk->dma_busbase);
> > +
> > + tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
> > + writel(tmp, (void __iomem *)&ptrblk->dma_bustop);
> > +
> > + writel(channel->back_buffer_busaddr,
> > + (void __iomem *)&ptrblk->dma_bus_wp);
> > +
> > + dev_dbg(fei->dev,
> > + "%s:%d stopping DMA feed on stdemux=%p channel=%d\n",
> > + __func__, __LINE__, stdemux, channel->tsin_id);
> > +
> > + /* turn off all PIDS in the bitmap*/
> > + memset((void *)channel->pid_buffer_aligned
> > + , 0x00, PID_TABLE_SIZE);
> > +
> > + /* manage cache so data is visible to HW */
> > + dma_sync_single_for_device(fei->dev,
> > + channel->pid_buffer_busaddr,
> > + PID_TABLE_SIZE,
> > + DMA_TO_DEVICE);
> > +
> > + channel->active = 0;
> > + }
> > +
> > + if (--fei->global_feed_count == 0) {
> > + dev_dbg(fei->dev, "%s:%d global_feed_count=%d\n"
> > + , __func__, __LINE__, fei->global_feed_count);
> > +
> > + del_timer(&fei->timer);
> > + }
> > +
> > + mutex_unlock(&fei->lock);
> > +
> > + return 0;
> > +}
> > +
> > +static struct channel_info *find_channel(struct c8sectpfei *fei, int tsin_num)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < C8SECTPFE_MAXCHANNEL; i++) {
> > + if (!fei->channel_data[i])
> > + continue;
> > +
> > + if (fei->channel_data[i]->tsin_id == tsin_num)
> > + return fei->channel_data[i];
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +static void c8sectpfe_getconfig(struct c8sectpfei *fei)
> > +{
> > + struct c8sectpfe_hw *hw = &fei->hw_stats;
> > +
> > + hw->num_ib = readl(fei->io + SYS_CFG_NUM_IB);
> > + hw->num_mib = readl(fei->io + SYS_CFG_NUM_MIB);
> > + hw->num_swts = readl(fei->io + SYS_CFG_NUM_SWTS);
> > + hw->num_tsout = readl(fei->io + SYS_CFG_NUM_TSOUT);
> > + hw->num_ccsc = readl(fei->io + SYS_CFG_NUM_CCSC);
> > + hw->num_ram = readl(fei->io + SYS_CFG_NUM_RAM);
> > + hw->num_tp = readl(fei->io + SYS_CFG_NUM_TP);
> > +
> > + dev_info(fei->dev, "C8SECTPFE hw supports the following:\n");
> > + dev_info(fei->dev, "Input Blocks: %d\n", hw->num_ib);
> > + dev_info(fei->dev, "Merged Input Blocks: %d\n", hw->num_mib);
> > + dev_info(fei->dev, "Software Transport Stream Inputs: %d\n"
> > + , hw->num_swts);
> > + dev_info(fei->dev, "Transport Stream Output: %d\n", hw->num_tsout);
> > + dev_info(fei->dev, "Cable Card Converter: %d\n", hw->num_ccsc);
> > + dev_info(fei->dev, "RAMs supported by C8SECTPFE: %d\n", hw->num_ram);
> > + dev_info(fei->dev, "Tango TPs supported by C8SECTPFE: %d\n"
> > + , hw->num_tp);
> > +}
> > +
> > +static irqreturn_t c8sectpfe_idle_irq_handler(int irq, void *priv)
> > +{
> > + struct c8sectpfei *fei = priv;
> > + struct channel_info *chan;
> > + int bit;
> > + unsigned long tmp = readl(fei->io + DMA_IDLE_REQ);
> > +
> > + /* page 168 of functional spec: Clear the idle request
> > + by writing 0 to the C8SECTPFE_DMA_IDLE_REQ register. */
> > +
> > + /* signal idle completion */
> > + for_each_set_bit(bit, &tmp, fei->hw_stats.num_ib) {
> > +
> > + chan = find_channel(fei, bit);
> > +
> > + if (chan)
> > + complete(&chan->idle_completion);
> > + }
> > +
> > + writel(0, fei->io + DMA_IDLE_REQ);
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +
> > +static void free_input_block(struct c8sectpfei *fei, struct channel_info *tsin)
> > +{
> > + BUG_ON(!fei);
> > + BUG_ON(!tsin);
>
> Again, BUG_ON() don't make much sense here. Just return if those vars are
> NULL.
I've removed the BUG_ON in V2
>
> > +
> > + if (tsin->back_buffer_busaddr)
> > + if (!dma_mapping_error(fei->dev, tsin->back_buffer_busaddr))
> > + dma_unmap_single(fei->dev, tsin->back_buffer_busaddr,
> > + FEI_BUFFER_SIZE, DMA_BIDIRECTIONAL);
> > +
> > + kfree(tsin->back_buffer_start);
> > +
> > + if (tsin->pid_buffer_busaddr)
> > + if (!dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr))
> > + dma_unmap_single(fei->dev, tsin->pid_buffer_busaddr,
> > + PID_TABLE_SIZE, DMA_BIDIRECTIONAL);
> > +
> > + kfree(tsin->pid_buffer_start);
> > +}
> > +
> > +#define MAX_NAME 20
> > +
> > +static int configure_input_block(struct c8sectpfei *fei,
> > + struct channel_info *tsin)
> > +{
> > + int ret;
> > + u32 tmp = 0;
> > + struct tpentry *ptrblk;
> > + char tsin_pin_name[MAX_NAME];
> > +
> > + BUG_ON(!fei);
> > + BUG_ON(!tsin);
>
> Same here: BUG_ON() seems too severe.
Removed in V2.
>
> > +
> > + dev_dbg(fei->dev, "%s:%d Configuring channel=%p tsin=%d\n"
> > + , __func__, __LINE__, tsin, tsin->tsin_id);
> > +
> > + init_completion(&tsin->idle_completion);
> > +
> > + tsin->back_buffer_start = kzalloc(FEI_BUFFER_SIZE +
> > + FEI_ALIGNMENT, GFP_KERNEL);
> > +
> > + if (!tsin->back_buffer_start) {
> > + ret = -ENOMEM;
> > + goto err_unmap;
> > + }
> > +
> > + /* Ensure backbuffer is 32byte aligned */
> > + tsin->back_buffer_aligned = tsin->back_buffer_start
> > + + FEI_ALIGNMENT;
> > +
> > + tsin->back_buffer_aligned = (void *)
> > + (((uintptr_t) tsin->back_buffer_aligned) & ~0x1F);
> > +
> > + tsin->back_buffer_busaddr = dma_map_single(fei->dev,
> > + (void *)tsin->back_buffer_aligned,
> > + FEI_BUFFER_SIZE,
> > + DMA_BIDIRECTIONAL);
> > +
> > + if (dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) {
> > + dev_err(fei->dev, "failed to map back_buffer\n");
> > + ret = -EFAULT;
> > + goto err_unmap;
> > + }
> > +
> > + /*
> > + * The pid buffer can be configured (in hw) for byte or bit
> > + * per pid. By powers of deduction we conclude stih407 family
> > + * is configured (at SoC design stage) for bit per pid.
> > + */
> > + tsin->pid_buffer_start = kzalloc(2048, GFP_KERNEL);
> > +
> > + if (!tsin->pid_buffer_start) {
> > + ret = -ENOMEM;
> > + goto err_unmap;
> > + }
> > +
> > + /*
> > + * PID buffer needs to be aligned to size of the pid table
> > + * which at bit per pid is 1024 bytes (8192 pids / 8).
> > + * PIDF_BASE register enforces this alignment when writing
> > + * the register.
> > + */
> > +
> > + tsin->pid_buffer_aligned = tsin->pid_buffer_start +
> > + PID_TABLE_SIZE;
> > +
> > + tsin->pid_buffer_aligned = (void *)
> > + (((uintptr_t) tsin->pid_buffer_aligned) & ~0x3ff);
> > +
> > + tsin->pid_buffer_busaddr = dma_map_single(fei->dev,
> > + tsin->pid_buffer_aligned,
> > + PID_TABLE_SIZE,
> > + DMA_BIDIRECTIONAL);
> > +
> > + if (dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) {
> > + dev_err(fei->dev, "failed to map pid_bitmap\n");
> > + ret = -EFAULT;
> > + goto err_unmap;
> > + }
> > +
> > + /* manage cache so pid bitmap is visible to HW */
> > + dma_sync_single_for_device(fei->dev,
> > + tsin->pid_buffer_busaddr,
> > + PID_TABLE_SIZE,
> > + DMA_TO_DEVICE);
> > +
> > + snprintf(tsin_pin_name, MAX_NAME, "tsin%d-%s", tsin->tsin_id,
> > + (tsin->serial_not_parallel ? "serial" : "parallel"));
> > +
> > + tsin->pstate = pinctrl_lookup_state(fei->pinctrl, tsin_pin_name);
> > + if (IS_ERR(tsin->pstate)) {
> > + dev_err(fei->dev, "%s: pinctrl_lookup_state couldn't find %s state\n"
> > + , __func__, tsin_pin_name);
> > + ret = PTR_ERR(tsin->pstate);
> > + goto err_unmap;
> > + }
> > +
> > + ret = pinctrl_select_state(fei->pinctrl, tsin->pstate);
> > +
> > + if (ret) {
> > + dev_err(fei->dev, "%s: pinctrl_select_state failed\n"
> > + , __func__);
> > + goto err_unmap;
> > + }
> > +
> > + /* Enable this input block */
> > + tmp = readl(fei->io + SYS_INPUT_CLKEN);
> > + tmp |= BIT(tsin->tsin_id);
> > + writel(tmp, fei->io + SYS_INPUT_CLKEN);
> > +
> > + if (tsin->serial_not_parallel)
> > + tmp |= C8SECTPFE_SERIAL_NOT_PARALLEL;
> > +
> > + if (tsin->invert_ts_clk)
> > + tmp |= C8SECTPFE_INVERT_TSCLK;
> > +
> > + if (tsin->async_not_sync)
> > + tmp |= C8SECTPFE_ASYNC_NOT_SYNC;
> > +
> > + tmp |= C8SECTPFE_ALIGN_BYTE_SOP | C8SECTPFE_BYTE_ENDIANNESS_MSB;
> > +
> > + writel(tmp, fei->io + C8SECTPFE_IB_IP_FMT_CFG(tsin->tsin_id));
> > +
> > + writel(C8SECTPFE_SYNC(0x9) |
> > + C8SECTPFE_DROP(0x9) |
> > + C8SECTPFE_TOKEN(0x47),
> > + fei->io + C8SECTPFE_IB_SYNCLCKDRP_CFG(tsin->tsin_id));
> > +
> > + writel(TS_PKT_SIZE, fei->io + C8SECTPFE_IB_PKT_LEN(tsin->tsin_id));
> > +
> > + /* Place the FIFO's at the end of the irec descriptors */
> > +
> > + tsin->fifo = (tsin->tsin_id * FIFO_LEN);
> > +
> > + writel(tsin->fifo, fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id));
> > + writel(tsin->fifo + FIFO_LEN - 1,
> > + fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id));
> > +
> > + writel(tsin->fifo, fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id));
> > + writel(tsin->fifo, fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id));
> > +
> > + writel(tsin->pid_buffer_busaddr,
> > + fei->io + PIDF_BASE(tsin->tsin_id));
> > +
> > + dev_dbg(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=0x%x\n",
> > + tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)),
> > + tsin->pid_buffer_busaddr);
> > +
> > + /* Configure and enable HW PID filtering */
> > +
> > + /*
> > + * The PID value is created by assembling the first 8 bytes of
> > + * the TS packet into a 64-bit word in big-endian format. A
> > + * slice of that 64-bit word is taken from
> > + * (PID_OFFSET+PID_NUM_BITS-1) to PID_OFFSET.
> > + */
> > + tmp = (C8SECTPFE_PID_ENABLE | C8SECTPFE_PID_NUMBITS(13)
> > + | C8SECTPFE_PID_OFFSET(40));
> > +
> > + writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(tsin->tsin_id));
> > +
> > + dev_dbg(fei->dev, "chan=%d setting wp: %d, rp: %d, buf: %d-%d\n",
> > + tsin->tsin_id,
> > + readl(fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)),
> > + readl(fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)),
> > + readl(fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)),
> > + readl(fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id)));
> > +
> > + /*
> > + * Base address of pointer record block relative to
> > + * base address of DMEM
> > + */
> > + fei->irec = fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET +
> > + readl(fei->io + DMA_PTRREC_BASE);
> > +
> > + tsin->irec = fei->irec += tsin->tsin_id;
> > +
> > + ptrblk = &tsin->irec->ptr_data[0];
> > +
> > + writel(tsin->fifo,
> > + (void __iomem *)&tsin->irec->dma_membase);
> > +
> > + writel(tsin->fifo + FIFO_LEN - 1,
> > + (void __iomem *)&tsin->irec->dma_memtop);
> > +
> > + writel((188 + 7)&~7,
> > + (void __iomem *)&tsin->irec->dma_ts_pktsize);
> > +
> > + writel(0x1, (void __iomem *)&tsin->irec->tp_enable);
> > +
> > + /* read/write pointers with physical bus address */
> > + writel(tsin->back_buffer_busaddr,
> > + (void __iomem *)&ptrblk->dma_busbase);
> > +
> > + tmp = tsin->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
> > + writel(tmp, (void __iomem *)&ptrblk->dma_bustop);
> > +
> > + writel(tsin->back_buffer_busaddr,
> > + (void __iomem *)&ptrblk->dma_bus_wp);
> > +
> > + writel(tsin->back_buffer_busaddr,
> > + (void __iomem *)&ptrblk->dma_bus_rp);
> > +
> > + /* initialize tasklet */
> > + tasklet_init(&tsin->tsklet, channel_swdemux_tsklet,
> > + (unsigned long) tsin);
> > +
> > + return 0;
> > +
> > +err_unmap:
> > + free_input_block(fei, tsin);
> > + return ret;
> > +}
> > +
> > +static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv)
> > +{
> > + struct c8sectpfei *fei = priv;
> > +
> > + dev_err(fei->dev, "%s: error handling not yet implemented\n"
> > + , __func__);
> > +
> > + /*
> > + * TODO FIXME we should detect some error conditions here
> > + * and ideally so something about them!
> > + */
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int c8sectpfe_probe(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct device_node *child, *np = dev->of_node;
> > + struct c8sectpfei *fei;
> > + struct resource *res;
> > + int ret, n;
> > + struct channel_info *tsin;
> > +
> > + /* Allocate the c8sectpfei structure */
> > + fei = devm_kzalloc(dev, sizeof(struct c8sectpfei), GFP_KERNEL);
> > + if (!fei)
> > + return -ENOMEM;
> > +
> > + fei->dev = dev;
> > +
> > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "c8sectpfe");
> > + fei->io = devm_ioremap_resource(dev, res);
> > + if (IS_ERR(fei->io))
> > + return PTR_ERR(fei->io);
> > +
> > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> > + "c8sectpfe-ram");
> > + fei->sram = devm_ioremap_resource(dev, res);
> > + if (IS_ERR(fei->sram))
> > + return PTR_ERR(fei->sram);
> > +
> > + fei->sram_size = res->end - res->start;
> > +
> > + fei->idle_irq = platform_get_irq_byname(pdev, "c8sectpfe-idle-irq");
> > + if (fei->idle_irq < 0) {
> > + dev_err(dev, "Can't get c8sectpfe-idle-irq\n");
> > + return fei->idle_irq;
> > + }
> > +
> > + fei->error_irq = platform_get_irq_byname(pdev, "c8sectpfe-error-irq");
> > + if (fei->error_irq < 0) {
> > + dev_err(dev, "Can't get c8sectpfe-error-irq\n");
> > + return fei->error_irq;
> > + }
> > +
> > + platform_set_drvdata(pdev, fei);
> > +
> > + fei->c8sectpfeclk = devm_clk_get(dev, "c8sectpfe");
> > + if (IS_ERR(fei->c8sectpfeclk)) {
> > + dev_err(dev, "c8sectpfe clk not found\n");
> > + return PTR_ERR(fei->c8sectpfeclk);
> > + }
> > +
> > + ret = clk_prepare_enable(fei->c8sectpfeclk);
> > + if (ret) {
> > + dev_err(dev, "Failed to enable c8sectpfe clock\n");
> > + return ret;
> > + }
> > +
> > + /* to save power disable all IP's (on by default) */
> > + writel(0, fei->io + SYS_INPUT_CLKEN);
> > +
> > + /* Enable memdma clock */
> > + writel(MEMDMAENABLE, fei->io + SYS_OTHER_CLKEN);
> > +
> > + /* clear internal sram */
> > + memset_io(fei->sram, 0x0, fei->sram_size);
> > +
> > + c8sectpfe_getconfig(fei);
> > +
> > + ret = load_slim_core_fw(fei);
> > + if (ret) {
> > + dev_err(dev, "Couldn't load slim core firmware\n");
> > + goto err_clk_disable;
> > + }
> > +
> > + ret = devm_request_irq(dev, fei->idle_irq, c8sectpfe_idle_irq_handler,
> > + 0, "c8sectpfe-idle-irq", fei);
> > + if (ret) {
> > + dev_err(dev, "Can't register c8sectpfe-idle-irq IRQ.\n");
> > + goto err_clk_disable;
> > + }
> > +
> > + ret = devm_request_irq(dev, fei->error_irq,
> > + c8sectpfe_error_irq_handler, 0,
> > + "c8sectpfe-error-irq", fei);
> > + if (ret) {
> > + dev_err(dev, "Can't register c8sectpfe-error-irq IRQ.\n");
> > + goto err_clk_disable;
> > + }
> > +
> > + fei->tsin_count = of_get_child_count(np);
> > +
> > + if (fei->tsin_count > C8SECTPFE_MAX_TSIN_CHAN ||
> > + fei->tsin_count > fei->hw_stats.num_ib) {
> > +
> > + dev_err(dev, "More tsin declared than exist on SoC!\n");
> > + ret = -EINVAL;
> > + goto err_clk_disable;
> > + }
> > +
> > + fei->pinctrl = devm_pinctrl_get(dev);
> > +
> > + if (IS_ERR(fei->pinctrl)) {
> > + dev_err(dev, "Error getting tsin pins\n");
> > + ret = PTR_ERR(fei->pinctrl);
> > + goto err_clk_disable;
> > + }
> > +
> > + n = 0;
> > + for_each_child_of_node(np, child) {
> > + struct device_node *i2c_bus;
> > +
> > + fei->channel_data[n] = devm_kzalloc(dev,
> > + sizeof(struct channel_info),
> > + GFP_KERNEL);
> > +
> > + if (!fei->channel_data[n]) {
> > + ret = -ENOMEM;
> > + goto err_clk_disable;
> > + }
> > +
> > + tsin = fei->channel_data[n];
> > +
> > + tsin->fei = fei;
> > +
> > + ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id);
> > + if (ret) {
> > + dev_err(&pdev->dev, "No tsin_num found\n");
> > + goto err_clk_disable;
> > + }
> > +
> > + /* sanity check value */
> > + if (tsin->tsin_id > fei->hw_stats.num_ib) {
> > + dev_err(&pdev->dev,
> > + "tsin-num %d specified greater than number\n\t"
> > + "of input block hw in SoC! (%d)",
> > + tsin->tsin_id, fei->hw_stats.num_ib);
> > + ret = -EINVAL;
> > + goto err_clk_disable;
> > + }
> > +
> > + tsin->invert_ts_clk = of_property_read_bool(child,
> > + "invert-ts-clk");
> > +
> > + tsin->serial_not_parallel = of_property_read_bool(child,
> > + "serial-not-parallel");
> > +
> > + tsin->async_not_sync = of_property_read_bool(child,
> > + "async-not-sync");
> > +
> > + ret = of_property_read_u32(child, "dvb-card",
> > + &tsin->dvb_card);
> > + if (ret) {
> > + dev_err(&pdev->dev, "No dvb-card found\n");
> > + goto err_clk_disable;
> > + }
> > +
> > + i2c_bus = of_parse_phandle(child, "i2c-bus", 0);
> > + if (!i2c_bus) {
> > + dev_err(&pdev->dev, "No i2c-bus found\n");
> > + goto err_clk_disable;
> > + }
> > + tsin->i2c_adapter =
> > + of_find_i2c_adapter_by_node(i2c_bus);
> > + if (!tsin->i2c_adapter) {
> > + dev_err(&pdev->dev, "No i2c adapter found\n");
> > + of_node_put(i2c_bus);
> > + goto err_clk_disable;
> > + }
> > + of_node_put(i2c_bus);
> > +
> > + tsin->rst_gpio = of_get_named_gpio(child, "rst-gpio", 0);
> > +
> > + ret = gpio_is_valid(tsin->rst_gpio);
> > + if (!ret) {
> > + dev_err(dev,
> > + "reset gpio for tsin%d not valid (gpio=%d)\n",
> > + tsin->tsin_id, tsin->rst_gpio);
> > + goto err_clk_disable;
> > + }
> > +
> > + ret = devm_gpio_request_one(dev, tsin->rst_gpio,
> > + GPIOF_OUT_INIT_LOW, "NIM reset");
> > + if (ret && ret != -EBUSY) {
> > + dev_err(dev, "Can't request tsin%d reset gpio\n"
> > + , fei->channel_data[n]->tsin_id);
> > + goto err_clk_disable;
> > + }
> > +
> > + if (!ret) {
> > + /* toggle reset lines */
> > + gpio_direction_output(tsin->rst_gpio, 0);
> > + msleep(1);
> > + gpio_direction_output(tsin->rst_gpio, 1);
> > + msleep(1);
>
> WARNING: msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt
>
> msleep(1) doesn't work nice, as it may sleep up to 20 ms, depending on
> how HZ is configured. Is that what you want?
Umm. ok I've updated it in v2 to use: -
usleep_range(3500, 5000);
This code is toggling the reset line to the demod. Checking the stv900 demod datasheet
it should be asserted for at least 3ms. The STV0367 demod doesn't specify any
timings in the datasheet.
>
> > + }
> > +
> > + tsin->demux_mapping = n;
> > +
> > + dev_dbg(fei->dev,
> > + "channel=%p n=%d tsin_num=%d, invert-ts-clk=%d\n\t"
> > + "serial-not-parallel=%d pkt-clk-valid=%d dvb-card=%d\n",
> > + fei->channel_data[n], n,
> > + tsin->tsin_id, tsin->invert_ts_clk,
> > + tsin->serial_not_parallel, tsin->async_not_sync,
> > + tsin->dvb_card);
> > +
> > + ret = configure_input_block(fei, fei->channel_data[n]);
> > +
> > + if (ret) {
> > + dev_err(fei->dev,
> > + "configure_input_block tsin=%d failed\n",
> > + fei->channel_data[n]->tsin_id);
> > + goto err_unmap;
> > + }
> > +
> > + n++;
> > + }
> > +
> > + /* Setup timer interrupt */
> > + init_timer(&fei->timer);
> > + fei->timer.function = c8sectpfe_timer_interrupt;
> > + fei->timer.data = (unsigned long)fei;
> > +
> > + mutex_init(&fei->lock);
> > +
> > + /* Get the configuration information about the tuners */
> > + ret = c8sectpfe_tuner_register_frontend(&fei->c8sectpfe[0],
> > + (void *)fei,
> > + c8sectpfe_start_feed,
> > + c8sectpfe_stop_feed);
> > + if (ret)
> > + goto err_unmap;
> > +
> > + /*
> > + * STBus target port can access IMEM and DMEM ports
> > + * without waiting for CPU
> > + */
> > + writel(0x1, fei->io + DMA_PER_STBUS_SYNC);
> > +
> > + dev_info(dev, "Boot the memdma SLIM core\n");
> > + writel(0x1, fei->io + DMA_CPU_RUN);
> > +
> > + c8sectpfe_debugfs_init(fei);
> > +
> > + return 0;
> > +
> > +err_unmap:
> > + for (n = 0; n < fei->tsin_count; n++) {
> > + tsin = fei->channel_data[n];
> > +
> > + if (tsin)
> > + free_input_block(fei, tsin);
> > + }
> > +
> > +err_clk_disable:
> > + /* TODO uncomment when upstream has taken a reference on this clk */
> > + /*clk_disable_unprepare(fei->c8sectpfeclk);*/
>
> Hmm... what's the above? Why is it commented?
The STFE clock has a peculiar property in that once it has been enabled,
it can't be turned off, otherwise it causes the stih407/10 SoC to hang.
Currently this platform is waiting for Lees critical clocks series here: -
https://lkml.org/lkml/2015/7/22/311
to be merged, so the STFE clock can be marked as critical. Once that is
in place, this clk_disable can be uncommented.
I consider this a silicon bug which will hopefully be fixed, so
wish to keep the knowledge of this clock bug away from the c8sectpfe driver
and have the driver issue balanced enable/disable calls. However this
can't happen until the above series is merged, as the system will hang
when you remove the module, or if probe fails.
Another temp workaround would be to issue clk_prepare_enable twice, and
uncomment the disables (if you prefer that to what I have currently).
>
> > + return ret;
> > +}
> > +
> > +static int c8sectpfe_remove(struct platform_device *pdev)
> > +{
> > + struct c8sectpfei *fei = platform_get_drvdata(pdev);
> > + struct channel_info *channel;
> > + unsigned int n;
> > +
> > + c8sectpfe_tuner_unregister_frontend(fei->c8sectpfe[0], fei);
> > +
> > + /*
> > + * Now loop through and un-configure each of the InputBlock resources
> > + */
> > + for (n = 0; n < fei->tsin_count; n++) {
> > + channel = fei->channel_data[n];
> > + if (channel)
> > + free_input_block(fei, channel);
> > + }
> > +
> > + c8sectpfe_debugfs_exit(fei);
> > +
> > + dev_info(fei->dev, "Stopping memdma SLIM core\n");
> > + if (readl(fei->io + DMA_CPU_RUN))
> > + writel(0x0, fei->io + DMA_CPU_RUN);
> > +
> > + /* unclock all internal IP's */
> > + if (readl(fei->io + SYS_INPUT_CLKEN))
> > + writel(0, fei->io + SYS_INPUT_CLKEN);
> > + if (readl(fei->io + SYS_OTHER_CLKEN))
> > + writel(0, fei->io + SYS_OTHER_CLKEN);
> > +
> > + /* TODO uncomment when upstream has taken a reference on this clk */
> > + /*
> > + if (fei->c8sectpfeclk)
> > + clk_disable_unprepare(fei->c8sectpfeclk);
> > + */
>
> Hmm... what's the above? Why is it commented?
See comment above about this being a critical clock on stih407/10.
>
> > +
> > + return 0;
> > +}
> > +
> > +static int load_slim_core_fw(struct c8sectpfei *fei)
> > +{
> > + const struct firmware *fw = NULL;
> > + unsigned char *pImem = NULL;
> > + unsigned char *pDmem = NULL;
> > + Elf32_Ehdr *ehdr;
> > + Elf32_Phdr *phdr;
> > + int err = 0;
> > + int i;
> > +
> > + dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA);
> > +
> > + err = request_firmware(&fw, FIRMWARE_MEMDMA, fei->dev);
> > + if (err) {
> > + dev_err(fei->dev, "Failed to load %s, %d.\n",
> > + FIRMWARE_MEMDMA, err);
> > + return -EINVAL;
> > + }
> > +
> > + /* Check ELF magic */
> > + ehdr = (Elf32_Ehdr *)fw->data;
> > + if (ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
> > + ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
> > + ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
> > + ehdr->e_ident[EI_MAG3] != ELFMAG3) {
> > + dev_err(fei->dev, "Invalid ELF magic\n");
> > + err = -ENODEV;
> > + goto done;
> > + } else {
> > + dev_dbg(fei->dev, "Valid ELF header found\n");
> > + }
> > +
> > + /* Check program headers are within firmware size */
> > + if (ehdr->e_phoff + (ehdr->e_phnum * sizeof(Elf32_Phdr)) > fw->size) {
> > + dev_err(fei->dev, "Program headers outside of firmware file\n");
> > + err = -ENODEV;
> > + goto done;
> > + }
> > +
> > + pImem = (unsigned char *) fei->io + DMA_MEMDMA_OFFSET + DMA_IMEM_OFFSET;
> > + pDmem = (unsigned char *) fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET;
> > +
> > + phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff);
> > +
> > + /* go through the available ELF segments */
> > + for (i = 0; i < ehdr->e_phnum && !err; i++, phdr++) {
> > + unsigned char *dest;
> > + int imem = 0;
> > +
> > + /* Only consider LOAD segments */
> > + if (phdr->p_type != PT_LOAD)
> > + continue;
> > +
> > + /*
> > + * Check segment is contained within the fw->data buffer
> > + */
> > + if (phdr->p_offset + phdr->p_filesz > fw->size) {
> > + dev_err(fei->dev,
> > + "Segment %d is outside of firmware file\n", i);
> > + err = -EINVAL;
> > + break;
> > + }
> > +
> > + /*
> > + * MEMDMA IMEM has executable flag set, otherwise load
> > + * this segment to DMEM.
> > + */
> > +
> > + if (phdr->p_flags & PF_X) {
> > + dest = pImem;
> > + imem = 1;
> > + } else {
> > + dest = pDmem;
> > + }
> > +
> > + /*
> > + * The Slim ELF file uses 32-bit word addressing for
> > + * load offsets.
> > + */
> > +
> > + dest += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int);
> > +
> > + /*
> > + * For DMEM segments copy the segment data from the ELF
> > + * file and pad segment with zeroes
> > + *
> > + * For IMEM segments, the segment contains 24-bit
> > + * instructions which must be padded to 32-bit
> > + * instructions before being written. The written
> > + * segment is padded with NOP instructions.
> > + */
> > +
> > + if (!imem) {
> > + dev_dbg(fei->dev,
> > + "Loading DMEM segment %d 0x%08x\n\t"
> > + "(0x%x bytes) -> 0x%08x (0x%x bytes)\n",
> > + i, phdr->p_paddr, phdr->p_filesz,
> > + (unsigned int)dest, phdr->p_memsz);
> > +
> > + memcpy((void *)dest, (void *)fw->data + phdr->p_offset,
> > + phdr->p_filesz);
> > +
> > + memset((void *)dest + phdr->p_filesz, 0,
> > + phdr->p_memsz - phdr->p_filesz);
> > + } else {
> > + const unsigned char *imem_src = fw->data
> > + + phdr->p_offset;
> > + unsigned char *imem_dest = dest;
> > + int j;
> > +
> > + dev_dbg(fei->dev,
> > + "Loading IMEM segment %d 0x%08x\n\t"
> > + " (0x%x bytes) -> 0x%08x (0x%x bytes)\n", i,
> > + phdr->p_paddr, phdr->p_filesz,
> > + (unsigned int)dest,
> > + phdr->p_memsz + phdr->p_memsz / 3);
> > +
> > + for (j = 0; j < phdr->p_filesz; j++) {
> > + *imem_dest = *imem_src;
> > +
> > + /* Every 3 bytes, add an additional
> > + * padding zero in destination */
> > + if (j % 3 == 2) {
> > + imem_dest++;
> > + *imem_dest = 0x00;
> > + }
> > + imem_dest++;
> > + imem_src++;
> > + }
> > + }
> > + }
> > +
> > +done:
> > + release_firmware(fw);
> > + return err;
> > +}
> > +
> > +static const struct of_device_id c8sectpfe_match[] = {
> > + { .compatible = "st,stih407-c8sectpfe" },
> > + { /* sentinel */ },
> > +};
> > +MODULE_DEVICE_TABLE(of, c8sectpfe_match);
> > +
> > +static struct platform_driver c8sectpfe_driver = {
> > + .driver = {
> > + .name = "c8sectpfe",
> > + .of_match_table = of_match_ptr(c8sectpfe_match),
> > + },
> > + .probe = c8sectpfe_probe,
> > + .remove = c8sectpfe_remove,
> > +};
> > +
> > +module_platform_driver(c8sectpfe_driver);
> > +
> > +MODULE_AUTHOR("Peter Bennett <[email protected]>");
> > +MODULE_AUTHOR("Peter Griffin <[email protected]>");
> > +MODULE_DESCRIPTION("C8SECTPFE STi DVB Driver");
> > +MODULE_LICENSE("GPL");
> > diff --git a/drivers/media/tsin/c8sectpfe/c8sectpfe-core.h b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.h
> > new file mode 100644
> > index 0000000..3466303
> > --- /dev/null
> > +++ b/drivers/media/tsin/c8sectpfe/c8sectpfe-core.h
> > @@ -0,0 +1,288 @@
> > +/*
> > + * c8sectpfe-core.h - C8SECTPFE STi DVB driver
> > + *
> > + * Copyright (c) STMicroelectronics 2015
> > + *
> > + * Author:Peter Bennett <[email protected]>
> > + * Peter Griffin <[email protected]>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License as
> > + * published by the Free Software Foundation; either version 2 of
> > + * the License, or (at your option) any later version.
> > + */
> > +#ifndef _C8SECTPFE_CORE_H_
> > +#define _C8SECTPFE_CORE_H_
> > +
> > +#define C8SECTPFEI_MAXCHANNEL 16
> > +#define C8SECTPFEI_MAXADAPTER 3
> > +
> > +#define C8SECTPFE_MAX_TSIN_CHAN 8
> > +
> > +struct channel_info {
> > +
> > + int tsin_id;
> > + bool invert_ts_clk;
> > + bool serial_not_parallel;
> > + bool async_not_sync;
> > + int i2c;
> > + int dvb_card;
> > +
> > + int rst_gpio;
> > +
> > + struct i2c_adapter *i2c_adapter;
> > + struct i2c_adapter *tuner_i2c;
> > + struct i2c_adapter *lnb_i2c;
> > + struct i2c_client *i2c_client;
> > + struct dvb_frontend *frontend;
> > +
> > + struct pinctrl_state *pstate;
> > +
> > + int demux_mapping;
> > + int active;
> > +
> > + void *back_buffer_start;
> > + void *back_buffer_aligned;
> > + dma_addr_t back_buffer_busaddr;
> > +
> > + void *pid_buffer_start;
> > + void *pid_buffer_aligned;
> > + dma_addr_t pid_buffer_busaddr;
> > +
> > + unsigned long fifo;
> > +
> > + struct completion idle_completion;
> > + struct tasklet_struct tsklet;
> > +
> > + struct c8sectpfei *fei;
> > + struct c8sectpfe_input_record *irec;
> > +
> > +};
> > +
> > +struct c8sectpfe_hw {
> > + int num_ib;
> > + int num_mib;
> > + int num_swts;
> > + int num_tsout;
> > + int num_ccsc;
> > + int num_ram;
> > + int num_tp;
> > +};
> > +
> > +struct c8sectpfei {
> > +
> > + struct device *dev;
> > + struct pinctrl *pinctrl;
> > +
> > + struct dentry *root;
> > + struct debugfs_regset32 *regset;
> > +
> > + int tsin_count;
> > +
> > + struct c8sectpfe_hw hw_stats;
> > +
> > + struct c8sectpfe *c8sectpfe[C8SECTPFEI_MAXADAPTER];
> > +
> > + int mapping[C8SECTPFEI_MAXCHANNEL];
> > +
> > + struct mutex lock;
> > +
> > + struct timer_list timer; /* timer interrupts for outputs */
> > +
> > + void __iomem *io;
> > + void __iomem *sram;
> > +
> > + unsigned long sram_size;
> > +
> > + struct channel_info *channel_data[C8SECTPFE_MAX_TSIN_CHAN];
> > +
> > + struct clk *c8sectpfeclk;
> > + int nima_rst_gpio;
> > + int nimb_rst_gpio;
> > +
> > + int idle_irq;
> > + int error_irq;
> > +
> > + int global_feed_count;
> > +
> > + struct c8sectpfe_input_record *irec;
> > +};
> > +
> > +/* C8SECTPFE SYS Regs list */
> > +
> > +#define SYS_INPUT_ERR_STATUS 0x0
> > +#define SYS_OTHER_ERR_STATUS 0x8
> > +#define SYS_INPUT_ERR_MASK 0x10
> > +#define SYS_OTHER_ERR_MASK 0x18
> > +#define SYS_DMA_ROUTE 0x20
> > +#define SYS_INPUT_CLKEN 0x30
> > +#define IBENABLE_MASK 0x7F
> > +
> > +#define SYS_OTHER_CLKEN 0x38
> > +#define TSDMAENABLE BIT(1)
> > +#define MEMDMAENABLE BIT(0)
> > +
> > +#define SYS_CFG_NUM_IB 0x200
> > +#define SYS_CFG_NUM_MIB 0x204
> > +#define SYS_CFG_NUM_SWTS 0x208
> > +#define SYS_CFG_NUM_TSOUT 0x20C
> > +#define SYS_CFG_NUM_CCSC 0x210
> > +#define SYS_CFG_NUM_RAM 0x214
> > +#define SYS_CFG_NUM_TP 0x218
> > +
> > +/* Input Block Regs */
> > +
> > +#define C8SECTPFE_INPUTBLK_OFFSET 0x1000
> > +#define C8SECTPFE_CHANNEL_OFFSET(x) ((x*0x40) + C8SECTPFE_INPUTBLK_OFFSET)
> > +
> > +#define C8SECTPFE_IB_IP_FMT_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x00)
> > +#define C8SECTPFE_IGNORE_ERR_AT_SOP BIT(7)
> > +#define C8SECTPFE_IGNORE_ERR_IN_PKT BIT(6)
> > +#define C8SECTPFE_IGNORE_ERR_IN_BYTE BIT(5)
> > +#define C8SECTPFE_INVERT_TSCLK BIT(4)
> > +#define C8SECTPFE_ALIGN_BYTE_SOP BIT(3)
> > +#define C8SECTPFE_ASYNC_NOT_SYNC BIT(2)
> > +#define C8SECTPFE_BYTE_ENDIANNESS_MSB BIT(1)
> > +#define C8SECTPFE_SERIAL_NOT_PARALLEL BIT(0)
> > +
> > +#define C8SECTPFE_IB_SYNCLCKDRP_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x04)
> > +#define C8SECTPFE_SYNC(x) (x & 0xf)
> > +#define C8SECTPFE_DROP(x) ((x<<4) & 0xf)
> > +#define C8SECTPFE_TOKEN(x) ((x<<8) & 0xff00)
> > +#define C8SECTPFE_SLDENDIANNESS BIT(16)
> > +
> > +#define C8SECTPFE_IB_TAGBYTES_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x08)
> > +#define C8SECTPFE_TAG_HEADER(x) (x << 16)
> > +#define C8SECTPFE_TAG_COUNTER(x) ((x<<1) & 0x7fff)
> > +#define C8SECTPFE_TAG_ENABLE BIT(0)
> > +
> > +#define C8SECTPFE_IB_PID_SET(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x0C)
> > +#define C8SECTPFE_PID_OFFSET(x) (x & 0x3f)
> > +#define C8SECTPFE_PID_NUMBITS(x) ((x << 6) & 0xfff)
> > +#define C8SECTPFE_PID_ENABLE BIT(31)
> > +
> > +#define C8SECTPFE_IB_PKT_LEN(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x10)
> > +
> > +#define C8SECTPFE_IB_BUFF_STRT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x14)
> > +#define C8SECTPFE_IB_BUFF_END(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x18)
> > +#define C8SECTPFE_IB_READ_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x1C)
> > +#define C8SECTPFE_IB_WRT_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x20)
> > +
> > +#define C8SECTPFE_IB_PRI_THRLD(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x24)
> > +#define C8SECTPFE_PRI_VALUE(x) (x & 0x7fffff)
> > +#define C8SECTPFE_PRI_LOWPRI(x) ((x & 0xf) << 24)
> > +#define C8SECTPFE_PRI_HIGHPRI(x) ((x & 0xf) << 28)
> > +
> > +#define C8SECTPFE_IB_STAT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x28)
> > +#define C8SECTPFE_STAT_FIFO_OVERFLOW(x) (x & 0x1)
> > +#define C8SECTPFE_STAT_BUFFER_OVERFLOW(x) (x & 0x2)
> > +#define C8SECTPFE_STAT_OUTOFORDERRP(x) (x & 0x4)
> > +#define C8SECTPFE_STAT_PID_OVERFLOW(x) (x & 0x8)
> > +#define C8SECTPFE_STAT_PKT_OVERFLOW(x) (x & 0x10)
> > +#define C8SECTPFE_STAT_ERROR_PACKETS(x) ((x >> 8) & 0xf)
> > +#define C8SECTPFE_STAT_SHORT_PACKETS(x) ((x >> 12) & 0xf)
> > +
> > +#define C8SECTPFE_IB_MASK(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x2C)
> > +#define C8SECTPFE_MASK_FIFO_OVERFLOW BIT(0)
> > +#define C8SECTPFE_MASK_BUFFER_OVERFLOW BIT(1)
> > +#define C8SECTPFE_MASK_OUTOFORDERRP(x) BIT(2)
> > +#define C8SECTPFE_MASK_PID_OVERFLOW(x) BIT(3)
> > +#define C8SECTPFE_MASK_PKT_OVERFLOW(x) BIT(4)
> > +#define C8SECTPFE_MASK_ERROR_PACKETS(x) ((x & 0xf) << 8)
> > +#define C8SECTPFE_MASK_SHORT_PACKETS(x) ((x & 0xf) >> 12)
> > +
> > +#define C8SECTPFE_IB_SYS(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x30)
> > +#define C8SECTPFE_SYS_RESET BIT(1)
> > +#define C8SECTPFE_SYS_ENABLE BIT(0)
> > +
> > +/*
> > + * Ponter record data structure required for each input block
> > + * see Table 82 on page 167
> > + */
> > +
> > +struct tpentry {
> > + /* The following entries are bus addresses for memdma */
> > + unsigned long dma_busbase;
> > + unsigned long dma_bustop;
> > + unsigned long dma_bus_wp;
> > + unsigned long dma_bus_rp;
> > +};
> > +
> > +struct c8sectpfe_input_record {
> > + unsigned long dma_membase; /* Internal sram base address */
> > + unsigned long dma_memtop; /* Internal sram top address */
> > +
> > + /*
> > + * TS packet size, including tag bytes added by input block,
> > + * rounded up to the next multiple of 8 bytes. The packet size,
> > + * including any tagging bytes and rounded up to the nearest
> > + * multiple of 8 bytes must be less than 255 bytes.
> > + */
> > + unsigned long dma_ts_pktsize;
> > + unsigned long tp_enable;
> > + struct tpentry ptr_data[1];
> > +};
> > +
> > +#define DMA_MEMDMA_OFFSET 0x4000
> > +#define DMA_IMEM_OFFSET 0x0
> > +#define DMA_DMEM_OFFSET 0x4000
> > +#define DMA_CPU 0x8000
> > +#define DMA_PER_OFFSET 0xb000
> > +
> > +/* XP70 Slim core regs */
> > +#define DMA_CPU_ID (DMA_MEMDMA_OFFSET + DMA_CPU + 0x0)
> > +#define DMA_CPU_VCR (DMA_MEMDMA_OFFSET + DMA_CPU + 0x4)
> > +#define DMA_CPU_RUN (DMA_MEMDMA_OFFSET + DMA_CPU + 0x8)
> > +#define DMA_CPU_CLOCKGATE (DMA_MEMDMA_OFFSET + DMA_CPU + 0xc)
> > +#define DMA_CPU_PC (DMA_MEMDMA_OFFSET + DMA_CPU + 0x20)
> > +
> > +/* Enable Interrupt for a IB */
> > +#define DMA_PER_TPn_DREQ_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd00)
> > +/* Ack interrupt by setting corresponding bit */
> > +#define DMA_PER_TPn_DACK_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd80)
> > +#define DMA_PER_TPn_DREQ (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe00)
> > +#define DMA_PER_TPn_DACK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe80)
> > +#define DMA_PER_DREQ_MODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf80)
> > +#define DMA_PER_STBUS_SYNC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf88)
> > +#define DMA_PER_STBUS_ACCESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf8c)
> > +#define DMA_PER_STBUS_ADDRESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf90)
> > +#define DMA_PER_IDLE_INT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfa8)
> > +#define DMA_PER_PRIORITY (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfac)
> > +#define DMA_PER_MAX_OPCODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb0)
> > +#define DMA_PER_MAX_CHUNK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb4)
> > +#define DMA_PER_PAGE_SIZE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfbc)
> > +#define DMA_PER_MBOX_STATUS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc0)
> > +#define DMA_PER_MBOX_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc8)
> > +#define DMA_PER_MBOX_CLEAR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd0)
> > +#define DMA_PER_MBOX_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd8)
> > +#define DMA_PER_INJECT_PKT_SRC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe0)
> > +#define DMA_PER_INJECT_PKT_DEST (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe4)
> > +#define DMA_PER_INJECT_PKT_ADDR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe8)
> > +#define DMA_PER_INJECT_PKT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfec)
> > +#define DMA_PER_PAT_PTR_INIT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff0)
> > +#define DMA_PER_PAT_PTR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff4)
> > +#define DMA_PER_SLEEP_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff8)
> > +#define DMA_PER_SLEEP_COUNTER (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xffc)
> > +/* #define DMA_RF_CPUREGn DMA_RFBASEADDR n=0 to 15) slim regsa */
> > +
> > +/* The following are from DMA_DMEM_BaseAddress */
> > +#define DMA_FIRMWARE_VERSION (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x0)
> > +#define DMA_PTRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x4)
> > +#define DMA_PTRREC_INPUT_OFFSET (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x8)
> > +#define DMA_ERRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0xc)
> > +#define DMA_ERROR_RECORD(n) ((n*4) + DMA_ERRREC_BASE + 0x4)
> > +#define DMA_IDLE_REQ (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x10)
> > +#define IDLEREQ BIT(31)
> > +
> > +#define DMA_FIRMWARE_CONFIG (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x14)
> > +
> > +/* Regs for PID Filter */
> > +
> > +#define PIDF_OFFSET 0x2800
> > +#define PIDF_BASE(n) ((n*4) + PIDF_OFFSET)
> > +#define PIDF_LEAK_ENABLE (PIDF_OFFSET + 0x100)
> > +#define PIDF_LEAK_STATUS (PIDF_OFFSET + 0x108)
> > +#define PIDF_LEAK_COUNT_RESET (PIDF_OFFSET + 0x110)
> > +#define PIDF_LEAK_COUNTER (PIDF_OFFSET + 0x114)
> > +
> > +#endif /* _C8SECTPFE_CORE_H_ */
regards,
Peter.
On Thu, Jul 30, 2015 at 6:08 AM, Joe Perches <[email protected]> wrote:
> On Thu, 2015-07-30 at 10:47 +0100, Peter Griffin wrote:
>> Hi Mauro / Joe,
>>
>> On Wed, 22 Jul 2015, Mauro Carvalho Chehab wrote:
>>
>> > Em Wed, 24 Jun 2015 18:17:37 -0700
>> > Joe Perches <[email protected]> escreveu:
>> >
>> > > On Wed, 2015-06-24 at 16:11 +0100, Peter Griffin wrote:
>> > > > This is used in conjunction with the STV0367 demodulator on
>> > > > the STV0367-NIM-V1.0 NIM card which can be used with the STi
>> > > > STB SoC's.
>> > >
>> > > Barely associated to this specific patch, but for
>> > > dvb-pll.c, another thing that seems possible is to
>> > > convert the struct dvb_pll_desc uses to const and
>> > > change the "entries" fixed array size from 12 to []
>> > >
>> > > It'd save a couple KB overall and remove ~5KB of data.
>> > >
>> > > $ size drivers/media/dvb-frontends/dvb-pll.o*
>> > > text data bss dec hex filename
>> > > 8520 1552 2120 12192 2fa0 drivers/media/dvb-frontends/dvb-pll.o.new
>> > > 5624 6363 2120 14107 371b drivers/media/dvb-frontends/dvb-pll.o.old
>> >
>> > Peter,
>> >
>> > Please add this patch on the next patch series you submit.
>>
>> Ok will do, I've added this patch with a slightly updated commit message
>> to my series.
>>
>> Joe - Can I add your signed-off-by?
>
> Signed-off-by: Joe Perches <[email protected]>
Reviewed-by: Michael Ira Krufky <[email protected]>
Joe, nice optimization - thanks for that.
With regards to Peter's patch, is this a digital-only tuner, or is it
a hybrid tuner?
The 5th byte that you send to the THOMSON DTT7546X seems to resemble
the 'auxiliary byte' that gets set in tuner-simple.c
I'm not sure that dvb-pll is the right place for this tuner
definition, if this is the case. Maybe this definition belongs in
tuner-simple instead, if the pattern matches better there.
Mauro, can we hold off on merging Peter's patch until we resolve this?
-Michael Ira Krufky
On Thu, Jul 30, 2015 at 7:14 AM, Michael Ira Krufky <[email protected]> wrote:
> On Thu, Jul 30, 2015 at 6:08 AM, Joe Perches <[email protected]> wrote:
>> On Thu, 2015-07-30 at 10:47 +0100, Peter Griffin wrote:
>>> Hi Mauro / Joe,
>>>
>>> On Wed, 22 Jul 2015, Mauro Carvalho Chehab wrote:
>>>
>>> > Em Wed, 24 Jun 2015 18:17:37 -0700
>>> > Joe Perches <[email protected]> escreveu:
>>> >
>>> > > On Wed, 2015-06-24 at 16:11 +0100, Peter Griffin wrote:
>>> > > > This is used in conjunction with the STV0367 demodulator on
>>> > > > the STV0367-NIM-V1.0 NIM card which can be used with the STi
>>> > > > STB SoC's.
>>> > >
>>> > > Barely associated to this specific patch, but for
>>> > > dvb-pll.c, another thing that seems possible is to
>>> > > convert the struct dvb_pll_desc uses to const and
>>> > > change the "entries" fixed array size from 12 to []
>>> > >
>>> > > It'd save a couple KB overall and remove ~5KB of data.
>>> > >
>>> > > $ size drivers/media/dvb-frontends/dvb-pll.o*
>>> > > text data bss dec hex filename
>>> > > 8520 1552 2120 12192 2fa0 drivers/media/dvb-frontends/dvb-pll.o.new
>>> > > 5624 6363 2120 14107 371b drivers/media/dvb-frontends/dvb-pll.o.old
>>> >
>>> > Peter,
>>> >
>>> > Please add this patch on the next patch series you submit.
>>>
>>> Ok will do, I've added this patch with a slightly updated commit message
>>> to my series.
>>>
>>> Joe - Can I add your signed-off-by?
>>
>> Signed-off-by: Joe Perches <[email protected]>
>
> Reviewed-by: Michael Ira Krufky <[email protected]>
>
> Joe, nice optimization - thanks for that.
>
> With regards to Peter's patch, is this a digital-only tuner, or is it
> a hybrid tuner?
>
> The 5th byte that you send to the THOMSON DTT7546X seems to resemble
> the 'auxiliary byte' that gets set in tuner-simple.c
>
> I'm not sure that dvb-pll is the right place for this tuner
> definition, if this is the case. Maybe this definition belongs in
> tuner-simple instead, if the pattern matches better there.
>
> Mauro, can we hold off on merging Peter's patch until we resolve this?
>
> -Michael Ira Krufky
eek! I mispelled my own email address.
With regards to Joe's patch - I'd like to see that merged. ...and
here is my correctly spelled email address:
Reviewed-by: Michael Ira Krufky <[email protected]>
On Thu, Jul 30, 2015 at 7:14 AM, Michael Ira Krufky <[email protected]> wrote:
> On Thu, Jul 30, 2015 at 6:08 AM, Joe Perches <[email protected]> wrote:
>> On Thu, 2015-07-30 at 10:47 +0100, Peter Griffin wrote:
>>> Hi Mauro / Joe,
>>>
>>> On Wed, 22 Jul 2015, Mauro Carvalho Chehab wrote:
>>>
>>> > Em Wed, 24 Jun 2015 18:17:37 -0700
>>> > Joe Perches <[email protected]> escreveu:
>>> >
>>> > > On Wed, 2015-06-24 at 16:11 +0100, Peter Griffin wrote:
>>> > > > This is used in conjunction with the STV0367 demodulator on
>>> > > > the STV0367-NIM-V1.0 NIM card which can be used with the STi
>>> > > > STB SoC's.
>>> > >
>>> > > Barely associated to this specific patch, but for
>>> > > dvb-pll.c, another thing that seems possible is to
>>> > > convert the struct dvb_pll_desc uses to const and
>>> > > change the "entries" fixed array size from 12 to []
>>> > >
>>> > > It'd save a couple KB overall and remove ~5KB of data.
>>> > >
>>> > > $ size drivers/media/dvb-frontends/dvb-pll.o*
>>> > > text data bss dec hex filename
>>> > > 8520 1552 2120 12192 2fa0 drivers/media/dvb-frontends/dvb-pll.o.new
>>> > > 5624 6363 2120 14107 371b drivers/media/dvb-frontends/dvb-pll.o.old
>>> >
>>> > Peter,
>>> >
>>> > Please add this patch on the next patch series you submit.
>>>
>>> Ok will do, I've added this patch with a slightly updated commit message
>>> to my series.
>>>
>>> Joe - Can I add your signed-off-by?
>>
>> Signed-off-by: Joe Perches <[email protected]>
>
> Reviewed-by: Michael Ira Krufky <[email protected]>
>
> Joe, nice optimization - thanks for that.
>
> With regards to Peter's patch, is this a digital-only tuner, or is it
> a hybrid tuner?
>
> The 5th byte that you send to the THOMSON DTT7546X seems to resemble
> the 'auxiliary byte' that gets set in tuner-simple.c
>
> I'm not sure that dvb-pll is the right place for this tuner
> definition, if this is the case. Maybe this definition belongs in
> tuner-simple instead, if the pattern matches better there.
>
> Mauro, can we hold off on merging Peter's patch until we resolve this?
This code block, specifically, I would rather not see added into dvb-pll:
+static int dvb_pll_get_num_regs(struct dvb_pll_priv *priv)
+{
+ int num_regs = 4;
+
+ if (strncmp(priv->pll_desc->name, "Thomson dtt7546x", 16) == 0)
+ num_regs = 5;
+
+ return num_regs;
+}
+
tuner-simple provides an infrastructure that allows this tuner to be
added in a more elegant way without the need to add special cases to
otherwise generic code, as done in the above.
I'm sorry, Peter. Can you take a look at tuner-simple and consider
sending a new patch?
-Michael Ira Krufky
Hi Michael,
On Thu, 30 Jul 2015, Michael Ira Krufky wrote:
> On Thu, Jul 30, 2015 at 7:14 AM, Michael Ira Krufky <[email protected]> wrote:
> > On Thu, Jul 30, 2015 at 6:08 AM, Joe Perches <[email protected]> wrote:
> >> On Thu, 2015-07-30 at 10:47 +0100, Peter Griffin wrote:
> >>> Hi Mauro / Joe,
> >>>
> >>> On Wed, 22 Jul 2015, Mauro Carvalho Chehab wrote:
> >>>
> >>> > Em Wed, 24 Jun 2015 18:17:37 -0700
> >>> > Joe Perches <[email protected]> escreveu:
> >>> >
> >>> > > On Wed, 2015-06-24 at 16:11 +0100, Peter Griffin wrote:
> >>> > > > This is used in conjunction with the STV0367 demodulator on
> >>> > > > the STV0367-NIM-V1.0 NIM card which can be used with the STi
> >>> > > > STB SoC's.
> >>> > >
> >>> > > Barely associated to this specific patch, but for
> >>> > > dvb-pll.c, another thing that seems possible is to
> >>> > > convert the struct dvb_pll_desc uses to const and
> >>> > > change the "entries" fixed array size from 12 to []
> >>> > >
> >>> > > It'd save a couple KB overall and remove ~5KB of data.
> >>> > >
> >>> > > $ size drivers/media/dvb-frontends/dvb-pll.o*
> >>> > > text data bss dec hex filename
> >>> > > 8520 1552 2120 12192 2fa0 drivers/media/dvb-frontends/dvb-pll.o.new
> >>> > > 5624 6363 2120 14107 371b drivers/media/dvb-frontends/dvb-pll.o.old
> >>> >
> >>> > Peter,
> >>> >
> >>> > Please add this patch on the next patch series you submit.
> >>>
> >>> Ok will do, I've added this patch with a slightly updated commit message
> >>> to my series.
> >>>
> >>> Joe - Can I add your signed-off-by?
> >>
> >> Signed-off-by: Joe Perches <[email protected]>
> >
> > Reviewed-by: Michael Ira Krufky <[email protected]>
> >
> > Joe, nice optimization - thanks for that.
> >
> > With regards to Peter's patch, is this a digital-only tuner, or is it
> > a hybrid tuner?
> >
> > The 5th byte that you send to the THOMSON DTT7546X seems to resemble
> > the 'auxiliary byte' that gets set in tuner-simple.c
> >
> > I'm not sure that dvb-pll is the right place for this tuner
> > definition, if this is the case. Maybe this definition belongs in
> > tuner-simple instead, if the pattern matches better there.
> >
> > Mauro, can we hold off on merging Peter's patch until we resolve this?
> >
> > -Michael Ira Krufky
>
> eek! I mispelled my own email address.
>
>
> With regards to Joe's patch - I'd like to see that merged. ...and
> here is my correctly spelled email address:
>
>
> Reviewed-by: Michael Ira Krufky <[email protected]>
Ok I will add your reviewed-by and include it in my v2.
Hi Michael,
On Thu, 30 Jul 2015, Michael Ira Krufky wrote:
> On Thu, Jul 30, 2015 at 7:14 AM, Michael Ira Krufky <[email protected]> wrote:
> > On Thu, Jul 30, 2015 at 6:08 AM, Joe Perches <[email protected]> wrote:
> >> On Thu, 2015-07-30 at 10:47 +0100, Peter Griffin wrote:
> >>> Hi Mauro / Joe,
> >>>
> >>> On Wed, 22 Jul 2015, Mauro Carvalho Chehab wrote:
> >>>
> >>> > Em Wed, 24 Jun 2015 18:17:37 -0700
> >>> > Joe Perches <[email protected]> escreveu:
> >>> >
> >>> > > On Wed, 2015-06-24 at 16:11 +0100, Peter Griffin wrote:
> >>> > > > This is used in conjunction with the STV0367 demodulator on
> >>> > > > the STV0367-NIM-V1.0 NIM card which can be used with the STi
> >>> > > > STB SoC's.
> >>> > >
> >>> > > Barely associated to this specific patch, but for
> >>> > > dvb-pll.c, another thing that seems possible is to
> >>> > > convert the struct dvb_pll_desc uses to const and
> >>> > > change the "entries" fixed array size from 12 to []
> >>> > >
> >>> > > It'd save a couple KB overall and remove ~5KB of data.
> >>> > >
> >>> > > $ size drivers/media/dvb-frontends/dvb-pll.o*
> >>> > > text data bss dec hex filename
> >>> > > 8520 1552 2120 12192 2fa0 drivers/media/dvb-frontends/dvb-pll.o.new
> >>> > > 5624 6363 2120 14107 371b drivers/media/dvb-frontends/dvb-pll.o.old
> >>> >
> >>> > Peter,
> >>> >
> >>> > Please add this patch on the next patch series you submit.
> >>>
> >>> Ok will do, I've added this patch with a slightly updated commit message
> >>> to my series.
> >>>
> >>> Joe - Can I add your signed-off-by?
> >>
> >> Signed-off-by: Joe Perches <[email protected]>
> >
> > Reviewed-by: Michael Ira Krufky <[email protected]>
> >
> > Joe, nice optimization - thanks for that.
> >
> > With regards to Peter's patch, is this a digital-only tuner, or is it
> > a hybrid tuner?
> >
> > The 5th byte that you send to the THOMSON DTT7546X seems to resemble
> > the 'auxiliary byte' that gets set in tuner-simple.c
> >
> > I'm not sure that dvb-pll is the right place for this tuner
> > definition, if this is the case. Maybe this definition belongs in
> > tuner-simple instead, if the pattern matches better there.
> >
> > Mauro, can we hold off on merging Peter's patch until we resolve this?
>
> This code block, specifically, I would rather not see added into dvb-pll:
>
> +static int dvb_pll_get_num_regs(struct dvb_pll_priv *priv)
> +{
> + int num_regs = 4;
> +
> + if (strncmp(priv->pll_desc->name, "Thomson dtt7546x", 16) == 0)
> + num_regs = 5;
> +
> + return num_regs;
> +}
> +
>
> tuner-simple provides an infrastructure that allows this tuner to be
> added in a more elegant way without the need to add special cases to
> otherwise generic code, as done in the above.
>
> I'm sorry, Peter. Can you take a look at tuner-simple and consider
> sending a new patch?
Yes sure. I wasn't actually aware that tuner-simple existed. From what I
can see briefly looking at it, it does look a more suitable way of adding
support for this tuner.
The dtt7546x is a dual tuner in that it supports dvb-t and dvb-c, however
I have only tested it with DVB-T as that is the only feed I have to my home office.
As I have a V2 incorporating all of Mauro's changes ready to send, and I'm
on holiday for 3 weeks from Friday, I will temporarily drop support for this
tuner and NIM card in V2, and migrate over to the new driver when I return end
of August.
regards,
Peter.