18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    Driver for SAA6588 RDS decoder
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci    (c) 2005 Hans J. Koch
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci*/
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/i2c.h>
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/poll.h>
198c2ecf20Sopenharmony_ci#include <linux/wait.h>
208c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <media/i2c/saa6588.h>
238c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* insmod options */
278c2ecf20Sopenharmony_cistatic unsigned int debug;
288c2ecf20Sopenharmony_cistatic unsigned int xtal;
298c2ecf20Sopenharmony_cistatic unsigned int mmbs;
308c2ecf20Sopenharmony_cistatic unsigned int plvl;
318c2ecf20Sopenharmony_cistatic unsigned int bufblocks = 100;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "enable debug messages");
358c2ecf20Sopenharmony_cimodule_param(xtal, int, 0);
368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(xtal, "select oscillator frequency (0..3), default 0");
378c2ecf20Sopenharmony_cimodule_param(mmbs, int, 0);
388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mmbs, "enable MMBS mode: 0=off (default), 1=on");
398c2ecf20Sopenharmony_cimodule_param(plvl, int, 0);
408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(plvl, "select pause level (0..3), default 0");
418c2ecf20Sopenharmony_cimodule_param(bufblocks, int, 0);
428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bufblocks, "number of buffered blocks, default 100");
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("v4l2 driver module for SAA6588 RDS decoder");
458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans J. Koch <koch@hjk-az.de>");
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define UNSET       (-1U)
528c2ecf20Sopenharmony_ci#define PREFIX      "saa6588: "
538c2ecf20Sopenharmony_ci#define dprintk     if (debug) printk
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct saa6588 {
568c2ecf20Sopenharmony_ci	struct v4l2_subdev sd;
578c2ecf20Sopenharmony_ci	struct delayed_work work;
588c2ecf20Sopenharmony_ci	spinlock_t lock;
598c2ecf20Sopenharmony_ci	unsigned char *buffer;
608c2ecf20Sopenharmony_ci	unsigned int buf_size;
618c2ecf20Sopenharmony_ci	unsigned int rd_index;
628c2ecf20Sopenharmony_ci	unsigned int wr_index;
638c2ecf20Sopenharmony_ci	unsigned int block_count;
648c2ecf20Sopenharmony_ci	unsigned char last_blocknum;
658c2ecf20Sopenharmony_ci	wait_queue_head_t read_queue;
668c2ecf20Sopenharmony_ci	int data_available_for_read;
678c2ecf20Sopenharmony_ci	u8 sync;
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	return container_of(sd, struct saa6588, sd);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/*
788c2ecf20Sopenharmony_ci * SAA6588 defines
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* Initialization and mode control byte (0w) */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* bit 0+1 (DAC0/DAC1) */
848c2ecf20Sopenharmony_ci#define cModeStandard           0x00
858c2ecf20Sopenharmony_ci#define cModeFastPI             0x01
868c2ecf20Sopenharmony_ci#define cModeReducedRequest     0x02
878c2ecf20Sopenharmony_ci#define cModeInvalid            0x03
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* bit 2 (RBDS) */
908c2ecf20Sopenharmony_ci#define cProcessingModeRDS      0x00
918c2ecf20Sopenharmony_ci#define cProcessingModeRBDS     0x04
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* bit 3+4 (SYM0/SYM1) */
948c2ecf20Sopenharmony_ci#define cErrCorrectionNone      0x00
958c2ecf20Sopenharmony_ci#define cErrCorrection2Bits     0x08
968c2ecf20Sopenharmony_ci#define cErrCorrection5Bits     0x10
978c2ecf20Sopenharmony_ci#define cErrCorrectionNoneRBDS  0x18
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/* bit 5 (NWSY) */
1008c2ecf20Sopenharmony_ci#define cSyncNormal             0x00
1018c2ecf20Sopenharmony_ci#define cSyncRestart            0x20
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/* bit 6 (TSQD) */
1048c2ecf20Sopenharmony_ci#define cSigQualityDetectOFF    0x00
1058c2ecf20Sopenharmony_ci#define cSigQualityDetectON     0x40
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* bit 7 (SQCM) */
1088c2ecf20Sopenharmony_ci#define cSigQualityTriggered    0x00
1098c2ecf20Sopenharmony_ci#define cSigQualityContinous    0x80
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* Pause level and flywheel control byte (1w) */
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* bits 0..5 (FEB0..FEB5) */
1148c2ecf20Sopenharmony_ci#define cFlywheelMaxBlocksMask  0x3F
1158c2ecf20Sopenharmony_ci#define cFlywheelDefault        0x20
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/* bits 6+7 (PL0/PL1) */
1188c2ecf20Sopenharmony_ci#define cPauseLevel_11mV	0x00
1198c2ecf20Sopenharmony_ci#define cPauseLevel_17mV        0x40
1208c2ecf20Sopenharmony_ci#define cPauseLevel_27mV        0x80
1218c2ecf20Sopenharmony_ci#define cPauseLevel_43mV        0xC0
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/* Pause time/oscillator frequency/quality detector control byte (1w) */
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/* bits 0..4 (SQS0..SQS4) */
1268c2ecf20Sopenharmony_ci#define cQualityDetectSensMask  0x1F
1278c2ecf20Sopenharmony_ci#define cQualityDetectDefault   0x0F
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* bit 5 (SOSC) */
1308c2ecf20Sopenharmony_ci#define cSelectOscFreqOFF	0x00
1318c2ecf20Sopenharmony_ci#define cSelectOscFreqON	0x20
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/* bit 6+7 (PTF0/PTF1) */
1348c2ecf20Sopenharmony_ci#define cOscFreq_4332kHz	0x00
1358c2ecf20Sopenharmony_ci#define cOscFreq_8664kHz	0x40
1368c2ecf20Sopenharmony_ci#define cOscFreq_12996kHz	0x80
1378c2ecf20Sopenharmony_ci#define cOscFreq_17328kHz	0xC0
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic bool block_from_buf(struct saa6588 *s, unsigned char *buf)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	int i;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (s->rd_index == s->wr_index) {
1468c2ecf20Sopenharmony_ci		if (debug > 2)
1478c2ecf20Sopenharmony_ci			dprintk(PREFIX "Read: buffer empty.\n");
1488c2ecf20Sopenharmony_ci		return false;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (debug > 2) {
1528c2ecf20Sopenharmony_ci		dprintk(PREFIX "Read: ");
1538c2ecf20Sopenharmony_ci		for (i = s->rd_index; i < s->rd_index + 3; i++)
1548c2ecf20Sopenharmony_ci			dprintk("0x%02x ", s->buffer[i]);
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	memcpy(buf, &s->buffer[s->rd_index], 3);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	s->rd_index += 3;
1608c2ecf20Sopenharmony_ci	if (s->rd_index >= s->buf_size)
1618c2ecf20Sopenharmony_ci		s->rd_index = 0;
1628c2ecf20Sopenharmony_ci	s->block_count--;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (debug > 2)
1658c2ecf20Sopenharmony_ci		dprintk("%d blocks total.\n", s->block_count);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return true;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic void read_from_buf(struct saa6588 *s, struct saa6588_command *a)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	unsigned char __user *buf_ptr = a->buffer;
1738c2ecf20Sopenharmony_ci	unsigned char buf[3];
1748c2ecf20Sopenharmony_ci	unsigned long flags;
1758c2ecf20Sopenharmony_ci	unsigned int rd_blocks;
1768c2ecf20Sopenharmony_ci	unsigned int i;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	a->result = 0;
1798c2ecf20Sopenharmony_ci	if (!a->buffer)
1808c2ecf20Sopenharmony_ci		return;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	while (!a->nonblocking && !s->data_available_for_read) {
1838c2ecf20Sopenharmony_ci		int ret = wait_event_interruptible(s->read_queue,
1848c2ecf20Sopenharmony_ci					     s->data_available_for_read);
1858c2ecf20Sopenharmony_ci		if (ret == -ERESTARTSYS) {
1868c2ecf20Sopenharmony_ci			a->result = -EINTR;
1878c2ecf20Sopenharmony_ci			return;
1888c2ecf20Sopenharmony_ci		}
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	rd_blocks = a->block_count;
1928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
1938c2ecf20Sopenharmony_ci	if (rd_blocks > s->block_count)
1948c2ecf20Sopenharmony_ci		rd_blocks = s->block_count;
1958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (!rd_blocks)
1988c2ecf20Sopenharmony_ci		return;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	for (i = 0; i < rd_blocks; i++) {
2018c2ecf20Sopenharmony_ci		bool got_block;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		spin_lock_irqsave(&s->lock, flags);
2048c2ecf20Sopenharmony_ci		got_block = block_from_buf(s, buf);
2058c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&s->lock, flags);
2068c2ecf20Sopenharmony_ci		if (!got_block)
2078c2ecf20Sopenharmony_ci			break;
2088c2ecf20Sopenharmony_ci		if (copy_to_user(buf_ptr, buf, 3)) {
2098c2ecf20Sopenharmony_ci			a->result = -EFAULT;
2108c2ecf20Sopenharmony_ci			return;
2118c2ecf20Sopenharmony_ci		}
2128c2ecf20Sopenharmony_ci		buf_ptr += 3;
2138c2ecf20Sopenharmony_ci		a->result += 3;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
2168c2ecf20Sopenharmony_ci	s->data_available_for_read = (s->block_count > 0);
2178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic void block_to_buf(struct saa6588 *s, unsigned char *blockbuf)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	unsigned int i;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (debug > 3)
2258c2ecf20Sopenharmony_ci		dprintk(PREFIX "New block: ");
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	for (i = 0; i < 3; ++i) {
2288c2ecf20Sopenharmony_ci		if (debug > 3)
2298c2ecf20Sopenharmony_ci			dprintk("0x%02x ", blockbuf[i]);
2308c2ecf20Sopenharmony_ci		s->buffer[s->wr_index] = blockbuf[i];
2318c2ecf20Sopenharmony_ci		s->wr_index++;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (s->wr_index >= s->buf_size)
2358c2ecf20Sopenharmony_ci		s->wr_index = 0;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (s->wr_index == s->rd_index) {
2388c2ecf20Sopenharmony_ci		s->rd_index += 3;
2398c2ecf20Sopenharmony_ci		if (s->rd_index >= s->buf_size)
2408c2ecf20Sopenharmony_ci			s->rd_index = 0;
2418c2ecf20Sopenharmony_ci	} else
2428c2ecf20Sopenharmony_ci		s->block_count++;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (debug > 3)
2458c2ecf20Sopenharmony_ci		dprintk("%d blocks total.\n", s->block_count);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void saa6588_i2c_poll(struct saa6588 *s)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&s->sd);
2518c2ecf20Sopenharmony_ci	unsigned long flags;
2528c2ecf20Sopenharmony_ci	unsigned char tmpbuf[6];
2538c2ecf20Sopenharmony_ci	unsigned char blocknum;
2548c2ecf20Sopenharmony_ci	unsigned char tmp;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/* Although we only need 3 bytes, we have to read at least 6.
2578c2ecf20Sopenharmony_ci	   SAA6588 returns garbage otherwise. */
2588c2ecf20Sopenharmony_ci	if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) {
2598c2ecf20Sopenharmony_ci		if (debug > 1)
2608c2ecf20Sopenharmony_ci			dprintk(PREFIX "read error!\n");
2618c2ecf20Sopenharmony_ci		return;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	s->sync = tmpbuf[0] & 0x10;
2658c2ecf20Sopenharmony_ci	if (!s->sync)
2668c2ecf20Sopenharmony_ci		return;
2678c2ecf20Sopenharmony_ci	blocknum = tmpbuf[0] >> 5;
2688c2ecf20Sopenharmony_ci	if (blocknum == s->last_blocknum) {
2698c2ecf20Sopenharmony_ci		if (debug > 3)
2708c2ecf20Sopenharmony_ci			dprintk("Saw block %d again.\n", blocknum);
2718c2ecf20Sopenharmony_ci		return;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	s->last_blocknum = blocknum;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/*
2778c2ecf20Sopenharmony_ci	   Byte order according to v4l2 specification:
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	   Byte 0: Least Significant Byte of RDS Block
2808c2ecf20Sopenharmony_ci	   Byte 1: Most Significant Byte of RDS Block
2818c2ecf20Sopenharmony_ci	   Byte 2 Bit 7: Error bit. Indicates that an uncorrectable error
2828c2ecf20Sopenharmony_ci	   occurred during reception of this block.
2838c2ecf20Sopenharmony_ci	   Bit 6: Corrected bit. Indicates that an error was
2848c2ecf20Sopenharmony_ci	   corrected for this data block.
2858c2ecf20Sopenharmony_ci	   Bits 5-3: Same as bits 0-2.
2868c2ecf20Sopenharmony_ci	   Bits 2-0: Block number.
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	   SAA6588 byte order is Status-MSB-LSB, so we have to swap the
2898c2ecf20Sopenharmony_ci	   first and the last of the 3 bytes block.
2908c2ecf20Sopenharmony_ci	 */
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	swap(tmpbuf[2], tmpbuf[0]);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/* Map 'Invalid block E' to 'Invalid Block' */
2958c2ecf20Sopenharmony_ci	if (blocknum == 6)
2968c2ecf20Sopenharmony_ci		blocknum = V4L2_RDS_BLOCK_INVALID;
2978c2ecf20Sopenharmony_ci	/* And if are not in mmbs mode, then 'Block E' is also mapped
2988c2ecf20Sopenharmony_ci	   to 'Invalid Block'. As far as I can tell MMBS is discontinued,
2998c2ecf20Sopenharmony_ci	   and if there is ever a need to support E blocks, then please
3008c2ecf20Sopenharmony_ci	   contact the linux-media mailinglist. */
3018c2ecf20Sopenharmony_ci	else if (!mmbs && blocknum == 5)
3028c2ecf20Sopenharmony_ci		blocknum = V4L2_RDS_BLOCK_INVALID;
3038c2ecf20Sopenharmony_ci	tmp = blocknum;
3048c2ecf20Sopenharmony_ci	tmp |= blocknum << 3;	/* Received offset == Offset Name (OK ?) */
3058c2ecf20Sopenharmony_ci	if ((tmpbuf[2] & 0x03) == 0x03)
3068c2ecf20Sopenharmony_ci		tmp |= V4L2_RDS_BLOCK_ERROR;	 /* uncorrectable error */
3078c2ecf20Sopenharmony_ci	else if ((tmpbuf[2] & 0x03) != 0x00)
3088c2ecf20Sopenharmony_ci		tmp |= V4L2_RDS_BLOCK_CORRECTED; /* corrected error */
3098c2ecf20Sopenharmony_ci	tmpbuf[2] = tmp;	/* Is this enough ? Should we also check other bits ? */
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
3128c2ecf20Sopenharmony_ci	block_to_buf(s, tmpbuf);
3138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
3148c2ecf20Sopenharmony_ci	s->data_available_for_read = 1;
3158c2ecf20Sopenharmony_ci	wake_up_interruptible(&s->read_queue);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic void saa6588_work(struct work_struct *work)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct saa6588 *s = container_of(work, struct saa6588, work.work);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	saa6588_i2c_poll(s);
3238c2ecf20Sopenharmony_ci	schedule_delayed_work(&s->work, msecs_to_jiffies(20));
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic void saa6588_configure(struct saa6588 *s)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&s->sd);
3298c2ecf20Sopenharmony_ci	unsigned char buf[3];
3308c2ecf20Sopenharmony_ci	int rc;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	buf[0] = cSyncRestart;
3338c2ecf20Sopenharmony_ci	if (mmbs)
3348c2ecf20Sopenharmony_ci		buf[0] |= cProcessingModeRBDS;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	buf[1] = cFlywheelDefault;
3378c2ecf20Sopenharmony_ci	switch (plvl) {
3388c2ecf20Sopenharmony_ci	case 0:
3398c2ecf20Sopenharmony_ci		buf[1] |= cPauseLevel_11mV;
3408c2ecf20Sopenharmony_ci		break;
3418c2ecf20Sopenharmony_ci	case 1:
3428c2ecf20Sopenharmony_ci		buf[1] |= cPauseLevel_17mV;
3438c2ecf20Sopenharmony_ci		break;
3448c2ecf20Sopenharmony_ci	case 2:
3458c2ecf20Sopenharmony_ci		buf[1] |= cPauseLevel_27mV;
3468c2ecf20Sopenharmony_ci		break;
3478c2ecf20Sopenharmony_ci	case 3:
3488c2ecf20Sopenharmony_ci		buf[1] |= cPauseLevel_43mV;
3498c2ecf20Sopenharmony_ci		break;
3508c2ecf20Sopenharmony_ci	default:		/* nothing */
3518c2ecf20Sopenharmony_ci		break;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	buf[2] = cQualityDetectDefault | cSelectOscFreqON;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	switch (xtal) {
3578c2ecf20Sopenharmony_ci	case 0:
3588c2ecf20Sopenharmony_ci		buf[2] |= cOscFreq_4332kHz;
3598c2ecf20Sopenharmony_ci		break;
3608c2ecf20Sopenharmony_ci	case 1:
3618c2ecf20Sopenharmony_ci		buf[2] |= cOscFreq_8664kHz;
3628c2ecf20Sopenharmony_ci		break;
3638c2ecf20Sopenharmony_ci	case 2:
3648c2ecf20Sopenharmony_ci		buf[2] |= cOscFreq_12996kHz;
3658c2ecf20Sopenharmony_ci		break;
3668c2ecf20Sopenharmony_ci	case 3:
3678c2ecf20Sopenharmony_ci		buf[2] |= cOscFreq_17328kHz;
3688c2ecf20Sopenharmony_ci		break;
3698c2ecf20Sopenharmony_ci	default:		/* nothing */
3708c2ecf20Sopenharmony_ci		break;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	dprintk(PREFIX "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n",
3748c2ecf20Sopenharmony_ci		buf[0], buf[1], buf[2]);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	rc = i2c_master_send(client, buf, 3);
3778c2ecf20Sopenharmony_ci	if (rc != 3)
3788c2ecf20Sopenharmony_ci		printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic long saa6588_command(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	struct saa6588 *s = to_saa6588(sd);
3868c2ecf20Sopenharmony_ci	struct saa6588_command *a = arg;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	switch (cmd) {
3898c2ecf20Sopenharmony_ci		/* --- close() for /dev/radio --- */
3908c2ecf20Sopenharmony_ci	case SAA6588_CMD_CLOSE:
3918c2ecf20Sopenharmony_ci		s->data_available_for_read = 1;
3928c2ecf20Sopenharmony_ci		wake_up_interruptible(&s->read_queue);
3938c2ecf20Sopenharmony_ci		s->data_available_for_read = 0;
3948c2ecf20Sopenharmony_ci		a->result = 0;
3958c2ecf20Sopenharmony_ci		break;
3968c2ecf20Sopenharmony_ci		/* --- read() for /dev/radio --- */
3978c2ecf20Sopenharmony_ci	case SAA6588_CMD_READ:
3988c2ecf20Sopenharmony_ci		read_from_buf(s, a);
3998c2ecf20Sopenharmony_ci		break;
4008c2ecf20Sopenharmony_ci		/* --- poll() for /dev/radio --- */
4018c2ecf20Sopenharmony_ci	case SAA6588_CMD_POLL:
4028c2ecf20Sopenharmony_ci		a->poll_mask = 0;
4038c2ecf20Sopenharmony_ci		if (s->data_available_for_read)
4048c2ecf20Sopenharmony_ci			a->poll_mask |= EPOLLIN | EPOLLRDNORM;
4058c2ecf20Sopenharmony_ci		poll_wait(a->instance, &s->read_queue, a->event_list);
4068c2ecf20Sopenharmony_ci		break;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	default:
4098c2ecf20Sopenharmony_ci		/* nothing */
4108c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci	return 0;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic int saa6588_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct saa6588 *s = to_saa6588(sd);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	vt->capability |= V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO;
4208c2ecf20Sopenharmony_ci	if (s->sync)
4218c2ecf20Sopenharmony_ci		vt->rxsubchans |= V4L2_TUNER_SUB_RDS;
4228c2ecf20Sopenharmony_ci	return 0;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic int saa6588_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct saa6588 *s = to_saa6588(sd);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	saa6588_configure(s);
4308c2ecf20Sopenharmony_ci	return 0;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops saa6588_core_ops = {
4368c2ecf20Sopenharmony_ci	.command = saa6588_command,
4378c2ecf20Sopenharmony_ci};
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_tuner_ops saa6588_tuner_ops = {
4408c2ecf20Sopenharmony_ci	.g_tuner = saa6588_g_tuner,
4418c2ecf20Sopenharmony_ci	.s_tuner = saa6588_s_tuner,
4428c2ecf20Sopenharmony_ci};
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops saa6588_ops = {
4458c2ecf20Sopenharmony_ci	.core = &saa6588_core_ops,
4468c2ecf20Sopenharmony_ci	.tuner = &saa6588_tuner_ops,
4478c2ecf20Sopenharmony_ci};
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic int saa6588_probe(struct i2c_client *client,
4528c2ecf20Sopenharmony_ci			 const struct i2c_device_id *id)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct saa6588 *s;
4558c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	v4l_info(client, "saa6588 found @ 0x%x (%s)\n",
4588c2ecf20Sopenharmony_ci			client->addr << 1, client->adapter->name);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	s = devm_kzalloc(&client->dev, sizeof(*s), GFP_KERNEL);
4618c2ecf20Sopenharmony_ci	if (s == NULL)
4628c2ecf20Sopenharmony_ci		return -ENOMEM;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	s->buf_size = bufblocks * 3;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	s->buffer = devm_kzalloc(&client->dev, s->buf_size, GFP_KERNEL);
4678c2ecf20Sopenharmony_ci	if (s->buffer == NULL)
4688c2ecf20Sopenharmony_ci		return -ENOMEM;
4698c2ecf20Sopenharmony_ci	sd = &s->sd;
4708c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(sd, client, &saa6588_ops);
4718c2ecf20Sopenharmony_ci	spin_lock_init(&s->lock);
4728c2ecf20Sopenharmony_ci	s->block_count = 0;
4738c2ecf20Sopenharmony_ci	s->wr_index = 0;
4748c2ecf20Sopenharmony_ci	s->rd_index = 0;
4758c2ecf20Sopenharmony_ci	s->last_blocknum = 0xff;
4768c2ecf20Sopenharmony_ci	init_waitqueue_head(&s->read_queue);
4778c2ecf20Sopenharmony_ci	s->data_available_for_read = 0;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	saa6588_configure(s);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* start polling via eventd */
4828c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&s->work, saa6588_work);
4838c2ecf20Sopenharmony_ci	schedule_delayed_work(&s->work, 0);
4848c2ecf20Sopenharmony_ci	return 0;
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic int saa6588_remove(struct i2c_client *client)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = i2c_get_clientdata(client);
4908c2ecf20Sopenharmony_ci	struct saa6588 *s = to_saa6588(sd);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(sd);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&s->work);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	return 0;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic const struct i2c_device_id saa6588_id[] = {
5028c2ecf20Sopenharmony_ci	{ "saa6588", 0 },
5038c2ecf20Sopenharmony_ci	{ }
5048c2ecf20Sopenharmony_ci};
5058c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, saa6588_id);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic struct i2c_driver saa6588_driver = {
5088c2ecf20Sopenharmony_ci	.driver = {
5098c2ecf20Sopenharmony_ci		.name	= "saa6588",
5108c2ecf20Sopenharmony_ci	},
5118c2ecf20Sopenharmony_ci	.probe		= saa6588_probe,
5128c2ecf20Sopenharmony_ci	.remove		= saa6588_remove,
5138c2ecf20Sopenharmony_ci	.id_table	= saa6588_id,
5148c2ecf20Sopenharmony_ci};
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cimodule_i2c_driver(saa6588_driver);
517