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