1 | /* $NetBSD: mt2131.c,v 1.5 2015/03/07 14:16:51 jmcneill Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2008, 2011 Jonathan A. Kollasch |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
20 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
21 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
25 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
26 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: mt2131.c,v 1.5 2015/03/07 14:16:51 jmcneill Exp $" ); |
31 | |
32 | #include <sys/param.h> |
33 | #include <sys/systm.h> |
34 | #include <sys/device.h> |
35 | #include <sys/kmem.h> |
36 | #include <sys/syslog.h> |
37 | #include <sys/proc.h> |
38 | #include <sys/module.h> |
39 | |
40 | #include <dev/i2c/mt2131var.h> |
41 | |
42 | #define PWR 0x07 |
43 | #define UPC_1 0x0b |
44 | #define AGC_RL 0x10 |
45 | #define MISC_2 0x15 |
46 | |
47 | #define IF1 1220 |
48 | #define IF2 44000 |
49 | #define REF 16000 |
50 | |
51 | static const uint8_t mt2131_initstring[] = { |
52 | 0x01, |
53 | 0x50, 0x00, 0x50, 0x80, 0x00, 0x49, |
54 | 0xfa, 0x88, 0x08, 0x77, 0x41, 0x04, 0x00, 0x00, 0x00, 0x32, |
55 | 0x7f, 0xda, 0x4c, 0x00, 0x10, 0xaa, 0x78, 0x80, 0xff, 0x68, |
56 | 0xa0, 0xff, 0xdd, 0x00, 0x00 |
57 | }; |
58 | |
59 | static const uint8_t mt2131_agcinitstring[] = { |
60 | AGC_RL, |
61 | 0x7f, 0xc8, 0x0a, 0x5f, 0x00, 0x04 |
62 | }; |
63 | |
64 | |
65 | struct mt2131_softc { |
66 | device_t parent; |
67 | i2c_tag_t tag; |
68 | i2c_addr_t addr; |
69 | uint32_t frequency; |
70 | uint32_t bandwidth; |
71 | }; |
72 | |
73 | static int mt2131_init(struct mt2131_softc *); |
74 | |
75 | static int mt2131_read(struct mt2131_softc *, uint8_t, uint8_t *); |
76 | static int mt2131_write(struct mt2131_softc *, uint8_t, uint8_t); |
77 | |
78 | struct mt2131_softc * |
79 | mt2131_open(device_t parent, i2c_tag_t t, i2c_addr_t a) |
80 | { |
81 | struct mt2131_softc *sc; |
82 | int ret; |
83 | uint8_t cmd, reg; |
84 | |
85 | cmd = reg = 0; |
86 | |
87 | /* get id reg */ |
88 | iic_acquire_bus(t, I2C_F_POLL); |
89 | ret = iic_exec(t, I2C_OP_READ_WITH_STOP, a, &cmd, 1, ®, 1, I2C_F_POLL); |
90 | iic_release_bus(t, I2C_F_POLL); |
91 | |
92 | if (ret) { |
93 | device_printf(parent, "%s(): read fail\n" , __func__); |
94 | return NULL; |
95 | } |
96 | |
97 | if ((reg & 0xfe) != 0x3e) { |
98 | device_printf(parent, "%s(): chip id %02x unknown\n" , |
99 | __func__, reg); |
100 | return NULL; |
101 | } |
102 | |
103 | sc = kmem_alloc(sizeof(*sc), KM_SLEEP); |
104 | if (sc == NULL) |
105 | return NULL; |
106 | |
107 | sc->parent = parent; |
108 | sc->tag = t; |
109 | sc->addr = a; |
110 | |
111 | mt2131_init(sc); |
112 | |
113 | return sc; |
114 | } |
115 | |
116 | void |
117 | mt2131_close(struct mt2131_softc *sc) |
118 | { |
119 | kmem_free(sc, sizeof(*sc)); |
120 | } |
121 | |
122 | int |
123 | mt2131_tune_dtv(struct mt2131_softc *sc, const struct dvb_frontend_parameters *p) |
124 | { |
125 | int rv, i; |
126 | uint64_t o1, o2; |
127 | uint64_t d1, d2; |
128 | uint32_t r1, r2; |
129 | uint32_t fr; |
130 | uint8_t b[7]; |
131 | uint8_t regval; |
132 | |
133 | mt2131_init(sc); |
134 | |
135 | b[0] = 0x01; |
136 | |
137 | if(p->frequency != 0 && |
138 | (p->frequency < 50000000 || p->frequency > 1000000000)) |
139 | return EINVAL; |
140 | |
141 | fr = p->frequency / 1000; |
142 | |
143 | o1 = fr + IF1 * 1000; |
144 | o2 = o1 - fr - IF2; |
145 | |
146 | d1 = (o1 * 8192)/REF; |
147 | d2 = (o2 * 8192)/REF; |
148 | |
149 | r1 = d1/8192; |
150 | r2 = d2/8192; |
151 | |
152 | b[1] = (d1 & 0x1fe0) >> 5; |
153 | b[2] = (d1 & 0x001f); |
154 | b[3] = r1; |
155 | b[4] = (d2 & 0x1fe0) >> 5; |
156 | b[5] = (d2 & 0x001f); |
157 | b[6] = r2; |
158 | |
159 | iic_acquire_bus(sc->tag, I2C_F_POLL); |
160 | rv = iic_exec(sc->tag, I2C_OP_WRITE_WITH_STOP, sc->addr, b, 7, NULL, 0, I2C_F_POLL); |
161 | iic_release_bus(sc->tag, I2C_F_POLL); |
162 | |
163 | regval = (fr - 27501) / 55000; |
164 | |
165 | if(regval > 0x13) |
166 | regval = 0x13; |
167 | |
168 | rv = mt2131_write(sc, UPC_1, regval); |
169 | |
170 | if (rv != 0) |
171 | device_printf(sc->parent, "%s write failed\n" , __func__); |
172 | |
173 | sc->frequency = (o1 - o2 - IF2) * 1000; |
174 | |
175 | for (i = 0; i < 100; i++) { |
176 | kpause("mt2131" , true, 1, NULL); |
177 | |
178 | rv = mt2131_read(sc, 0x08, ®val); |
179 | if (rv != 0) |
180 | device_printf(sc->parent, "%s read failed\n" , __func__); |
181 | |
182 | if (( regval & 0x88 ) == 0x88 ) { |
183 | return 0; |
184 | } |
185 | } |
186 | |
187 | device_printf(sc->parent, "mt2131 not locked, %02x\n" , b[1]); |
188 | |
189 | return rv; |
190 | } |
191 | |
192 | static int |
193 | mt2131_init(struct mt2131_softc *sc) |
194 | { |
195 | int ret; |
196 | |
197 | ret = iic_acquire_bus(sc->tag, I2C_F_POLL); |
198 | if (ret) |
199 | return -1; |
200 | ret = iic_exec(sc->tag, I2C_OP_WRITE_WITH_STOP, sc->addr, |
201 | mt2131_initstring, sizeof(mt2131_initstring), NULL, 0, I2C_F_POLL); |
202 | if (ret) |
203 | return -1; |
204 | iic_release_bus(sc->tag, I2C_F_POLL); |
205 | |
206 | ret = mt2131_write(sc, UPC_1, 0x09); |
207 | ret = mt2131_write(sc, MISC_2, 0x47); |
208 | ret = mt2131_write(sc, PWR, 0xf2); |
209 | ret = mt2131_write(sc, UPC_1, 0x01); |
210 | |
211 | ret = iic_acquire_bus(sc->tag, I2C_F_POLL); |
212 | if (ret) |
213 | return -1; |
214 | ret = iic_exec(sc->tag, I2C_OP_WRITE_WITH_STOP, sc->addr, |
215 | mt2131_agcinitstring, sizeof(mt2131_agcinitstring), |
216 | NULL, 0, I2C_F_POLL); |
217 | iic_release_bus(sc->tag, I2C_F_POLL); |
218 | if (ret) |
219 | return -1; |
220 | |
221 | return 0; |
222 | } |
223 | |
224 | static int |
225 | mt2131_read(struct mt2131_softc *sc, uint8_t r, uint8_t *v) |
226 | { |
227 | int ret; |
228 | |
229 | ret = iic_acquire_bus(sc->tag, I2C_F_POLL); |
230 | if (ret) |
231 | return ret; |
232 | ret = iic_exec(sc->tag, I2C_OP_READ_WITH_STOP, sc->addr, |
233 | &r, 1, v, 1, I2C_F_POLL); |
234 | |
235 | iic_release_bus(sc->tag, I2C_F_POLL); |
236 | |
237 | return ret; |
238 | } |
239 | |
240 | static int |
241 | mt2131_write(struct mt2131_softc *sc, uint8_t a, uint8_t v) |
242 | { |
243 | int ret; |
244 | uint8_t b[] = { a, v }; |
245 | |
246 | ret = iic_acquire_bus(sc->tag, I2C_F_POLL); |
247 | if (ret) |
248 | return ret; |
249 | |
250 | ret = iic_exec(sc->tag, I2C_OP_READ_WITH_STOP, sc->addr, |
251 | b, sizeof(b), NULL, 0, I2C_F_POLL); |
252 | |
253 | iic_release_bus(sc->tag, I2C_F_POLL); |
254 | |
255 | return ret; |
256 | } |
257 | |
258 | MODULE(MODULE_CLASS_DRIVER, mt2131, "i2cexec" ); |
259 | |
260 | static int |
261 | mt2131_modcmd(modcmd_t cmd, void *priv) |
262 | { |
263 | if (cmd == MODULE_CMD_INIT || cmd == MODULE_CMD_FINI) |
264 | return 0; |
265 | return ENOTTY; |
266 | } |
267 | |