1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | Mantis PCI bridge driver |
4 | Copyright (C) Manu Abraham (abraham.manu@gmail.com) |
5 | |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/bitops.h> |
10 | |
11 | #include <linux/signal.h> |
12 | #include <linux/sched.h> |
13 | #include <linux/interrupt.h> |
14 | #include <linux/pci.h> |
15 | #include <linux/i2c.h> |
16 | |
17 | #include <media/dmxdev.h> |
18 | #include <media/dvbdev.h> |
19 | #include <media/dvb_demux.h> |
20 | #include <media/dvb_frontend.h> |
21 | #include <media/dvb_net.h> |
22 | |
23 | #include "mantis_common.h" |
24 | #include "mantis_dma.h" |
25 | #include "mantis_ca.h" |
26 | #include "mantis_ioc.h" |
27 | #include "mantis_dvb.h" |
28 | |
29 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
30 | |
31 | int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power) |
32 | { |
33 | struct mantis_hwconfig *config = mantis->hwconfig; |
34 | |
35 | switch (power) { |
36 | case POWER_ON: |
37 | dprintk(MANTIS_DEBUG, 1, "Power ON" ); |
38 | mantis_gpio_set_bits(mantis, bitpos: config->power, value: POWER_ON); |
39 | msleep(msecs: 100); |
40 | mantis_gpio_set_bits(mantis, bitpos: config->power, value: POWER_ON); |
41 | msleep(msecs: 100); |
42 | break; |
43 | |
44 | case POWER_OFF: |
45 | dprintk(MANTIS_DEBUG, 1, "Power OFF" ); |
46 | mantis_gpio_set_bits(mantis, bitpos: config->power, value: POWER_OFF); |
47 | msleep(msecs: 100); |
48 | break; |
49 | |
50 | default: |
51 | dprintk(MANTIS_DEBUG, 1, "Unknown state <%02x>" , power); |
52 | return -1; |
53 | } |
54 | |
55 | return 0; |
56 | } |
57 | EXPORT_SYMBOL_GPL(mantis_frontend_power); |
58 | |
59 | void mantis_frontend_soft_reset(struct mantis_pci *mantis) |
60 | { |
61 | struct mantis_hwconfig *config = mantis->hwconfig; |
62 | |
63 | dprintk(MANTIS_DEBUG, 1, "Frontend RESET" ); |
64 | mantis_gpio_set_bits(mantis, bitpos: config->reset, value: 0); |
65 | msleep(msecs: 100); |
66 | mantis_gpio_set_bits(mantis, bitpos: config->reset, value: 0); |
67 | msleep(msecs: 100); |
68 | mantis_gpio_set_bits(mantis, bitpos: config->reset, value: 1); |
69 | msleep(msecs: 100); |
70 | mantis_gpio_set_bits(mantis, bitpos: config->reset, value: 1); |
71 | msleep(msecs: 100); |
72 | |
73 | return; |
74 | } |
75 | EXPORT_SYMBOL_GPL(mantis_frontend_soft_reset); |
76 | |
77 | static int mantis_frontend_shutdown(struct mantis_pci *mantis) |
78 | { |
79 | int err; |
80 | |
81 | mantis_frontend_soft_reset(mantis); |
82 | err = mantis_frontend_power(mantis, POWER_OFF); |
83 | if (err != 0) { |
84 | dprintk(MANTIS_ERROR, 1, "Frontend POWER OFF failed! <%d>" , err); |
85 | return 1; |
86 | } |
87 | |
88 | return 0; |
89 | } |
90 | |
91 | static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) |
92 | { |
93 | struct dvb_demux *dvbdmx = dvbdmxfeed->demux; |
94 | struct mantis_pci *mantis = dvbdmx->priv; |
95 | |
96 | dprintk(MANTIS_DEBUG, 1, "Mantis DVB Start feed" ); |
97 | if (!dvbdmx->dmx.frontend) { |
98 | dprintk(MANTIS_DEBUG, 1, "no frontend ?" ); |
99 | return -EINVAL; |
100 | } |
101 | |
102 | mantis->feeds++; |
103 | dprintk(MANTIS_DEBUG, 1, "mantis start feed, feeds=%d" , mantis->feeds); |
104 | |
105 | if (mantis->feeds == 1) { |
106 | dprintk(MANTIS_DEBUG, 1, "mantis start feed & dma" ); |
107 | mantis_dma_start(mantis); |
108 | tasklet_enable(t: &mantis->tasklet); |
109 | } |
110 | |
111 | return mantis->feeds; |
112 | } |
113 | |
114 | static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) |
115 | { |
116 | struct dvb_demux *dvbdmx = dvbdmxfeed->demux; |
117 | struct mantis_pci *mantis = dvbdmx->priv; |
118 | |
119 | dprintk(MANTIS_DEBUG, 1, "Mantis DVB Stop feed" ); |
120 | if (!dvbdmx->dmx.frontend) { |
121 | dprintk(MANTIS_DEBUG, 1, "no frontend ?" ); |
122 | return -EINVAL; |
123 | } |
124 | |
125 | mantis->feeds--; |
126 | if (mantis->feeds == 0) { |
127 | dprintk(MANTIS_DEBUG, 1, "mantis stop feed and dma" ); |
128 | tasklet_disable(t: &mantis->tasklet); |
129 | mantis_dma_stop(mantis); |
130 | } |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | int mantis_dvb_init(struct mantis_pci *mantis) |
136 | { |
137 | struct mantis_hwconfig *config = mantis->hwconfig; |
138 | int result; |
139 | |
140 | dprintk(MANTIS_DEBUG, 1, "dvb_register_adapter" ); |
141 | |
142 | result = dvb_register_adapter(adap: &mantis->dvb_adapter, |
143 | name: "Mantis DVB adapter" , |
144 | THIS_MODULE, |
145 | device: &mantis->pdev->dev, |
146 | adapter_nums: adapter_nr); |
147 | |
148 | if (result < 0) { |
149 | |
150 | dprintk(MANTIS_ERROR, 1, "Error registering adapter" ); |
151 | return -ENODEV; |
152 | } |
153 | |
154 | mantis->dvb_adapter.priv = mantis; |
155 | mantis->demux.dmx.capabilities = DMX_TS_FILTERING | |
156 | DMX_SECTION_FILTERING | |
157 | DMX_MEMORY_BASED_FILTERING; |
158 | |
159 | mantis->demux.priv = mantis; |
160 | mantis->demux.filternum = 256; |
161 | mantis->demux.feednum = 256; |
162 | mantis->demux.start_feed = mantis_dvb_start_feed; |
163 | mantis->demux.stop_feed = mantis_dvb_stop_feed; |
164 | mantis->demux.write_to_decoder = NULL; |
165 | |
166 | dprintk(MANTIS_DEBUG, 1, "dvb_dmx_init" ); |
167 | result = dvb_dmx_init(demux: &mantis->demux); |
168 | if (result < 0) { |
169 | dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d" , result); |
170 | |
171 | goto err0; |
172 | } |
173 | |
174 | mantis->dmxdev.filternum = 256; |
175 | mantis->dmxdev.demux = &mantis->demux.dmx; |
176 | mantis->dmxdev.capabilities = 0; |
177 | dprintk(MANTIS_DEBUG, 1, "dvb_dmxdev_init" ); |
178 | |
179 | result = dvb_dmxdev_init(dmxdev: &mantis->dmxdev, adap: &mantis->dvb_adapter); |
180 | if (result < 0) { |
181 | |
182 | dprintk(MANTIS_ERROR, 1, "dvb_dmxdev_init failed, ERROR=%d" , result); |
183 | goto err1; |
184 | } |
185 | |
186 | mantis->fe_hw.source = DMX_FRONTEND_0; |
187 | result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_hw); |
188 | if (result < 0) { |
189 | |
190 | dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d" , result); |
191 | goto err2; |
192 | } |
193 | |
194 | mantis->fe_mem.source = DMX_MEMORY_FE; |
195 | result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_mem); |
196 | if (result < 0) { |
197 | dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d" , result); |
198 | goto err3; |
199 | } |
200 | |
201 | result = mantis->demux.dmx.connect_frontend(&mantis->demux.dmx, &mantis->fe_hw); |
202 | if (result < 0) { |
203 | dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d" , result); |
204 | goto err4; |
205 | } |
206 | |
207 | dvb_net_init(adap: &mantis->dvb_adapter, dvbnet: &mantis->dvbnet, dmxdemux: &mantis->demux.dmx); |
208 | tasklet_setup(t: &mantis->tasklet, callback: mantis_dma_xfer); |
209 | tasklet_disable(t: &mantis->tasklet); |
210 | if (mantis->hwconfig) { |
211 | result = config->frontend_init(mantis, mantis->fe); |
212 | if (result < 0) { |
213 | dprintk(MANTIS_ERROR, 1, "!!! NO Frontends found !!!" ); |
214 | goto err5; |
215 | } else { |
216 | if (mantis->fe == NULL) { |
217 | result = -ENOMEM; |
218 | dprintk(MANTIS_ERROR, 1, "FE <NULL>" ); |
219 | goto err5; |
220 | } |
221 | result = dvb_register_frontend(dvb: &mantis->dvb_adapter, fe: mantis->fe); |
222 | if (result) { |
223 | dprintk(MANTIS_ERROR, 1, "ERROR: Frontend registration failed" ); |
224 | |
225 | if (mantis->fe->ops.release) |
226 | mantis->fe->ops.release(mantis->fe); |
227 | |
228 | mantis->fe = NULL; |
229 | goto err5; |
230 | } |
231 | } |
232 | } |
233 | |
234 | return 0; |
235 | |
236 | /* Error conditions .. */ |
237 | err5: |
238 | tasklet_kill(t: &mantis->tasklet); |
239 | dvb_net_release(dvbnet: &mantis->dvbnet); |
240 | if (mantis->fe) { |
241 | dvb_unregister_frontend(fe: mantis->fe); |
242 | dvb_frontend_detach(fe: mantis->fe); |
243 | } |
244 | err4: |
245 | mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); |
246 | |
247 | err3: |
248 | mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw); |
249 | |
250 | err2: |
251 | dvb_dmxdev_release(dmxdev: &mantis->dmxdev); |
252 | |
253 | err1: |
254 | dvb_dmx_release(demux: &mantis->demux); |
255 | |
256 | err0: |
257 | dvb_unregister_adapter(adap: &mantis->dvb_adapter); |
258 | |
259 | return result; |
260 | } |
261 | EXPORT_SYMBOL_GPL(mantis_dvb_init); |
262 | |
263 | int mantis_dvb_exit(struct mantis_pci *mantis) |
264 | { |
265 | int err; |
266 | |
267 | if (mantis->fe) { |
268 | /* mantis_ca_exit(mantis); */ |
269 | err = mantis_frontend_shutdown(mantis); |
270 | if (err != 0) |
271 | dprintk(MANTIS_ERROR, 1, "Frontend exit while POWER ON! <%d>" , err); |
272 | dvb_unregister_frontend(fe: mantis->fe); |
273 | dvb_frontend_detach(fe: mantis->fe); |
274 | } |
275 | |
276 | tasklet_kill(t: &mantis->tasklet); |
277 | dvb_net_release(dvbnet: &mantis->dvbnet); |
278 | |
279 | mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); |
280 | mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw); |
281 | |
282 | dvb_dmxdev_release(dmxdev: &mantis->dmxdev); |
283 | dvb_dmx_release(demux: &mantis->demux); |
284 | |
285 | dprintk(MANTIS_DEBUG, 1, "dvb_unregister_adapter" ); |
286 | dvb_unregister_adapter(adap: &mantis->dvb_adapter); |
287 | |
288 | return 0; |
289 | } |
290 | EXPORT_SYMBOL_GPL(mantis_dvb_exit); |
291 | |