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