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
27int fm_tx_set_stereo_mono(struct fmdev *fmdev, u16 mode)
28{
29	u16 payload;
30	int 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
49static int set_rds_text(struct fmdev *fmdev, u8 *rds_text)
50{
51	u16 payload;
52	int 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
69static int set_rds_data_mode(struct fmdev *fmdev, u8 mode)
70{
71	u16 payload;
72	int 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
92static int set_rds_len(struct fmdev *fmdev, u8 type, u16 len)
93{
94	u16 payload;
95	int 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
108int fm_tx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
109{
110	u16 payload;
111	int 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
151int fm_tx_set_radio_text(struct fmdev *fmdev, u8 *rds_text, u8 rds_type)
152{
153	u16 payload;
154	int 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
179int fm_tx_set_af(struct fmdev *fmdev, u32 af)
180{
181	u16 payload;
182	int 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
199int fm_tx_set_region(struct fmdev *fmdev, u8 region)
200{
201	u16 payload;
202	int 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
219int fm_tx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
220{
221	u16 payload;
222	int 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 */
236static int set_audio_io(struct fmdev *fmdev)
237{
238	struct fmtx_data *tx = &fmdev->tx_data;
239	u16 payload;
240	int 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 */
254static int 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	int 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 */
292int 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	int 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 */
331int fm_tx_set_preemph_filter(struct fmdev *fmdev, u32 preemphasis)
332{
333	struct fmtx_data *tx = &fmdev->tx_data;
334	u16 payload;
335	int 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.*/
363int fm_tx_get_tune_cap_val(struct fmdev *fmdev)
364{
365	u16 curr_val;
366	u32 resp_len;
367	int ret;
368
369	if (fmdev->curr_fmmode != FM_MODE_TX)
370		return -EPERM;
371
372	ret = fmc_send_cmd(fmdev, READ_FMANT_TUNE_VALUE, REG_RD,
373			NULL, sizeof(curr_val), &curr_val, &resp_len);
374	if (ret < 0)
375		return ret;
376
377	curr_val = be16_to_cpu((__force __be16)curr_val);
378
379	return curr_val;
380}
381
382/* Set TX Frequency */
383int fm_tx_set_freq(struct fmdev *fmdev, u32 freq_to_set)
384{
385	struct fmtx_data *tx = &fmdev->tx_data;
386	u16 payload, chanl_index;
387	int ret;
388
389	if (test_bit(FM_CORE_TX_XMITING, &fmdev->flag)) {
390		enable_xmit(fmdev, 0);
391		clear_bit(FM_CORE_TX_XMITING, &fmdev->flag);
392	}
393
394	/* Enable FR, BL interrupts */
395	payload = (FM_FR_EVENT | FM_BL_EVENT);
396	ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
397			sizeof(payload), NULL, NULL);
398	if (ret < 0)
399		return ret;
400
401	tx->tx_frq = (unsigned long)freq_to_set;
402	fmdbg("tx: freq_to_set %ld\n", (long int)tx->tx_frq);
403
404	chanl_index = freq_to_set / 10;
405
406	/* Set current tuner channel */
407	payload = chanl_index;
408	ret = fmc_send_cmd(fmdev, CHANL_SET, REG_WR, &payload,
409			sizeof(payload), NULL, NULL);
410	if (ret < 0)
411		return ret;
412
413	fm_tx_set_pwr_lvl(fmdev, tx->pwr_lvl);
414	fm_tx_set_preemph_filter(fmdev, tx->preemph);
415
416	tx->audio_io = 0x01;	/* I2S */
417	set_audio_io(fmdev);
418
419	enable_xmit(fmdev, 0x01);	/* Enable transmission */
420
421	tx->aud_mode = FM_STEREO_MODE;
422	tx->rds.flag = FM_RDS_DISABLE;
423
424	return 0;
425}
426
427