xc3028.c revision 1.6 1 1.6 ozaki /* $NetBSD: xc3028.c,v 1.6 2015/01/07 07:05:48 ozaki-r Exp $ */
2 1.1 jmcneill
3 1.1 jmcneill /*-
4 1.1 jmcneill * Copyright (c) 2011 Jared D. McNeill <jmcneill (at) invisible.ca>
5 1.1 jmcneill * All rights reserved.
6 1.1 jmcneill *
7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without
8 1.1 jmcneill * modification, are permitted provided that the following conditions
9 1.1 jmcneill * are met:
10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright
11 1.1 jmcneill * notice, this list of conditions and the following disclaimer.
12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the
14 1.1 jmcneill * documentation and/or other materials provided with the distribution.
15 1.1 jmcneill *
16 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 jmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 jmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 jmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 jmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 jmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 jmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 jmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 jmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 jmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 jmcneill * POSSIBILITY OF SUCH DAMAGE.
27 1.1 jmcneill */
28 1.1 jmcneill
29 1.1 jmcneill /*
30 1.1 jmcneill * Xceive XC3028L
31 1.1 jmcneill */
32 1.1 jmcneill
33 1.1 jmcneill #include <sys/cdefs.h>
34 1.6 ozaki __KERNEL_RCSID(0, "$NetBSD: xc3028.c,v 1.6 2015/01/07 07:05:48 ozaki-r Exp $");
35 1.1 jmcneill
36 1.1 jmcneill #include <sys/param.h>
37 1.1 jmcneill #include <sys/systm.h>
38 1.1 jmcneill #include <sys/device.h>
39 1.1 jmcneill #include <sys/conf.h>
40 1.1 jmcneill #include <sys/bus.h>
41 1.1 jmcneill #include <sys/kmem.h>
42 1.1 jmcneill #include <sys/mutex.h>
43 1.1 jmcneill #include <sys/module.h>
44 1.1 jmcneill
45 1.1 jmcneill #include <dev/firmload.h>
46 1.1 jmcneill #include <dev/i2c/i2cvar.h>
47 1.1 jmcneill
48 1.1 jmcneill #include <dev/i2c/xc3028reg.h>
49 1.1 jmcneill #include <dev/i2c/xc3028var.h>
50 1.1 jmcneill
51 1.1 jmcneill #define XC3028_FIRMWARE_DRVNAME "xc3028"
52 1.1 jmcneill
53 1.1 jmcneill #define XC3028_FREQ_MIN 1000000
54 1.1 jmcneill #define XC3028_FREQ_MAX 1023000000
55 1.1 jmcneill
56 1.1 jmcneill #define XC3028_FW_BASE (1 << 0)
57 1.1 jmcneill #define XC3028_FW_D2633 (1 << 4)
58 1.1 jmcneill #define XC3028_FW_DTV6 (1 << 5)
59 1.1 jmcneill #define XC3028_FW_QAM (1 << 6)
60 1.1 jmcneill #define XC3028_FW_ATSC (1 << 16)
61 1.1 jmcneill #define XC3028_FW_LG60 (1 << 18)
62 1.1 jmcneill #define XC3028_FW_F6MHZ (1 << 27)
63 1.1 jmcneill #define XC3028_FW_SCODE (1 << 29)
64 1.1 jmcneill #define XC3028_FW_HAS_IF (1 << 30)
65 1.1 jmcneill
66 1.1 jmcneill #define XC3028_FW_DEFAULT (XC3028_FW_ATSC|XC3028_FW_D2633|XC3028_FW_DTV6)
67 1.1 jmcneill
68 1.1 jmcneill static kmutex_t xc3028_firmware_lock;
69 1.1 jmcneill
70 1.1 jmcneill static int xc3028_reset(struct xc3028 *);
71 1.1 jmcneill static int xc3028_read_2(struct xc3028 *, uint16_t, uint16_t *);
72 1.1 jmcneill static int xc3028_write_buffer(struct xc3028 *, const uint8_t *, size_t);
73 1.1 jmcneill static int xc3028_firmware_open(struct xc3028 *);
74 1.1 jmcneill static int xc3028_firmware_parse(struct xc3028 *, const uint8_t *, size_t);
75 1.1 jmcneill static int xc3028_firmware_upload(struct xc3028 *, struct xc3028_fw *);
76 1.1 jmcneill static int xc3028_scode_upload(struct xc3028 *, struct xc3028_fw *);
77 1.1 jmcneill static void xc3028_dump_fw(struct xc3028 *, struct xc3028_fw *,
78 1.1 jmcneill const char *);
79 1.1 jmcneill
80 1.1 jmcneill static const char *
81 1.1 jmcneill xc3028_name(struct xc3028 *xc)
82 1.1 jmcneill {
83 1.1 jmcneill if (xc->type == XC3028L)
84 1.1 jmcneill return "xc3028l";
85 1.1 jmcneill else
86 1.1 jmcneill return "xc3028";
87 1.1 jmcneill }
88 1.1 jmcneill
89 1.1 jmcneill static const char *
90 1.1 jmcneill xc3028_firmware_name(struct xc3028 *xc)
91 1.1 jmcneill {
92 1.1 jmcneill if (xc->type == XC3028L)
93 1.1 jmcneill return "xc3028L-v36.fw";
94 1.1 jmcneill else
95 1.1 jmcneill return "xc3028-v27.fw";
96 1.1 jmcneill }
97 1.1 jmcneill
98 1.1 jmcneill static int
99 1.1 jmcneill xc3028_reset(struct xc3028 *xc)
100 1.1 jmcneill {
101 1.1 jmcneill int error = 0;
102 1.1 jmcneill
103 1.1 jmcneill if (xc->reset)
104 1.1 jmcneill error = xc->reset(xc->reset_priv);
105 1.1 jmcneill
106 1.1 jmcneill return error;
107 1.1 jmcneill }
108 1.1 jmcneill
109 1.1 jmcneill static struct xc3028_fw *
110 1.1 jmcneill xc3028_get_basefw(struct xc3028 *xc)
111 1.1 jmcneill {
112 1.1 jmcneill struct xc3028_fw *fw;
113 1.1 jmcneill unsigned int i;
114 1.1 jmcneill
115 1.1 jmcneill for (i = 0; i < xc->nfw; i++) {
116 1.1 jmcneill fw = &xc->fw[i];
117 1.1 jmcneill if (fw->type == XC3028_FW_BASE)
118 1.1 jmcneill return fw;
119 1.1 jmcneill }
120 1.1 jmcneill
121 1.1 jmcneill return NULL;
122 1.1 jmcneill }
123 1.1 jmcneill
124 1.1 jmcneill static struct xc3028_fw *
125 1.1 jmcneill xc3028_get_stdfw(struct xc3028 *xc)
126 1.1 jmcneill {
127 1.1 jmcneill struct xc3028_fw *fw;
128 1.1 jmcneill unsigned int i;
129 1.1 jmcneill
130 1.1 jmcneill for (i = 0; i < xc->nfw; i++) {
131 1.1 jmcneill fw = &xc->fw[i];
132 1.1 jmcneill if (fw->type == (XC3028_FW_D2633|XC3028_FW_DTV6|XC3028_FW_ATSC))
133 1.1 jmcneill return fw;
134 1.1 jmcneill }
135 1.1 jmcneill
136 1.1 jmcneill return NULL;
137 1.1 jmcneill }
138 1.1 jmcneill
139 1.1 jmcneill static struct xc3028_fw *
140 1.1 jmcneill xc3028_get_scode(struct xc3028 *xc)
141 1.1 jmcneill {
142 1.1 jmcneill struct xc3028_fw *fw;
143 1.1 jmcneill unsigned int i;
144 1.1 jmcneill
145 1.1 jmcneill for (i = 0; i < xc->nfw; i++) {
146 1.1 jmcneill fw = &xc->fw[i];
147 1.1 jmcneill if (fw->type ==
148 1.1 jmcneill (XC3028_FW_DTV6|XC3028_FW_QAM|XC3028_FW_ATSC|XC3028_FW_LG60|
149 1.1 jmcneill XC3028_FW_F6MHZ|XC3028_FW_SCODE|XC3028_FW_HAS_IF) &&
150 1.1 jmcneill fw->int_freq == 6200)
151 1.1 jmcneill return fw;
152 1.1 jmcneill }
153 1.1 jmcneill
154 1.1 jmcneill return NULL;
155 1.1 jmcneill }
156 1.1 jmcneill
157 1.1 jmcneill static int
158 1.1 jmcneill xc3028_firmware_open(struct xc3028 *xc)
159 1.1 jmcneill {
160 1.1 jmcneill firmware_handle_t fwh;
161 1.1 jmcneill struct xc3028_fw *basefw, *stdfw, *scode;
162 1.1 jmcneill uint8_t *fw = NULL;
163 1.1 jmcneill uint16_t xcversion = 0;
164 1.1 jmcneill size_t fwlen;
165 1.1 jmcneill int error;
166 1.1 jmcneill
167 1.1 jmcneill mutex_enter(&xc3028_firmware_lock);
168 1.1 jmcneill
169 1.1 jmcneill error = firmware_open(XC3028_FIRMWARE_DRVNAME,
170 1.1 jmcneill xc3028_firmware_name(xc), &fwh);
171 1.1 jmcneill if (error)
172 1.1 jmcneill goto done;
173 1.1 jmcneill fwlen = firmware_get_size(fwh);
174 1.1 jmcneill fw = firmware_malloc(fwlen);
175 1.1 jmcneill if (fw == NULL) {
176 1.1 jmcneill firmware_close(fwh);
177 1.1 jmcneill error = ENOMEM;
178 1.1 jmcneill goto done;
179 1.1 jmcneill }
180 1.1 jmcneill error = firmware_read(fwh, 0, fw, fwlen);
181 1.1 jmcneill firmware_close(fwh);
182 1.1 jmcneill if (error)
183 1.1 jmcneill goto done;
184 1.1 jmcneill
185 1.1 jmcneill device_printf(xc->parent, "%s: loading firmware '%s/%s'\n",
186 1.1 jmcneill xc3028_name(xc), XC3028_FIRMWARE_DRVNAME, xc3028_firmware_name(xc));
187 1.1 jmcneill error = xc3028_firmware_parse(xc, fw, fwlen);
188 1.1 jmcneill if (!error) {
189 1.1 jmcneill basefw = xc3028_get_basefw(xc);
190 1.1 jmcneill stdfw = xc3028_get_stdfw(xc);
191 1.1 jmcneill scode = xc3028_get_scode(xc);
192 1.1 jmcneill if (basefw && stdfw) {
193 1.1 jmcneill xc3028_reset(xc);
194 1.1 jmcneill xc3028_dump_fw(xc, basefw, "base");
195 1.1 jmcneill error = xc3028_firmware_upload(xc, basefw);
196 1.1 jmcneill if (error)
197 1.1 jmcneill return error;
198 1.1 jmcneill xc3028_dump_fw(xc, stdfw, "std");
199 1.1 jmcneill error = xc3028_firmware_upload(xc, stdfw);
200 1.1 jmcneill if (error)
201 1.1 jmcneill return error;
202 1.1 jmcneill if (scode) {
203 1.1 jmcneill xc3028_dump_fw(xc, scode, "scode");
204 1.1 jmcneill error = xc3028_scode_upload(xc, scode);
205 1.1 jmcneill if (error)
206 1.1 jmcneill return error;
207 1.1 jmcneill }
208 1.1 jmcneill } else
209 1.1 jmcneill error = ENODEV;
210 1.1 jmcneill }
211 1.1 jmcneill if (!error) {
212 1.1 jmcneill xc3028_read_2(xc, XC3028_REG_VERSION, &xcversion);
213 1.1 jmcneill
214 1.1 jmcneill device_printf(xc->parent, "%s: hw %d.%d, fw %d.%d\n",
215 1.1 jmcneill xc3028_name(xc),
216 1.1 jmcneill (xcversion >> 12) & 0xf, (xcversion >> 8) & 0xf,
217 1.1 jmcneill (xcversion >> 4) & 0xf, (xcversion >> 0) & 0xf);
218 1.1 jmcneill }
219 1.1 jmcneill
220 1.1 jmcneill done:
221 1.1 jmcneill if (fw)
222 1.6 ozaki firmware_free(fw, fwlen);
223 1.1 jmcneill mutex_exit(&xc3028_firmware_lock);
224 1.1 jmcneill
225 1.1 jmcneill if (error)
226 1.1 jmcneill aprint_error_dev(xc->parent,
227 1.1 jmcneill "%s: couldn't open firmware '%s/%s' (error=%d)\n",
228 1.1 jmcneill xc3028_name(xc), XC3028_FIRMWARE_DRVNAME,
229 1.1 jmcneill xc3028_firmware_name(xc), error);
230 1.1 jmcneill
231 1.1 jmcneill return error;
232 1.1 jmcneill }
233 1.1 jmcneill
234 1.1 jmcneill static const char *xc3028_fw_types[] = {
235 1.1 jmcneill "BASE",
236 1.1 jmcneill "F8MHZ",
237 1.1 jmcneill "MTS",
238 1.1 jmcneill "D2620",
239 1.1 jmcneill "D2633",
240 1.1 jmcneill "DTV6",
241 1.1 jmcneill "QAM",
242 1.1 jmcneill "DTV7",
243 1.1 jmcneill "DTV78",
244 1.1 jmcneill "DTV8",
245 1.1 jmcneill "FM",
246 1.1 jmcneill "INPUT1",
247 1.1 jmcneill "LCD",
248 1.1 jmcneill "NOGD",
249 1.1 jmcneill "INIT1",
250 1.1 jmcneill "MONO",
251 1.1 jmcneill "ATSC",
252 1.1 jmcneill "IF",
253 1.1 jmcneill "LG60",
254 1.1 jmcneill "ATI638",
255 1.1 jmcneill "OREN538",
256 1.1 jmcneill "OREN36",
257 1.1 jmcneill "TOYOTA388",
258 1.1 jmcneill "TOYOTA794",
259 1.1 jmcneill "DIBCOM52",
260 1.1 jmcneill "ZARLINK456",
261 1.1 jmcneill "CHINA",
262 1.1 jmcneill "F6MHZ",
263 1.1 jmcneill "INPUT2",
264 1.1 jmcneill "SCODE",
265 1.1 jmcneill "HAS_IF",
266 1.1 jmcneill };
267 1.1 jmcneill
268 1.1 jmcneill static void
269 1.1 jmcneill xc3028_dump_fw(struct xc3028 *xc, struct xc3028_fw *xcfw, const char *type)
270 1.1 jmcneill {
271 1.1 jmcneill unsigned int i;
272 1.1 jmcneill
273 1.1 jmcneill device_printf(xc->parent, "%s: %s:", xc3028_name(xc), type);
274 1.1 jmcneill if (xcfw == NULL) {
275 1.1 jmcneill printf(" <none>\n");
276 1.1 jmcneill return;
277 1.1 jmcneill }
278 1.1 jmcneill for (i = 0; i < __arraycount(xc3028_fw_types); i++) {
279 1.1 jmcneill if (xcfw->type & (1 << i))
280 1.1 jmcneill printf(" %s", xc3028_fw_types[i]);
281 1.1 jmcneill }
282 1.1 jmcneill if (xcfw->type & (1 << 30))
283 1.1 jmcneill printf("_%d", xcfw->int_freq);
284 1.1 jmcneill if (xcfw->id)
285 1.2 njoly printf(" id=%" PRIx64, xcfw->id);
286 1.1 jmcneill printf(" size=%u\n", xcfw->data_size);
287 1.1 jmcneill }
288 1.1 jmcneill
289 1.1 jmcneill static int
290 1.1 jmcneill xc3028_firmware_parse(struct xc3028 *xc, const uint8_t *fw, size_t fwlen)
291 1.1 jmcneill {
292 1.1 jmcneill const uint8_t *p = fw, *endp = p + fwlen;
293 1.1 jmcneill char fwname[32 + 1];
294 1.1 jmcneill uint16_t fwver, narr;
295 1.1 jmcneill unsigned int index;
296 1.1 jmcneill struct xc3028_fw *xcfw;
297 1.1 jmcneill
298 1.1 jmcneill if (fwlen < 36)
299 1.1 jmcneill return EINVAL;
300 1.1 jmcneill
301 1.1 jmcneill /* first 32 bytes are the firmware name string */
302 1.1 jmcneill memset(fwname, 0, sizeof(fwname));
303 1.1 jmcneill memcpy(fwname, p, sizeof(fwname) - 1);
304 1.1 jmcneill p += (sizeof(fwname) - 1);
305 1.1 jmcneill
306 1.1 jmcneill fwver = le16dec(p);
307 1.1 jmcneill p += sizeof(fwver);
308 1.1 jmcneill narr = le16dec(p);
309 1.1 jmcneill p += sizeof(narr);
310 1.1 jmcneill
311 1.1 jmcneill aprint_debug_dev(xc->parent, "%s: fw type %s, ver %d.%d, %d images\n",
312 1.1 jmcneill xc3028_name(xc), fwname, fwver >> 8, fwver & 0xff, narr);
313 1.1 jmcneill
314 1.1 jmcneill xc->fw = kmem_zalloc(sizeof(*xc->fw) * narr, KM_SLEEP);
315 1.1 jmcneill if (xc->fw == NULL)
316 1.1 jmcneill return ENOMEM;
317 1.1 jmcneill xc->nfw = narr;
318 1.1 jmcneill
319 1.1 jmcneill for (index = 0; index < xc->nfw && p < endp; index++) {
320 1.1 jmcneill xcfw = &xc->fw[index];
321 1.1 jmcneill
322 1.1 jmcneill if (endp - p < 16)
323 1.1 jmcneill goto corrupt;
324 1.1 jmcneill
325 1.1 jmcneill xcfw->type = le32dec(p);
326 1.1 jmcneill p += sizeof(xcfw->type);
327 1.1 jmcneill
328 1.1 jmcneill xcfw->id = le64dec(p);
329 1.1 jmcneill p += sizeof(xcfw->id);
330 1.1 jmcneill
331 1.1 jmcneill if (xcfw->type & XC3028_FW_HAS_IF) {
332 1.1 jmcneill xcfw->int_freq = le16dec(p);
333 1.1 jmcneill p += sizeof(xcfw->int_freq);
334 1.1 jmcneill if ((uint32_t)(endp - p) < sizeof(xcfw->data_size))
335 1.1 jmcneill goto corrupt;
336 1.1 jmcneill }
337 1.1 jmcneill
338 1.1 jmcneill xcfw->data_size = le32dec(p);
339 1.1 jmcneill p += sizeof(xcfw->data_size);
340 1.1 jmcneill
341 1.1 jmcneill if (xcfw->data_size == 0 ||
342 1.1 jmcneill xcfw->data_size > (uint32_t)(endp - p))
343 1.1 jmcneill goto corrupt;
344 1.1 jmcneill xcfw->data = kmem_alloc(xcfw->data_size, KM_SLEEP);
345 1.1 jmcneill if (xcfw->data == NULL)
346 1.1 jmcneill goto corrupt;
347 1.1 jmcneill memcpy(xcfw->data, p, xcfw->data_size);
348 1.1 jmcneill p += xcfw->data_size;
349 1.1 jmcneill }
350 1.1 jmcneill
351 1.1 jmcneill return 0;
352 1.1 jmcneill
353 1.1 jmcneill corrupt:
354 1.1 jmcneill aprint_error_dev(xc->parent, "%s: fw image corrupt\n", xc3028_name(xc));
355 1.1 jmcneill for (index = 0; index < xc->nfw; index++) {
356 1.1 jmcneill if (xc->fw[index].data)
357 1.1 jmcneill kmem_free(xc->fw[index].data, xc->fw[index].data_size);
358 1.1 jmcneill }
359 1.1 jmcneill kmem_free(xc->fw, sizeof(*xc->fw) * xc->nfw);
360 1.1 jmcneill xc->nfw = 0;
361 1.1 jmcneill
362 1.1 jmcneill return ENXIO;
363 1.1 jmcneill }
364 1.1 jmcneill
365 1.1 jmcneill static int
366 1.1 jmcneill xc3028_firmware_upload(struct xc3028 *xc, struct xc3028_fw *xcfw)
367 1.1 jmcneill {
368 1.1 jmcneill const uint8_t *fw = xcfw->data, *p;
369 1.1 jmcneill uint32_t fwlen = xcfw->data_size;
370 1.1 jmcneill uint8_t cmd[64];
371 1.1 jmcneill unsigned int i;
372 1.1 jmcneill uint16_t len, rem;
373 1.1 jmcneill size_t wrlen;
374 1.1 jmcneill int error;
375 1.1 jmcneill
376 1.1 jmcneill for (i = 0; i < fwlen - 2;) {
377 1.1 jmcneill len = le16dec(&fw[i]);
378 1.1 jmcneill i += 2;
379 1.1 jmcneill if (len == 0xffff)
380 1.1 jmcneill break;
381 1.1 jmcneill
382 1.1 jmcneill /* reset command */
383 1.1 jmcneill if (len == 0x0000) {
384 1.1 jmcneill error = xc3028_reset(xc);
385 1.1 jmcneill if (error)
386 1.1 jmcneill return error;
387 1.1 jmcneill continue;
388 1.1 jmcneill }
389 1.1 jmcneill /* reset clk command */
390 1.1 jmcneill if (len == 0xff00) {
391 1.1 jmcneill continue;
392 1.1 jmcneill }
393 1.1 jmcneill /* delay command */
394 1.1 jmcneill if (len & 0x8000) {
395 1.1 jmcneill delay((len & 0x7fff) * 1000);
396 1.1 jmcneill continue;
397 1.1 jmcneill }
398 1.1 jmcneill
399 1.1 jmcneill if (i + len > fwlen) {
400 1.1 jmcneill printf("weird len, i=%u len=%u fwlen=%u'\n", i, len, fwlen);
401 1.1 jmcneill return EINVAL;
402 1.1 jmcneill }
403 1.1 jmcneill
404 1.1 jmcneill cmd[0] = fw[i];
405 1.1 jmcneill p = &fw[i + 1];
406 1.1 jmcneill rem = len - 1;
407 1.1 jmcneill while (rem > 0) {
408 1.1 jmcneill wrlen = min(rem, __arraycount(cmd) - 1);
409 1.1 jmcneill memcpy(&cmd[1], p, wrlen);
410 1.1 jmcneill error = xc3028_write_buffer(xc, cmd, wrlen + 1);
411 1.1 jmcneill if (error)
412 1.1 jmcneill return error;
413 1.1 jmcneill p += wrlen;
414 1.1 jmcneill rem -= wrlen;
415 1.1 jmcneill }
416 1.1 jmcneill i += len;
417 1.1 jmcneill }
418 1.1 jmcneill
419 1.1 jmcneill return 0;
420 1.1 jmcneill }
421 1.1 jmcneill
422 1.1 jmcneill static int
423 1.1 jmcneill xc3028_scode_upload(struct xc3028 *xc, struct xc3028_fw *xcfw)
424 1.1 jmcneill {
425 1.1 jmcneill static uint8_t scode_init[] = { 0xa0, 0x00, 0x00, 0x00 };
426 1.1 jmcneill static uint8_t scode_fini[] = { 0x00, 0x8c };
427 1.1 jmcneill int error;
428 1.1 jmcneill
429 1.1 jmcneill if (xcfw->data_size < 12)
430 1.1 jmcneill return EINVAL;
431 1.1 jmcneill error = xc3028_write_buffer(xc, scode_init, sizeof(scode_init));
432 1.1 jmcneill if (error)
433 1.1 jmcneill return error;
434 1.1 jmcneill error = xc3028_write_buffer(xc, xcfw->data, 12);
435 1.1 jmcneill if (error)
436 1.1 jmcneill return error;
437 1.1 jmcneill error = xc3028_write_buffer(xc, scode_fini, sizeof(scode_fini));
438 1.1 jmcneill if (error)
439 1.1 jmcneill return error;
440 1.1 jmcneill
441 1.1 jmcneill return 0;
442 1.1 jmcneill }
443 1.1 jmcneill
444 1.1 jmcneill static int
445 1.1 jmcneill xc3028_read_2(struct xc3028 *xc, uint16_t reg, uint16_t *val)
446 1.1 jmcneill {
447 1.1 jmcneill uint8_t cmd[2], resp[2];
448 1.1 jmcneill int error;
449 1.1 jmcneill
450 1.1 jmcneill cmd[0] = reg >> 8;
451 1.1 jmcneill cmd[1] = reg & 0xff;
452 1.1 jmcneill error = iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr,
453 1.1 jmcneill cmd, sizeof(cmd), NULL, 0, 0);
454 1.1 jmcneill if (error)
455 1.1 jmcneill return error;
456 1.1 jmcneill resp[0] = resp[1] = 0;
457 1.1 jmcneill error = iic_exec(xc->i2c, I2C_OP_READ, xc->i2c_addr,
458 1.1 jmcneill NULL, 0, resp, sizeof(resp), 0);
459 1.1 jmcneill if (error)
460 1.1 jmcneill return error;
461 1.1 jmcneill
462 1.1 jmcneill *val = (resp[0] << 8) | resp[1];
463 1.1 jmcneill
464 1.1 jmcneill return 0;
465 1.1 jmcneill }
466 1.1 jmcneill
467 1.1 jmcneill static int
468 1.1 jmcneill xc3028_write_buffer(struct xc3028 *xc, const uint8_t *data, size_t datalen)
469 1.1 jmcneill {
470 1.1 jmcneill return iic_exec(xc->i2c, I2C_OP_WRITE_WITH_STOP, xc->i2c_addr,
471 1.1 jmcneill data, datalen, NULL, 0, 0);
472 1.1 jmcneill }
473 1.1 jmcneill
474 1.1 jmcneill #if notyet
475 1.1 jmcneill static int
476 1.1 jmcneill xc3028_write_2(struct xc3028 *xc, uint16_t reg, uint16_t val)
477 1.1 jmcneill {
478 1.1 jmcneill uint8_t data[4];
479 1.1 jmcneill
480 1.1 jmcneill data[0] = reg >> 8;
481 1.1 jmcneill data[1] = reg & 0xff;
482 1.1 jmcneill data[2] = val >> 8;
483 1.1 jmcneill data[3] = val & 0xff;
484 1.1 jmcneill
485 1.1 jmcneill return xc3028_write_buffer(xc, data, sizeof(data));
486 1.1 jmcneill }
487 1.1 jmcneill #endif
488 1.1 jmcneill
489 1.1 jmcneill struct xc3028 *
490 1.1 jmcneill xc3028_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr,
491 1.1 jmcneill xc3028_reset_cb reset, void *reset_priv, enum xc3028_type type)
492 1.1 jmcneill {
493 1.1 jmcneill struct xc3028 *xc;
494 1.1 jmcneill
495 1.1 jmcneill xc = kmem_alloc(sizeof(*xc), KM_SLEEP);
496 1.1 jmcneill if (xc == NULL)
497 1.1 jmcneill return NULL;
498 1.1 jmcneill xc->parent = parent;
499 1.1 jmcneill xc->i2c = i2c;
500 1.1 jmcneill xc->i2c_addr = addr;
501 1.1 jmcneill xc->reset = reset;
502 1.1 jmcneill xc->reset_priv = reset_priv;
503 1.1 jmcneill xc->type = type;
504 1.1 jmcneill
505 1.1 jmcneill if (xc3028_firmware_open(xc)) {
506 1.1 jmcneill aprint_error_dev(parent, "%s: fw open failed\n",
507 1.1 jmcneill xc3028_name(xc));
508 1.1 jmcneill goto failed;
509 1.1 jmcneill }
510 1.1 jmcneill
511 1.1 jmcneill return xc;
512 1.1 jmcneill
513 1.1 jmcneill failed:
514 1.1 jmcneill kmem_free(xc, sizeof(*xc));
515 1.1 jmcneill return NULL;
516 1.1 jmcneill }
517 1.1 jmcneill
518 1.1 jmcneill void
519 1.1 jmcneill xc3028_close(struct xc3028 *xc)
520 1.1 jmcneill {
521 1.1 jmcneill unsigned int index;
522 1.1 jmcneill
523 1.1 jmcneill if (xc->fw) {
524 1.1 jmcneill for (index = 0; index < xc->nfw; index++) {
525 1.1 jmcneill if (xc->fw[index].data)
526 1.1 jmcneill kmem_free(xc->fw[index].data,
527 1.1 jmcneill xc->fw[index].data_size);
528 1.1 jmcneill }
529 1.1 jmcneill kmem_free(xc->fw, sizeof(*xc->fw) * xc->nfw);
530 1.1 jmcneill }
531 1.1 jmcneill kmem_free(xc, sizeof(*xc));
532 1.1 jmcneill }
533 1.1 jmcneill
534 1.1 jmcneill int
535 1.1 jmcneill xc3028_tune_dtv(struct xc3028 *xc, const struct dvb_frontend_parameters *params)
536 1.1 jmcneill {
537 1.1 jmcneill static uint8_t freq_init[] = { 0x80, 0x02, 0x00, 0x00 };
538 1.1 jmcneill uint8_t freq_buf[4];
539 1.1 jmcneill uint32_t div, offset = 0;
540 1.1 jmcneill int error;
541 1.1 jmcneill
542 1.1 jmcneill if (params->u.vsb.modulation == VSB_8) {
543 1.1 jmcneill offset = 1750000;
544 1.1 jmcneill } else {
545 1.1 jmcneill return EINVAL;
546 1.1 jmcneill }
547 1.1 jmcneill
548 1.1 jmcneill div = (params->frequency - offset + 15625 / 2) / 15625;
549 1.1 jmcneill
550 1.1 jmcneill error = xc3028_write_buffer(xc, freq_init, sizeof(freq_init));
551 1.1 jmcneill if (error)
552 1.1 jmcneill return error;
553 1.1 jmcneill delay(10000);
554 1.1 jmcneill
555 1.1 jmcneill freq_buf[0] = (div >> 24) & 0xff;
556 1.1 jmcneill freq_buf[1] = (div >> 16) & 0xff;
557 1.1 jmcneill freq_buf[2] = (div >> 8) & 0xff;
558 1.1 jmcneill freq_buf[3] = (div >> 0) & 0xff;
559 1.1 jmcneill error = xc3028_write_buffer(xc, freq_buf, sizeof(freq_buf));
560 1.1 jmcneill if (error)
561 1.1 jmcneill return error;
562 1.1 jmcneill delay(100000);
563 1.1 jmcneill
564 1.1 jmcneill return 0;
565 1.1 jmcneill }
566 1.1 jmcneill
567 1.5 jmcneill MODULE(MODULE_CLASS_DRIVER, xc3028, "iic");
568 1.1 jmcneill
569 1.1 jmcneill static int
570 1.1 jmcneill xc3028_modcmd(modcmd_t cmd, void *opaque)
571 1.1 jmcneill {
572 1.1 jmcneill switch (cmd) {
573 1.1 jmcneill case MODULE_CMD_INIT:
574 1.1 jmcneill mutex_init(&xc3028_firmware_lock, MUTEX_DEFAULT, IPL_NONE);
575 1.1 jmcneill return 0;
576 1.1 jmcneill case MODULE_CMD_FINI:
577 1.1 jmcneill mutex_destroy(&xc3028_firmware_lock);
578 1.1 jmcneill return 0;
579 1.1 jmcneill default:
580 1.1 jmcneill return ENOTTY;
581 1.1 jmcneill }
582 1.1 jmcneill }
583