1*e7b8153eSMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0
2*e7b8153eSMauro Carvalho Chehab /*
3*e7b8153eSMauro Carvalho Chehab * c8sectpfe-common.c - C8SECTPFE STi DVB driver
4*e7b8153eSMauro Carvalho Chehab *
5*e7b8153eSMauro Carvalho Chehab * Copyright (c) STMicroelectronics 2015
6*e7b8153eSMauro Carvalho Chehab *
7*e7b8153eSMauro Carvalho Chehab * Author: Peter Griffin <peter.griffin@linaro.org>
8*e7b8153eSMauro Carvalho Chehab *
9*e7b8153eSMauro Carvalho Chehab */
10*e7b8153eSMauro Carvalho Chehab #include <linux/completion.h>
11*e7b8153eSMauro Carvalho Chehab #include <linux/delay.h>
12*e7b8153eSMauro Carvalho Chehab #include <linux/device.h>
13*e7b8153eSMauro Carvalho Chehab #include <linux/dvb/dmx.h>
14*e7b8153eSMauro Carvalho Chehab #include <linux/errno.h>
15*e7b8153eSMauro Carvalho Chehab #include <linux/init.h>
16*e7b8153eSMauro Carvalho Chehab #include <linux/interrupt.h>
17*e7b8153eSMauro Carvalho Chehab #include <linux/io.h>
18*e7b8153eSMauro Carvalho Chehab #include <linux/ioport.h>
19*e7b8153eSMauro Carvalho Chehab #include <linux/module.h>
20*e7b8153eSMauro Carvalho Chehab #include <linux/slab.h>
21*e7b8153eSMauro Carvalho Chehab #include <linux/time.h>
22*e7b8153eSMauro Carvalho Chehab #include <linux/wait.h>
23*e7b8153eSMauro Carvalho Chehab
24*e7b8153eSMauro Carvalho Chehab #include <media/dmxdev.h>
25*e7b8153eSMauro Carvalho Chehab #include <media/dvbdev.h>
26*e7b8153eSMauro Carvalho Chehab #include <media/dvb_demux.h>
27*e7b8153eSMauro Carvalho Chehab #include <media/dvb_frontend.h>
28*e7b8153eSMauro Carvalho Chehab #include <media/dvb_net.h>
29*e7b8153eSMauro Carvalho Chehab
30*e7b8153eSMauro Carvalho Chehab #include "c8sectpfe-common.h"
31*e7b8153eSMauro Carvalho Chehab #include "c8sectpfe-core.h"
32*e7b8153eSMauro Carvalho Chehab #include "c8sectpfe-dvb.h"
33*e7b8153eSMauro Carvalho Chehab
register_dvb(struct stdemux * demux,struct dvb_adapter * adap,void * start_feed,void * stop_feed,struct c8sectpfei * fei)34*e7b8153eSMauro Carvalho Chehab static int register_dvb(struct stdemux *demux, struct dvb_adapter *adap,
35*e7b8153eSMauro Carvalho Chehab void *start_feed, void *stop_feed,
36*e7b8153eSMauro Carvalho Chehab struct c8sectpfei *fei)
37*e7b8153eSMauro Carvalho Chehab {
38*e7b8153eSMauro Carvalho Chehab int result;
39*e7b8153eSMauro Carvalho Chehab
40*e7b8153eSMauro Carvalho Chehab demux->dvb_demux.dmx.capabilities = DMX_TS_FILTERING |
41*e7b8153eSMauro Carvalho Chehab DMX_SECTION_FILTERING |
42*e7b8153eSMauro Carvalho Chehab DMX_MEMORY_BASED_FILTERING;
43*e7b8153eSMauro Carvalho Chehab
44*e7b8153eSMauro Carvalho Chehab demux->dvb_demux.priv = demux;
45*e7b8153eSMauro Carvalho Chehab demux->dvb_demux.filternum = C8SECTPFE_MAXCHANNEL;
46*e7b8153eSMauro Carvalho Chehab demux->dvb_demux.feednum = C8SECTPFE_MAXCHANNEL;
47*e7b8153eSMauro Carvalho Chehab
48*e7b8153eSMauro Carvalho Chehab demux->dvb_demux.start_feed = start_feed;
49*e7b8153eSMauro Carvalho Chehab demux->dvb_demux.stop_feed = stop_feed;
50*e7b8153eSMauro Carvalho Chehab demux->dvb_demux.write_to_decoder = NULL;
51*e7b8153eSMauro Carvalho Chehab
52*e7b8153eSMauro Carvalho Chehab result = dvb_dmx_init(&demux->dvb_demux);
53*e7b8153eSMauro Carvalho Chehab if (result < 0) {
54*e7b8153eSMauro Carvalho Chehab dev_err(fei->dev, "dvb_dmx_init failed (errno = %d)\n",
55*e7b8153eSMauro Carvalho Chehab result);
56*e7b8153eSMauro Carvalho Chehab goto err_dmx;
57*e7b8153eSMauro Carvalho Chehab }
58*e7b8153eSMauro Carvalho Chehab
59*e7b8153eSMauro Carvalho Chehab demux->dmxdev.filternum = demux->dvb_demux.filternum;
60*e7b8153eSMauro Carvalho Chehab demux->dmxdev.demux = &demux->dvb_demux.dmx;
61*e7b8153eSMauro Carvalho Chehab demux->dmxdev.capabilities = 0;
62*e7b8153eSMauro Carvalho Chehab
63*e7b8153eSMauro Carvalho Chehab result = dvb_dmxdev_init(&demux->dmxdev, adap);
64*e7b8153eSMauro Carvalho Chehab if (result < 0) {
65*e7b8153eSMauro Carvalho Chehab dev_err(fei->dev, "dvb_dmxdev_init failed (errno = %d)\n",
66*e7b8153eSMauro Carvalho Chehab result);
67*e7b8153eSMauro Carvalho Chehab
68*e7b8153eSMauro Carvalho Chehab goto err_dmxdev;
69*e7b8153eSMauro Carvalho Chehab }
70*e7b8153eSMauro Carvalho Chehab
71*e7b8153eSMauro Carvalho Chehab demux->hw_frontend.source = DMX_FRONTEND_0 + demux->tsin_index;
72*e7b8153eSMauro Carvalho Chehab
73*e7b8153eSMauro Carvalho Chehab result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx,
74*e7b8153eSMauro Carvalho Chehab &demux->hw_frontend);
75*e7b8153eSMauro Carvalho Chehab if (result < 0) {
76*e7b8153eSMauro Carvalho Chehab dev_err(fei->dev, "add_frontend failed (errno = %d)\n", result);
77*e7b8153eSMauro Carvalho Chehab goto err_fe_hw;
78*e7b8153eSMauro Carvalho Chehab }
79*e7b8153eSMauro Carvalho Chehab
80*e7b8153eSMauro Carvalho Chehab demux->mem_frontend.source = DMX_MEMORY_FE;
81*e7b8153eSMauro Carvalho Chehab result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx,
82*e7b8153eSMauro Carvalho Chehab &demux->mem_frontend);
83*e7b8153eSMauro Carvalho Chehab if (result < 0) {
84*e7b8153eSMauro Carvalho Chehab dev_err(fei->dev, "add_frontend failed (%d)\n", result);
85*e7b8153eSMauro Carvalho Chehab goto err_fe_mem;
86*e7b8153eSMauro Carvalho Chehab }
87*e7b8153eSMauro Carvalho Chehab
88*e7b8153eSMauro Carvalho Chehab result = demux->dvb_demux.dmx.connect_frontend(&demux->dvb_demux.dmx,
89*e7b8153eSMauro Carvalho Chehab &demux->hw_frontend);
90*e7b8153eSMauro Carvalho Chehab if (result < 0) {
91*e7b8153eSMauro Carvalho Chehab dev_err(fei->dev, "connect_frontend (%d)\n", result);
92*e7b8153eSMauro Carvalho Chehab goto err_fe_con;
93*e7b8153eSMauro Carvalho Chehab }
94*e7b8153eSMauro Carvalho Chehab
95*e7b8153eSMauro Carvalho Chehab return 0;
96*e7b8153eSMauro Carvalho Chehab
97*e7b8153eSMauro Carvalho Chehab err_fe_con:
98*e7b8153eSMauro Carvalho Chehab demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
99*e7b8153eSMauro Carvalho Chehab &demux->mem_frontend);
100*e7b8153eSMauro Carvalho Chehab err_fe_mem:
101*e7b8153eSMauro Carvalho Chehab demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
102*e7b8153eSMauro Carvalho Chehab &demux->hw_frontend);
103*e7b8153eSMauro Carvalho Chehab err_fe_hw:
104*e7b8153eSMauro Carvalho Chehab dvb_dmxdev_release(&demux->dmxdev);
105*e7b8153eSMauro Carvalho Chehab err_dmxdev:
106*e7b8153eSMauro Carvalho Chehab dvb_dmx_release(&demux->dvb_demux);
107*e7b8153eSMauro Carvalho Chehab err_dmx:
108*e7b8153eSMauro Carvalho Chehab return result;
109*e7b8153eSMauro Carvalho Chehab
110*e7b8153eSMauro Carvalho Chehab }
111*e7b8153eSMauro Carvalho Chehab
unregister_dvb(struct stdemux * demux)112*e7b8153eSMauro Carvalho Chehab static void unregister_dvb(struct stdemux *demux)
113*e7b8153eSMauro Carvalho Chehab {
114*e7b8153eSMauro Carvalho Chehab
115*e7b8153eSMauro Carvalho Chehab demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
116*e7b8153eSMauro Carvalho Chehab &demux->mem_frontend);
117*e7b8153eSMauro Carvalho Chehab
118*e7b8153eSMauro Carvalho Chehab demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
119*e7b8153eSMauro Carvalho Chehab &demux->hw_frontend);
120*e7b8153eSMauro Carvalho Chehab
121*e7b8153eSMauro Carvalho Chehab dvb_dmxdev_release(&demux->dmxdev);
122*e7b8153eSMauro Carvalho Chehab
123*e7b8153eSMauro Carvalho Chehab dvb_dmx_release(&demux->dvb_demux);
124*e7b8153eSMauro Carvalho Chehab }
125*e7b8153eSMauro Carvalho Chehab
c8sectpfe_create(struct c8sectpfei * fei,void * start_feed,void * stop_feed)126*e7b8153eSMauro Carvalho Chehab static struct c8sectpfe *c8sectpfe_create(struct c8sectpfei *fei,
127*e7b8153eSMauro Carvalho Chehab void *start_feed,
128*e7b8153eSMauro Carvalho Chehab void *stop_feed)
129*e7b8153eSMauro Carvalho Chehab {
130*e7b8153eSMauro Carvalho Chehab struct c8sectpfe *c8sectpfe;
131*e7b8153eSMauro Carvalho Chehab int result;
132*e7b8153eSMauro Carvalho Chehab int i, j;
133*e7b8153eSMauro Carvalho Chehab
134*e7b8153eSMauro Carvalho Chehab short int ids[] = { -1 };
135*e7b8153eSMauro Carvalho Chehab
136*e7b8153eSMauro Carvalho Chehab c8sectpfe = kzalloc(sizeof(struct c8sectpfe), GFP_KERNEL);
137*e7b8153eSMauro Carvalho Chehab if (!c8sectpfe)
138*e7b8153eSMauro Carvalho Chehab goto err1;
139*e7b8153eSMauro Carvalho Chehab
140*e7b8153eSMauro Carvalho Chehab mutex_init(&c8sectpfe->lock);
141*e7b8153eSMauro Carvalho Chehab
142*e7b8153eSMauro Carvalho Chehab c8sectpfe->device = fei->dev;
143*e7b8153eSMauro Carvalho Chehab
144*e7b8153eSMauro Carvalho Chehab result = dvb_register_adapter(&c8sectpfe->adapter, "STi c8sectpfe",
145*e7b8153eSMauro Carvalho Chehab THIS_MODULE, fei->dev, ids);
146*e7b8153eSMauro Carvalho Chehab if (result < 0) {
147*e7b8153eSMauro Carvalho Chehab dev_err(fei->dev, "dvb_register_adapter failed (errno = %d)\n",
148*e7b8153eSMauro Carvalho Chehab result);
149*e7b8153eSMauro Carvalho Chehab goto err2;
150*e7b8153eSMauro Carvalho Chehab }
151*e7b8153eSMauro Carvalho Chehab
152*e7b8153eSMauro Carvalho Chehab c8sectpfe->adapter.priv = fei;
153*e7b8153eSMauro Carvalho Chehab
154*e7b8153eSMauro Carvalho Chehab for (i = 0; i < fei->tsin_count; i++) {
155*e7b8153eSMauro Carvalho Chehab
156*e7b8153eSMauro Carvalho Chehab c8sectpfe->demux[i].tsin_index = i;
157*e7b8153eSMauro Carvalho Chehab c8sectpfe->demux[i].c8sectpfei = fei;
158*e7b8153eSMauro Carvalho Chehab
159*e7b8153eSMauro Carvalho Chehab result = register_dvb(&c8sectpfe->demux[i], &c8sectpfe->adapter,
160*e7b8153eSMauro Carvalho Chehab start_feed, stop_feed, fei);
161*e7b8153eSMauro Carvalho Chehab if (result < 0) {
162*e7b8153eSMauro Carvalho Chehab dev_err(fei->dev,
163*e7b8153eSMauro Carvalho Chehab "register_dvb feed=%d failed (errno = %d)\n",
164*e7b8153eSMauro Carvalho Chehab result, i);
165*e7b8153eSMauro Carvalho Chehab
166*e7b8153eSMauro Carvalho Chehab /* we take a all or nothing approach */
167*e7b8153eSMauro Carvalho Chehab for (j = 0; j < i; j++)
168*e7b8153eSMauro Carvalho Chehab unregister_dvb(&c8sectpfe->demux[j]);
169*e7b8153eSMauro Carvalho Chehab goto err3;
170*e7b8153eSMauro Carvalho Chehab }
171*e7b8153eSMauro Carvalho Chehab }
172*e7b8153eSMauro Carvalho Chehab
173*e7b8153eSMauro Carvalho Chehab c8sectpfe->num_feeds = fei->tsin_count;
174*e7b8153eSMauro Carvalho Chehab
175*e7b8153eSMauro Carvalho Chehab return c8sectpfe;
176*e7b8153eSMauro Carvalho Chehab err3:
177*e7b8153eSMauro Carvalho Chehab dvb_unregister_adapter(&c8sectpfe->adapter);
178*e7b8153eSMauro Carvalho Chehab err2:
179*e7b8153eSMauro Carvalho Chehab kfree(c8sectpfe);
180*e7b8153eSMauro Carvalho Chehab err1:
181*e7b8153eSMauro Carvalho Chehab return NULL;
182*e7b8153eSMauro Carvalho Chehab };
183*e7b8153eSMauro Carvalho Chehab
c8sectpfe_delete(struct c8sectpfe * c8sectpfe)184*e7b8153eSMauro Carvalho Chehab static void c8sectpfe_delete(struct c8sectpfe *c8sectpfe)
185*e7b8153eSMauro Carvalho Chehab {
186*e7b8153eSMauro Carvalho Chehab int i;
187*e7b8153eSMauro Carvalho Chehab
188*e7b8153eSMauro Carvalho Chehab if (!c8sectpfe)
189*e7b8153eSMauro Carvalho Chehab return;
190*e7b8153eSMauro Carvalho Chehab
191*e7b8153eSMauro Carvalho Chehab for (i = 0; i < c8sectpfe->num_feeds; i++)
192*e7b8153eSMauro Carvalho Chehab unregister_dvb(&c8sectpfe->demux[i]);
193*e7b8153eSMauro Carvalho Chehab
194*e7b8153eSMauro Carvalho Chehab dvb_unregister_adapter(&c8sectpfe->adapter);
195*e7b8153eSMauro Carvalho Chehab
196*e7b8153eSMauro Carvalho Chehab kfree(c8sectpfe);
197*e7b8153eSMauro Carvalho Chehab };
198*e7b8153eSMauro Carvalho Chehab
c8sectpfe_tuner_unregister_frontend(struct c8sectpfe * c8sectpfe,struct c8sectpfei * fei)199*e7b8153eSMauro Carvalho Chehab void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe,
200*e7b8153eSMauro Carvalho Chehab struct c8sectpfei *fei)
201*e7b8153eSMauro Carvalho Chehab {
202*e7b8153eSMauro Carvalho Chehab int n;
203*e7b8153eSMauro Carvalho Chehab struct channel_info *tsin;
204*e7b8153eSMauro Carvalho Chehab
205*e7b8153eSMauro Carvalho Chehab for (n = 0; n < fei->tsin_count; n++) {
206*e7b8153eSMauro Carvalho Chehab
207*e7b8153eSMauro Carvalho Chehab tsin = fei->channel_data[n];
208*e7b8153eSMauro Carvalho Chehab
209*e7b8153eSMauro Carvalho Chehab if (tsin) {
210*e7b8153eSMauro Carvalho Chehab if (tsin->frontend) {
211*e7b8153eSMauro Carvalho Chehab dvb_unregister_frontend(tsin->frontend);
212*e7b8153eSMauro Carvalho Chehab dvb_frontend_detach(tsin->frontend);
213*e7b8153eSMauro Carvalho Chehab }
214*e7b8153eSMauro Carvalho Chehab
215*e7b8153eSMauro Carvalho Chehab i2c_put_adapter(tsin->i2c_adapter);
216*e7b8153eSMauro Carvalho Chehab
217*e7b8153eSMauro Carvalho Chehab if (tsin->i2c_client) {
218*e7b8153eSMauro Carvalho Chehab module_put(tsin->i2c_client->dev.driver->owner);
219*e7b8153eSMauro Carvalho Chehab i2c_unregister_device(tsin->i2c_client);
220*e7b8153eSMauro Carvalho Chehab }
221*e7b8153eSMauro Carvalho Chehab }
222*e7b8153eSMauro Carvalho Chehab }
223*e7b8153eSMauro Carvalho Chehab
224*e7b8153eSMauro Carvalho Chehab c8sectpfe_delete(c8sectpfe);
225*e7b8153eSMauro Carvalho Chehab };
226*e7b8153eSMauro Carvalho Chehab
c8sectpfe_tuner_register_frontend(struct c8sectpfe ** c8sectpfe,struct c8sectpfei * fei,void * start_feed,void * stop_feed)227*e7b8153eSMauro Carvalho Chehab int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe,
228*e7b8153eSMauro Carvalho Chehab struct c8sectpfei *fei,
229*e7b8153eSMauro Carvalho Chehab void *start_feed,
230*e7b8153eSMauro Carvalho Chehab void *stop_feed)
231*e7b8153eSMauro Carvalho Chehab {
232*e7b8153eSMauro Carvalho Chehab struct channel_info *tsin;
233*e7b8153eSMauro Carvalho Chehab struct dvb_frontend *frontend;
234*e7b8153eSMauro Carvalho Chehab int n, res;
235*e7b8153eSMauro Carvalho Chehab
236*e7b8153eSMauro Carvalho Chehab *c8sectpfe = c8sectpfe_create(fei, start_feed, stop_feed);
237*e7b8153eSMauro Carvalho Chehab if (!*c8sectpfe)
238*e7b8153eSMauro Carvalho Chehab return -ENOMEM;
239*e7b8153eSMauro Carvalho Chehab
240*e7b8153eSMauro Carvalho Chehab for (n = 0; n < fei->tsin_count; n++) {
241*e7b8153eSMauro Carvalho Chehab tsin = fei->channel_data[n];
242*e7b8153eSMauro Carvalho Chehab
243*e7b8153eSMauro Carvalho Chehab res = c8sectpfe_frontend_attach(&frontend, *c8sectpfe, tsin, n);
244*e7b8153eSMauro Carvalho Chehab if (res)
245*e7b8153eSMauro Carvalho Chehab goto err;
246*e7b8153eSMauro Carvalho Chehab
247*e7b8153eSMauro Carvalho Chehab res = dvb_register_frontend(&c8sectpfe[0]->adapter, frontend);
248*e7b8153eSMauro Carvalho Chehab if (res < 0) {
249*e7b8153eSMauro Carvalho Chehab dev_err(fei->dev, "dvb_register_frontend failed (%d)\n",
250*e7b8153eSMauro Carvalho Chehab res);
251*e7b8153eSMauro Carvalho Chehab goto err;
252*e7b8153eSMauro Carvalho Chehab }
253*e7b8153eSMauro Carvalho Chehab
254*e7b8153eSMauro Carvalho Chehab tsin->frontend = frontend;
255*e7b8153eSMauro Carvalho Chehab }
256*e7b8153eSMauro Carvalho Chehab
257*e7b8153eSMauro Carvalho Chehab return 0;
258*e7b8153eSMauro Carvalho Chehab
259*e7b8153eSMauro Carvalho Chehab err:
260*e7b8153eSMauro Carvalho Chehab c8sectpfe_tuner_unregister_frontend(*c8sectpfe, fei);
261*e7b8153eSMauro Carvalho Chehab return res;
262*e7b8153eSMauro Carvalho Chehab }
263