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