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