xref: /linux/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c (revision 58f6259b7a08f8d47d4629609703d358b042f0fd)
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 }
263