• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
register_dvb(struct stdemux * demux,struct dvb_adapter * adap,void * start_feed,void * stop_feed,struct c8sectpfei * fei)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 
unregister_dvb(struct stdemux * demux)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 
c8sectpfe_create(struct c8sectpfei * fei,void * start_feed,void * stop_feed)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 
c8sectpfe_delete(struct c8sectpfe * c8sectpfe)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 
c8sectpfe_tuner_unregister_frontend(struct c8sectpfe * c8sectpfe,struct c8sectpfei * fei)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 
c8sectpfe_tuner_register_frontend(struct c8sectpfe ** c8sectpfe,struct c8sectpfei * fei,void * start_feed,void * stop_feed)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 }
266