diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_tx.c | 425 | ||||
-rw-r--r-- | drivers/media/radio/wl128x/fmdrv_tx.h | 37 |
2 files changed, 462 insertions, 0 deletions
diff --git a/drivers/media/radio/wl128x/fmdrv_tx.c b/drivers/media/radio/wl128x/fmdrv_tx.c new file mode 100644 index 000000000000..be54068b56a8 --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_tx.c | |||
@@ -0,0 +1,425 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * This sub-module of FM driver implements FM TX functionality. | ||
4 | * | ||
5 | * Copyright (C) 2011 Texas Instruments | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/delay.h> | ||
23 | #include "fmdrv.h" | ||
24 | #include "fmdrv_common.h" | ||
25 | #include "fmdrv_tx.h" | ||
26 | |||
27 | u32 fm_tx_set_stereo_mono(struct fmdev *fmdev, u16 mode) | ||
28 | { | ||
29 | u16 payload; | ||
30 | u32 ret; | ||
31 | |||
32 | if (fmdev->tx_data.aud_mode == mode) | ||
33 | return 0; | ||
34 | |||
35 | fmdbg("stereo mode: %d\n", mode); | ||
36 | |||
37 | /* Set Stereo/Mono mode */ | ||
38 | payload = (1 - mode); | ||
39 | ret = fmc_send_cmd(fmdev, MONO_SET, REG_WR, &payload, | ||
40 | sizeof(payload), NULL, NULL); | ||
41 | if (ret < 0) | ||
42 | return ret; | ||
43 | |||
44 | fmdev->tx_data.aud_mode = mode; | ||
45 | |||
46 | return ret; | ||
47 | } | ||
48 | |||
49 | static u32 set_rds_text(struct fmdev *fmdev, u8 *rds_text) | ||
50 | { | ||
51 | u16 payload; | ||
52 | u32 ret; | ||
53 | |||
54 | ret = fmc_send_cmd(fmdev, RDS_DATA_SET, REG_WR, rds_text, | ||
55 | strlen(rds_text), NULL, NULL); | ||
56 | if (ret < 0) | ||
57 | return ret; | ||
58 | |||
59 | /* Scroll mode */ | ||
60 | payload = (u16)0x1; | ||
61 | ret = fmc_send_cmd(fmdev, DISPLAY_MODE, REG_WR, &payload, | ||
62 | sizeof(payload), NULL, NULL); | ||
63 | if (ret < 0) | ||
64 | return ret; | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static u32 set_rds_data_mode(struct fmdev *fmdev, u8 mode) | ||
70 | { | ||
71 | u16 payload; | ||
72 | u32 ret; | ||
73 | |||
74 | /* Setting unique PI TODO: how unique? */ | ||
75 | payload = (u16)0xcafe; | ||
76 | ret = fmc_send_cmd(fmdev, PI_SET, REG_WR, &payload, | ||
77 | sizeof(payload), NULL, NULL); | ||
78 | if (ret < 0) | ||
79 | return ret; | ||
80 | |||
81 | /* Set decoder id */ | ||
82 | payload = (u16)0xa; | ||
83 | ret = fmc_send_cmd(fmdev, DI_SET, REG_WR, &payload, | ||
84 | sizeof(payload), NULL, NULL); | ||
85 | if (ret < 0) | ||
86 | return ret; | ||
87 | |||
88 | /* TODO: RDS_MODE_GET? */ | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static u32 set_rds_len(struct fmdev *fmdev, u8 type, u16 len) | ||
93 | { | ||
94 | u16 payload; | ||
95 | u32 ret; | ||
96 | |||
97 | len |= type << 8; | ||
98 | payload = len; | ||
99 | ret = fmc_send_cmd(fmdev, RDS_CONFIG_DATA_SET, REG_WR, &payload, | ||
100 | sizeof(payload), NULL, NULL); | ||
101 | if (ret < 0) | ||
102 | return ret; | ||
103 | |||
104 | /* TODO: LENGTH_GET? */ | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | u32 fm_tx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis) | ||
109 | { | ||
110 | u16 payload; | ||
111 | u32 ret; | ||
112 | u8 rds_text[] = "Zoom2\n"; | ||
113 | |||
114 | fmdbg("rds_en_dis:%d(E:%d, D:%d)\n", rds_en_dis, | ||
115 | FM_RDS_ENABLE, FM_RDS_DISABLE); | ||
116 | |||
117 | if (rds_en_dis == FM_RDS_ENABLE) { | ||
118 | /* Set RDS length */ | ||
119 | set_rds_len(fmdev, 0, strlen(rds_text)); | ||
120 | |||
121 | /* Set RDS text */ | ||
122 | set_rds_text(fmdev, rds_text); | ||
123 | |||
124 | /* Set RDS mode */ | ||
125 | set_rds_data_mode(fmdev, 0x0); | ||
126 | } | ||
127 | |||
128 | /* Send command to enable RDS */ | ||
129 | if (rds_en_dis == FM_RDS_ENABLE) | ||
130 | payload = 0x01; | ||
131 | else | ||
132 | payload = 0x00; | ||
133 | |||
134 | ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload, | ||
135 | sizeof(payload), NULL, NULL); | ||
136 | if (ret < 0) | ||
137 | return ret; | ||
138 | |||
139 | if (rds_en_dis == FM_RDS_ENABLE) { | ||
140 | /* Set RDS length */ | ||
141 | set_rds_len(fmdev, 0, strlen(rds_text)); | ||
142 | |||
143 | /* Set RDS text */ | ||
144 | set_rds_text(fmdev, rds_text); | ||
145 | } | ||
146 | fmdev->tx_data.rds.flag = rds_en_dis; | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | u32 fm_tx_set_radio_text(struct fmdev *fmdev, u8 *rds_text, u8 rds_type) | ||
152 | { | ||
153 | u16 payload; | ||
154 | u32 ret; | ||
155 | |||
156 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
157 | return -EPERM; | ||
158 | |||
159 | fm_tx_set_rds_mode(fmdev, 0); | ||
160 | |||
161 | /* Set RDS length */ | ||
162 | set_rds_len(fmdev, rds_type, strlen(rds_text)); | ||
163 | |||
164 | /* Set RDS text */ | ||
165 | set_rds_text(fmdev, rds_text); | ||
166 | |||
167 | /* Set RDS mode */ | ||
168 | set_rds_data_mode(fmdev, 0x0); | ||
169 | |||
170 | payload = 1; | ||
171 | ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload, | ||
172 | sizeof(payload), NULL, NULL); | ||
173 | if (ret < 0) | ||
174 | return ret; | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | u32 fm_tx_set_af(struct fmdev *fmdev, u32 af) | ||
180 | { | ||
181 | u16 payload; | ||
182 | u32 ret; | ||
183 | |||
184 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
185 | return -EPERM; | ||
186 | |||
187 | fmdbg("AF: %d\n", af); | ||
188 | |||
189 | af = (af - 87500) / 100; | ||
190 | payload = (u16)af; | ||
191 | ret = fmc_send_cmd(fmdev, TA_SET, REG_WR, &payload, | ||
192 | sizeof(payload), NULL, NULL); | ||
193 | if (ret < 0) | ||
194 | return ret; | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | u32 fm_tx_set_region(struct fmdev *fmdev, u8 region) | ||
200 | { | ||
201 | u16 payload; | ||
202 | u32 ret; | ||
203 | |||
204 | if (region != FM_BAND_EUROPE_US && region != FM_BAND_JAPAN) { | ||
205 | fmerr("Invalid band\n"); | ||
206 | return -EINVAL; | ||
207 | } | ||
208 | |||
209 | /* Send command to set the band */ | ||
210 | payload = (u16)region; | ||
211 | ret = fmc_send_cmd(fmdev, TX_BAND_SET, REG_WR, &payload, | ||
212 | sizeof(payload), NULL, NULL); | ||
213 | if (ret < 0) | ||
214 | return ret; | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | u32 fm_tx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset) | ||
220 | { | ||
221 | u16 payload; | ||
222 | u32 ret; | ||
223 | |||
224 | fmdbg("tx: mute mode %d\n", mute_mode_toset); | ||
225 | |||
226 | payload = mute_mode_toset; | ||
227 | ret = fmc_send_cmd(fmdev, MUTE, REG_WR, &payload, | ||
228 | sizeof(payload), NULL, NULL); | ||
229 | if (ret < 0) | ||
230 | return ret; | ||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | /* Set TX Audio I/O */ | ||
236 | static u32 set_audio_io(struct fmdev *fmdev) | ||
237 | { | ||
238 | struct fmtx_data *tx = &fmdev->tx_data; | ||
239 | u16 payload; | ||
240 | u32 ret; | ||
241 | |||
242 | /* Set Audio I/O Enable */ | ||
243 | payload = tx->audio_io; | ||
244 | ret = fmc_send_cmd(fmdev, AUDIO_IO_SET, REG_WR, &payload, | ||
245 | sizeof(payload), NULL, NULL); | ||
246 | if (ret < 0) | ||
247 | return ret; | ||
248 | |||
249 | /* TODO: is audio set? */ | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | /* Start TX Transmission */ | ||
254 | static u32 enable_xmit(struct fmdev *fmdev, u8 new_xmit_state) | ||
255 | { | ||
256 | struct fmtx_data *tx = &fmdev->tx_data; | ||
257 | unsigned long timeleft; | ||
258 | u16 payload; | ||
259 | u32 ret; | ||
260 | |||
261 | /* Enable POWER_ENB interrupts */ | ||
262 | payload = FM_POW_ENB_EVENT; | ||
263 | ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, | ||
264 | sizeof(payload), NULL, NULL); | ||
265 | if (ret < 0) | ||
266 | return ret; | ||
267 | |||
268 | /* Set Power Enable */ | ||
269 | payload = new_xmit_state; | ||
270 | ret = fmc_send_cmd(fmdev, POWER_ENB_SET, REG_WR, &payload, | ||
271 | sizeof(payload), NULL, NULL); | ||
272 | if (ret < 0) | ||
273 | return ret; | ||
274 | |||
275 | /* Wait for Power Enabled */ | ||
276 | init_completion(&fmdev->maintask_comp); | ||
277 | timeleft = wait_for_completion_timeout(&fmdev->maintask_comp, | ||
278 | FM_DRV_TX_TIMEOUT); | ||
279 | if (!timeleft) { | ||
280 | fmerr("Timeout(%d sec),didn't get tune ended interrupt\n", | ||
281 | jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); | ||
282 | return -ETIMEDOUT; | ||
283 | } | ||
284 | |||
285 | set_bit(FM_CORE_TX_XMITING, &fmdev->flag); | ||
286 | tx->xmit_state = new_xmit_state; | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | /* Set TX power level */ | ||
292 | u32 fm_tx_set_pwr_lvl(struct fmdev *fmdev, u8 new_pwr_lvl) | ||
293 | { | ||
294 | u16 payload; | ||
295 | struct fmtx_data *tx = &fmdev->tx_data; | ||
296 | u32 ret; | ||
297 | |||
298 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
299 | return -EPERM; | ||
300 | fmdbg("tx: pwr_level_to_set %ld\n", (long int)new_pwr_lvl); | ||
301 | |||
302 | /* If the core isn't ready update global variable */ | ||
303 | if (!test_bit(FM_CORE_READY, &fmdev->flag)) { | ||
304 | tx->pwr_lvl = new_pwr_lvl; | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | /* Set power level: Application will specify power level value in | ||
309 | * units of dB/uV, whereas range and step are specific to FM chip. | ||
310 | * For TI's WL chips, convert application specified power level value | ||
311 | * to chip specific value by subtracting 122 from it. Refer to TI FM | ||
312 | * data sheet for details. | ||
313 | * */ | ||
314 | |||
315 | payload = (FM_PWR_LVL_HIGH - new_pwr_lvl); | ||
316 | ret = fmc_send_cmd(fmdev, POWER_LEV_SET, REG_WR, &payload, | ||
317 | sizeof(payload), NULL, NULL); | ||
318 | if (ret < 0) | ||
319 | return ret; | ||
320 | |||
321 | /* TODO: is the power level set? */ | ||
322 | tx->pwr_lvl = new_pwr_lvl; | ||
323 | |||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | /* | ||
328 | * Sets FM TX pre-emphasis filter value (OFF, 50us, or 75us) | ||
329 | * Convert V4L2 specified filter values to chip specific filter values. | ||
330 | */ | ||
331 | u32 fm_tx_set_preemph_filter(struct fmdev *fmdev, u32 preemphasis) | ||
332 | { | ||
333 | struct fmtx_data *tx = &fmdev->tx_data; | ||
334 | u16 payload; | ||
335 | u32 ret; | ||
336 | |||
337 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
338 | return -EPERM; | ||
339 | |||
340 | switch (preemphasis) { | ||
341 | case V4L2_PREEMPHASIS_DISABLED: | ||
342 | payload = FM_TX_PREEMPH_OFF; | ||
343 | break; | ||
344 | case V4L2_PREEMPHASIS_50_uS: | ||
345 | payload = FM_TX_PREEMPH_50US; | ||
346 | break; | ||
347 | case V4L2_PREEMPHASIS_75_uS: | ||
348 | payload = FM_TX_PREEMPH_75US; | ||
349 | break; | ||
350 | } | ||
351 | |||
352 | ret = fmc_send_cmd(fmdev, PREMPH_SET, REG_WR, &payload, | ||
353 | sizeof(payload), NULL, NULL); | ||
354 | if (ret < 0) | ||
355 | return ret; | ||
356 | |||
357 | tx->preemph = payload; | ||
358 | |||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | /* Get the TX tuning capacitor value.*/ | ||
363 | u32 fm_tx_get_tune_cap_val(struct fmdev *fmdev) | ||
364 | { | ||
365 | u16 curr_val; | ||
366 | u32 ret, resp_len; | ||
367 | |||
368 | if (fmdev->curr_fmmode != FM_MODE_TX) | ||
369 | return -EPERM; | ||
370 | |||
371 | ret = fmc_send_cmd(fmdev, READ_FMANT_TUNE_VALUE, REG_RD, | ||
372 | NULL, sizeof(curr_val), &curr_val, &resp_len); | ||
373 | if (ret < 0) | ||
374 | return ret; | ||
375 | |||
376 | curr_val = be16_to_cpu(curr_val); | ||
377 | |||
378 | return curr_val; | ||
379 | } | ||
380 | |||
381 | /* Set TX Frequency */ | ||
382 | u32 fm_tx_set_freq(struct fmdev *fmdev, u32 freq_to_set) | ||
383 | { | ||
384 | struct fmtx_data *tx = &fmdev->tx_data; | ||
385 | u16 payload, chanl_index; | ||
386 | u32 ret; | ||
387 | |||
388 | if (test_bit(FM_CORE_TX_XMITING, &fmdev->flag)) { | ||
389 | enable_xmit(fmdev, 0); | ||
390 | clear_bit(FM_CORE_TX_XMITING, &fmdev->flag); | ||
391 | } | ||
392 | |||
393 | /* Enable FR, BL interrupts */ | ||
394 | payload = (FM_FR_EVENT | FM_BL_EVENT); | ||
395 | ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, | ||
396 | sizeof(payload), NULL, NULL); | ||
397 | if (ret < 0) | ||
398 | return ret; | ||
399 | |||
400 | tx->tx_frq = (unsigned long)freq_to_set; | ||
401 | fmdbg("tx: freq_to_set %ld\n", (long int)tx->tx_frq); | ||
402 | |||
403 | chanl_index = freq_to_set / 10; | ||
404 | |||
405 | /* Set current tuner channel */ | ||
406 | payload = chanl_index; | ||
407 | ret = fmc_send_cmd(fmdev, CHANL_SET, REG_WR, &payload, | ||
408 | sizeof(payload), NULL, NULL); | ||
409 | if (ret < 0) | ||
410 | return ret; | ||
411 | |||
412 | fm_tx_set_pwr_lvl(fmdev, tx->pwr_lvl); | ||
413 | fm_tx_set_preemph_filter(fmdev, tx->preemph); | ||
414 | |||
415 | tx->audio_io = 0x01; /* I2S */ | ||
416 | set_audio_io(fmdev); | ||
417 | |||
418 | enable_xmit(fmdev, 0x01); /* Enable transmission */ | ||
419 | |||
420 | tx->aud_mode = FM_STEREO_MODE; | ||
421 | tx->rds.flag = FM_RDS_DISABLE; | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
diff --git a/drivers/media/radio/wl128x/fmdrv_tx.h b/drivers/media/radio/wl128x/fmdrv_tx.h new file mode 100644 index 000000000000..e393a2bdd49e --- /dev/null +++ b/drivers/media/radio/wl128x/fmdrv_tx.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * FM Driver for Connectivity chip of Texas Instruments. | ||
3 | * FM TX module header. | ||
4 | * | ||
5 | * Copyright (C) 2011 Texas Instruments | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifndef _FMDRV_TX_H | ||
23 | #define _FMDRV_TX_H | ||
24 | |||
25 | u32 fm_tx_set_freq(struct fmdev *, u32); | ||
26 | u32 fm_tx_set_pwr_lvl(struct fmdev *, u8); | ||
27 | u32 fm_tx_set_region(struct fmdev *, u8); | ||
28 | u32 fm_tx_set_mute_mode(struct fmdev *, u8); | ||
29 | u32 fm_tx_set_stereo_mono(struct fmdev *, u16); | ||
30 | u32 fm_tx_set_rds_mode(struct fmdev *, u8); | ||
31 | u32 fm_tx_set_radio_text(struct fmdev *, u8 *, u8); | ||
32 | u32 fm_tx_set_af(struct fmdev *, u32); | ||
33 | u32 fm_tx_set_preemph_filter(struct fmdev *, u32); | ||
34 | u32 fm_tx_get_tune_cap_val(struct fmdev *); | ||
35 | |||
36 | #endif | ||
37 | |||