blob: 90a70c4a9bcbaa9c7f1f01a89be1ff7f0a8f44c2
1 | /* |
2 | * Copyright (C) 2017 Amlogic, Inc. All rights reserved. |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation; either version 2 of the License, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, but WITHOUT |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
12 | * more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License along |
15 | * with this program; if not, write to the Free Software Foundation, Inc., |
16 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
17 | * |
18 | * Description: |
19 | */ |
20 | #include <linux/version.h> |
21 | #include <linux/kernel.h> |
22 | #include <linux/module.h> |
23 | #include <linux/mutex.h> |
24 | #include <linux/wait.h> |
25 | #include <linux/string.h> |
26 | #include <linux/interrupt.h> |
27 | #include <linux/fs.h> |
28 | #include <linux/cdev.h> |
29 | #include <linux/device.h> |
30 | #include <linux/spinlock.h> |
31 | #include <linux/fcntl.h> |
32 | //#include <asm/irq.h> |
33 | #include <linux/uaccess.h> |
34 | #include <linux/poll.h> |
35 | #include <linux/delay.h> |
36 | #include <linux/platform_device.h> |
37 | #include <linux/gpio.h> |
38 | #include <linux/string.h> |
39 | #include <linux/pinctrl/consumer.h> |
40 | #include <linux/reset.h> |
41 | #include <linux/of_gpio.h> |
42 | #include <linux/amlogic/media/utils/amstream.h> |
43 | //#include <linux/clk.h> |
44 | #include "c_stb_define.h" |
45 | #include "c_stb_regs_define.h" |
46 | #include "../aml_dvb.h" |
47 | #include "dvb_reg.h" |
48 | |
49 | #include "demod_gt.h" |
50 | #include "../../../../common/media_clock/switch/amports_gate.h" |
51 | |
52 | #define pr_error(fmt, args...) printk("DVB: " fmt, ## args) |
53 | #define pr_inf(fmt, args...) printk("DVB: " fmt, ## args) |
54 | |
55 | static struct dvb_frontend *frontend[FE_DEV_COUNT] = {NULL, NULL}; |
56 | static enum dtv_demod_type s_demod_type[FE_DEV_COUNT] = {AM_DTV_DEMOD_NONE, AM_DTV_DEMOD_NONE}; |
57 | static enum tuner_type s_tuner_type[FE_DEV_COUNT] = {AM_TUNER_NONE, AM_TUNER_NONE}; |
58 | |
59 | |
60 | ssize_t stb_show_tuner_setting(struct class *class, |
61 | struct class_attribute *attr, char *buf) |
62 | { |
63 | struct aml_dvb *dvb = aml_get_dvb_device(); |
64 | |
65 | if (dvb->tuner_cur >= 0) |
66 | pr_inf("dvb current attatch tuner %d, id: %d\n", |
67 | dvb->tuner_cur, dvb->tuners[dvb->tuner_cur].cfg.id); |
68 | else |
69 | pr_inf("dvb has no attatch tuner.\n"); |
70 | |
71 | return 0; |
72 | } |
73 | |
74 | ssize_t stb_store_tuner_setting(struct class *class, |
75 | struct class_attribute *attr, |
76 | const char *buf, size_t count) |
77 | { |
78 | int n = 0, i = 0, val = 0; |
79 | unsigned long tmp = 0; |
80 | char *buf_orig = NULL, *ps = NULL, *token = NULL; |
81 | char *parm[4] = { NULL }; |
82 | struct aml_dvb *dvb = aml_get_dvb_device(); |
83 | int tuner_id = 0; |
84 | struct aml_tuner *tuner = NULL; |
85 | |
86 | buf_orig = kstrdup(buf, GFP_KERNEL); |
87 | ps = buf_orig; |
88 | |
89 | while (1) { |
90 | token = strsep(&ps, "\n "); |
91 | if (token == NULL) |
92 | break; |
93 | if (*token == '\0') |
94 | continue; |
95 | parm[n++] = token; |
96 | } |
97 | |
98 | if (parm[0] && kstrtoul(parm[0], 10, &tmp) == 0) { |
99 | val = tmp; |
100 | |
101 | for (i = 0; i < dvb->tuner_num; ++i) { |
102 | if (dvb->tuners[i].cfg.id == val) { |
103 | tuner_id = dvb->tuners[i].cfg.id; |
104 | break; |
105 | } |
106 | } |
107 | |
108 | if (tuner_id == 0 || dvb->tuner_cur == i) { |
109 | pr_error("%s: set nonsupport or the same tuner %d.\n", |
110 | __func__, val); |
111 | goto EXIT; |
112 | } |
113 | |
114 | dvb->tuner_cur = i; |
115 | |
116 | for (i = 0; i < FE_DEV_COUNT; i++) { |
117 | tuner = &dvb->tuners[dvb->tuner_cur]; |
118 | |
119 | if (frontend[i] == NULL) |
120 | continue; |
121 | |
122 | if (aml_attach_tuner(tuner->cfg.id, frontend[i], &tuner->cfg) == NULL) { |
123 | s_tuner_type[i] = AM_TUNER_NONE; |
124 | pr_error("tuner[%d] [type = %d] attach error.\n", dvb->tuner_cur, tuner->cfg.id); |
125 | goto EXIT; |
126 | } else { |
127 | s_tuner_type[i] = tuner->cfg.id; |
128 | pr_error("tuner[%d] [type = %d] attach sucess.\n", dvb->tuner_cur, tuner->cfg.id); |
129 | } |
130 | } |
131 | |
132 | pr_error("%s: attach tuner %d done.\n", __func__, dvb->tuners[dvb->tuner_cur].cfg.id); |
133 | } |
134 | |
135 | EXIT: |
136 | |
137 | return count; |
138 | } |
139 | |
140 | |
141 | int frontend_probe(struct platform_device *pdev) |
142 | { |
143 | struct demod_config config; |
144 | struct tuner_config *tuner_cfg = NULL; |
145 | char buf[32]; |
146 | const char *str = NULL; |
147 | struct device_node *node_tuner = NULL; |
148 | struct device_node *node_i2c = NULL; |
149 | u32 i2c_addr = 0xFFFFFFFF; |
150 | u32 value = 0; |
151 | int i = 0; |
152 | int ret =0; |
153 | int j = 0; |
154 | struct aml_dvb *advb = aml_get_dvb_device(); |
155 | |
156 | for (i=0; i<FE_DEV_COUNT; i++) { |
157 | memset(&config, 0, sizeof(struct demod_config)); |
158 | |
159 | memset(buf, 0, 32); |
160 | snprintf(buf, sizeof(buf), "fe%d_mode", i); |
161 | ret = of_property_read_string(pdev->dev.of_node, buf, &str); |
162 | if (ret) { |
163 | continue; |
164 | } |
165 | |
166 | if (!strcmp(str, "internal")) { |
167 | config.mode = 0; |
168 | config.id = AM_DTV_DEMOD_AMLDTV; |
169 | frontend[i] = aml_attach_dtvdemod(config.id, &config); |
170 | if (frontend[i] == NULL) { |
171 | s_demod_type[i] = AM_DTV_DEMOD_NONE; |
172 | pr_error("internal dtvdemod [type = %d] attach error.\n", config.id); |
173 | goto error_fe; |
174 | } else { |
175 | s_demod_type[i] = config.id; |
176 | pr_error("internal dtvdemod [type = %d] attach success.\n", config.id); |
177 | } |
178 | |
179 | memset(buf, 0, 32); |
180 | snprintf(buf, sizeof(buf), "fe%d_tuner",i); |
181 | node_tuner = of_parse_phandle(pdev->dev.of_node, buf, 0); |
182 | if (!node_tuner){ |
183 | pr_err("can't find tuner.\n"); |
184 | goto error_fe; |
185 | } |
186 | ret = of_property_read_u32(node_tuner, "tuner_num", &value); |
187 | if (ret) { |
188 | pr_err("can't find tuner_num.\n"); |
189 | goto error_fe; |
190 | } else |
191 | advb->tuner_num = value; |
192 | |
193 | advb->tuners = kzalloc(sizeof(struct aml_tuner) * advb->tuner_num, GFP_KERNEL); |
194 | if (!advb->tuners) { |
195 | pr_err("can't kzalloc for tuners.\n"); |
196 | goto error_fe; |
197 | } |
198 | |
199 | ret = of_property_read_u32(node_tuner, "tuner_cur", &value); |
200 | if (ret) { |
201 | pr_err("can't find tuner_cur, use default 0.\n"); |
202 | advb->tuner_cur = -1; |
203 | } else |
204 | advb->tuner_cur = value; |
205 | |
206 | for (j = 0; j < advb->tuner_num; ++j) { |
207 | snprintf(buf, sizeof(buf), "tuner_name_%d", j); |
208 | ret = of_property_read_string(node_tuner, buf, &str); |
209 | if (ret) { |
210 | //pr_error("tuner%d type error\n",i); |
211 | ret = 0; |
212 | continue; |
213 | } else { |
214 | advb->tuners[j].cfg.id = aml_get_tuner_type(str); |
215 | if (advb->tuners[j].cfg.id == AM_TUNER_NONE) |
216 | pr_err("can't support tuner: %s.\n", str); |
217 | } |
218 | |
219 | snprintf(buf, sizeof(buf), "tuner_i2c_adap_%d", j); |
220 | node_i2c = of_parse_phandle(node_tuner, buf, 0); |
221 | if (!node_i2c) { |
222 | pr_error("tuner_i2c_adap_id error\n"); |
223 | } else { |
224 | advb->tuners[j].cfg.i2c_adap = of_find_i2c_adapter_by_node(node_i2c); |
225 | of_node_put(node_i2c); |
226 | if (advb->tuners[j].cfg.i2c_adap == NULL) { |
227 | pr_error("i2c_get_adapter error\n"); |
228 | of_node_put(node_tuner); |
229 | goto error_fe; |
230 | } |
231 | } |
232 | |
233 | snprintf(buf, sizeof(buf), "tuner_i2c_addr_%d", j); |
234 | ret = of_property_read_u32(node_tuner, buf, &i2c_addr); |
235 | if (ret) { |
236 | pr_error("i2c_addr error\n"); |
237 | } |
238 | else |
239 | advb->tuners[j].cfg.i2c_addr = i2c_addr; |
240 | |
241 | snprintf(buf, sizeof(buf), "tuner_xtal_%d", j); |
242 | ret = of_property_read_u32(node_tuner, buf, &value); |
243 | if (ret) |
244 | pr_err("tuner_xtal error.\n"); |
245 | else |
246 | advb->tuners[j].cfg.xtal = value; |
247 | |
248 | snprintf(buf, sizeof(buf), "tuner_xtal_mode_%d", j); |
249 | ret = of_property_read_u32(node_tuner, buf, &value); |
250 | if (ret) |
251 | pr_err("tuner_xtal_mode error.\n"); |
252 | else |
253 | advb->tuners[j].cfg.xtal_mode = value; |
254 | |
255 | snprintf(buf, sizeof(buf), "tuner_xtal_cap_%d", j); |
256 | ret = of_property_read_u32(node_tuner, buf, &value); |
257 | if (ret) |
258 | pr_err("tuner_xtal_cap error.\n"); |
259 | else |
260 | advb->tuners[j].cfg.xtal_cap = value; |
261 | } |
262 | |
263 | of_node_put(node_tuner); |
264 | |
265 | /* define general-purpose callback pointer */ |
266 | frontend[i]->callback = NULL; |
267 | |
268 | if (advb->tuner_cur >= 0) { |
269 | tuner_cfg = &advb->tuners[advb->tuner_cur].cfg; |
270 | if (aml_attach_tuner(tuner_cfg->id, frontend[i], tuner_cfg) == NULL) { |
271 | s_tuner_type[i] = AM_TUNER_NONE; |
272 | pr_error("tuner [type = %d] attach error.\n", tuner_cfg->id); |
273 | goto error_fe; |
274 | } else { |
275 | s_tuner_type[i] = tuner_cfg->id; |
276 | pr_error("tuner [type = %d] attach sucess.\n", tuner_cfg->id); |
277 | } |
278 | } |
279 | |
280 | ret = dvb_register_frontend(&advb->dvb_adapter, frontend[i]); |
281 | if (ret) { |
282 | pr_error("register dvb frontend failed\n"); |
283 | goto error_fe; |
284 | } |
285 | } else if(!strcmp(str, "external")) { |
286 | config.mode = 1; |
287 | config.id = AM_DTV_DEMOD_NONE; |
288 | ret = aml_get_dts_demod_config(pdev->dev.of_node, &config, i); |
289 | if (ret) { |
290 | pr_err("can't find demod %d.\n", i); |
291 | continue; |
292 | } |
293 | |
294 | memset(buf, 0, 32); |
295 | snprintf(buf, sizeof(buf), "fe%d_tuner", i); |
296 | node_tuner = of_parse_phandle(pdev->dev.of_node, buf, 0); |
297 | if (node_tuner) { |
298 | aml_get_dts_tuner_config(node_tuner, &config.tuner0, 0); |
299 | aml_get_dts_tuner_config(node_tuner, &config.tuner1, 1); |
300 | } else |
301 | pr_err("can't find %s.\n", buf); |
302 | |
303 | of_node_put(node_tuner); |
304 | |
305 | frontend[i] = aml_attach_dtvdemod(config.id, &config); |
306 | if (frontend[i] == NULL) { |
307 | s_demod_type[i] = AM_DTV_DEMOD_NONE; |
308 | pr_error("external dtvdemod [type = %d] attach error.\n", config.id); |
309 | goto error_fe; |
310 | } else { |
311 | s_demod_type[i] = config.id; |
312 | pr_error("external dtvdemod [type = %d] attach success.\n", config.id); |
313 | } |
314 | |
315 | if (frontend[i]) { |
316 | ret = dvb_register_frontend(&advb->dvb_adapter, frontend[i]); |
317 | if (ret) { |
318 | pr_error("register dvb frontend failed\n"); |
319 | goto error_fe; |
320 | } |
321 | } |
322 | } |
323 | } |
324 | if (advb->tuners) |
325 | kfree(advb->tuners); |
326 | return 0; |
327 | error_fe: |
328 | for (i=0; i<FE_DEV_COUNT; i++) { |
329 | aml_detach_dtvdemod(s_demod_type[i]); |
330 | frontend[i] = NULL; |
331 | s_demod_type[i] = AM_DTV_DEMOD_NONE; |
332 | |
333 | aml_detach_tuner(s_tuner_type[i]); |
334 | s_tuner_type[i] = AM_TUNER_NONE; |
335 | } |
336 | |
337 | if (advb->tuners) |
338 | kfree(advb->tuners); |
339 | |
340 | return 0; |
341 | } |
342 | |
343 | int frontend_remove(void) |
344 | { |
345 | int i; |
346 | |
347 | for (i=0; i<FE_DEV_COUNT; i++) { |
348 | aml_detach_dtvdemod(s_demod_type[i]); |
349 | |
350 | aml_detach_tuner(s_tuner_type[i]); |
351 | |
352 | if (frontend[i] && |
353 | ((s_tuner_type[i] == AM_TUNER_SI2151) |
354 | || (s_tuner_type[i] == AM_TUNER_MXL661) |
355 | || (s_tuner_type[i] == AM_TUNER_SI2159) |
356 | || (s_tuner_type[i] == AM_TUNER_R842) |
357 | || (s_tuner_type[i] == AM_TUNER_R840) |
358 | || (s_tuner_type[i] == AM_TUNER_ATBM2040))) { |
359 | dvb_unregister_frontend(frontend[i]); |
360 | dvb_frontend_detach(frontend[i]); |
361 | } |
362 | |
363 | frontend[i] = NULL; |
364 | s_demod_type[i] = AM_DTV_DEMOD_NONE; |
365 | s_tuner_type[i] = AM_TUNER_NONE; |
366 | |
367 | } |
368 | return 0; |
369 | } |
370 | |
371 |