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