18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/media/radio/si470x/radio-si470x-i2c.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * I2C driver for radios with Silicon Labs Si470x FM Radio Receivers 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2009 Samsung Electronics Co.Ltd 88c2ecf20Sopenharmony_ci * Author: Joonyoung Shim <jy0922.shim@samsung.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* driver definitions */ 138c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>"; 148c2ecf20Sopenharmony_ci#define DRIVER_CARD "Silicon Labs Si470x FM Radio" 158c2ecf20Sopenharmony_ci#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers" 168c2ecf20Sopenharmony_ci#define DRIVER_VERSION "1.0.2" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* kernel includes */ 198c2ecf20Sopenharmony_ci#include <linux/i2c.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 238c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "radio-si470x.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* I2C Device ID List */ 298c2ecf20Sopenharmony_cistatic const struct i2c_device_id si470x_i2c_id[] = { 308c2ecf20Sopenharmony_ci /* Generic Entry */ 318c2ecf20Sopenharmony_ci { "si470x", 0 }, 328c2ecf20Sopenharmony_ci /* Terminating entry */ 338c2ecf20Sopenharmony_ci { } 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, si470x_i2c_id); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/************************************************************************** 398c2ecf20Sopenharmony_ci * Module Parameters 408c2ecf20Sopenharmony_ci **************************************************************************/ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Radio Nr */ 438c2ecf20Sopenharmony_cistatic int radio_nr = -1; 448c2ecf20Sopenharmony_cimodule_param(radio_nr, int, 0444); 458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "Radio Nr"); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* RDS buffer blocks */ 488c2ecf20Sopenharmony_cistatic unsigned int rds_buf = 100; 498c2ecf20Sopenharmony_cimodule_param(rds_buf, uint, 0444); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*"); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* RDS maximum block errors */ 538c2ecf20Sopenharmony_cistatic unsigned short max_rds_errors = 1; 548c2ecf20Sopenharmony_ci/* 0 means 0 errors requiring correction */ 558c2ecf20Sopenharmony_ci/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */ 568c2ecf20Sopenharmony_ci/* 2 means 3-5 errors requiring correction */ 578c2ecf20Sopenharmony_ci/* 3 means 6+ errors or errors in checkword, correction not possible */ 588c2ecf20Sopenharmony_cimodule_param(max_rds_errors, ushort, 0644); 598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/************************************************************************** 648c2ecf20Sopenharmony_ci * I2C Definitions 658c2ecf20Sopenharmony_ci **************************************************************************/ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Write starts with the upper byte of register 0x02 */ 688c2ecf20Sopenharmony_ci#define WRITE_REG_NUM 8 698c2ecf20Sopenharmony_ci#define WRITE_INDEX(i) (i + 0x02) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* Read starts with the upper byte of register 0x0a */ 728c2ecf20Sopenharmony_ci#define READ_REG_NUM RADIO_REGISTER_NUM 738c2ecf20Sopenharmony_ci#define READ_INDEX(i) ((i + RADIO_REGISTER_NUM - 0x0a) % READ_REG_NUM) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/************************************************************************** 788c2ecf20Sopenharmony_ci * General Driver Functions - REGISTERs 798c2ecf20Sopenharmony_ci **************************************************************************/ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * si470x_get_register - read register 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic int si470x_get_register(struct si470x_device *radio, int regnr) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci __be16 buf[READ_REG_NUM]; 878c2ecf20Sopenharmony_ci struct i2c_msg msgs[1] = { 888c2ecf20Sopenharmony_ci { 898c2ecf20Sopenharmony_ci .addr = radio->client->addr, 908c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 918c2ecf20Sopenharmony_ci .len = sizeof(u16) * READ_REG_NUM, 928c2ecf20Sopenharmony_ci .buf = (void *)buf 938c2ecf20Sopenharmony_ci }, 948c2ecf20Sopenharmony_ci }; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (i2c_transfer(radio->client->adapter, msgs, 1) != 1) 978c2ecf20Sopenharmony_ci return -EIO; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci radio->registers[regnr] = __be16_to_cpu(buf[READ_INDEX(regnr)]); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* 1068c2ecf20Sopenharmony_ci * si470x_set_register - write register 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistatic int si470x_set_register(struct si470x_device *radio, int regnr) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci int i; 1118c2ecf20Sopenharmony_ci __be16 buf[WRITE_REG_NUM]; 1128c2ecf20Sopenharmony_ci struct i2c_msg msgs[1] = { 1138c2ecf20Sopenharmony_ci { 1148c2ecf20Sopenharmony_ci .addr = radio->client->addr, 1158c2ecf20Sopenharmony_ci .len = sizeof(u16) * WRITE_REG_NUM, 1168c2ecf20Sopenharmony_ci .buf = (void *)buf 1178c2ecf20Sopenharmony_ci }, 1188c2ecf20Sopenharmony_ci }; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci for (i = 0; i < WRITE_REG_NUM; i++) 1218c2ecf20Sopenharmony_ci buf[i] = __cpu_to_be16(radio->registers[WRITE_INDEX(i)]); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (i2c_transfer(radio->client->adapter, msgs, 1) != 1) 1248c2ecf20Sopenharmony_ci return -EIO; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/************************************************************************** 1328c2ecf20Sopenharmony_ci * General Driver Functions - ENTIRE REGISTERS 1338c2ecf20Sopenharmony_ci **************************************************************************/ 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * si470x_get_all_registers - read entire registers 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic int si470x_get_all_registers(struct si470x_device *radio) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int i; 1418c2ecf20Sopenharmony_ci __be16 buf[READ_REG_NUM]; 1428c2ecf20Sopenharmony_ci struct i2c_msg msgs[1] = { 1438c2ecf20Sopenharmony_ci { 1448c2ecf20Sopenharmony_ci .addr = radio->client->addr, 1458c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 1468c2ecf20Sopenharmony_ci .len = sizeof(u16) * READ_REG_NUM, 1478c2ecf20Sopenharmony_ci .buf = (void *)buf 1488c2ecf20Sopenharmony_ci }, 1498c2ecf20Sopenharmony_ci }; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (i2c_transfer(radio->client->adapter, msgs, 1) != 1) 1528c2ecf20Sopenharmony_ci return -EIO; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci for (i = 0; i < READ_REG_NUM; i++) 1558c2ecf20Sopenharmony_ci radio->registers[i] = __be16_to_cpu(buf[READ_INDEX(i)]); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/************************************************************************** 1638c2ecf20Sopenharmony_ci * File Operations Interface 1648c2ecf20Sopenharmony_ci **************************************************************************/ 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/* 1678c2ecf20Sopenharmony_ci * si470x_fops_open - file open 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_cistatic int si470x_fops_open(struct file *file) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct si470x_device *radio = video_drvdata(file); 1728c2ecf20Sopenharmony_ci int retval = v4l2_fh_open(file); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (retval) 1758c2ecf20Sopenharmony_ci return retval; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (v4l2_fh_is_singular_file(file)) { 1788c2ecf20Sopenharmony_ci /* start radio */ 1798c2ecf20Sopenharmony_ci retval = si470x_start(radio); 1808c2ecf20Sopenharmony_ci if (retval < 0) 1818c2ecf20Sopenharmony_ci goto done; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* enable RDS / STC interrupt */ 1848c2ecf20Sopenharmony_ci radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDSIEN; 1858c2ecf20Sopenharmony_ci radio->registers[SYSCONFIG1] |= SYSCONFIG1_STCIEN; 1868c2ecf20Sopenharmony_ci radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_GPIO2; 1878c2ecf20Sopenharmony_ci radio->registers[SYSCONFIG1] |= 0x1 << 2; 1888c2ecf20Sopenharmony_ci retval = si470x_set_register(radio, SYSCONFIG1); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cidone: 1928c2ecf20Sopenharmony_ci if (retval) 1938c2ecf20Sopenharmony_ci v4l2_fh_release(file); 1948c2ecf20Sopenharmony_ci return retval; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * si470x_fops_release - file release 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_cistatic int si470x_fops_release(struct file *file) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct si470x_device *radio = video_drvdata(file); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (v4l2_fh_is_singular_file(file)) 2068c2ecf20Sopenharmony_ci /* stop radio */ 2078c2ecf20Sopenharmony_ci si470x_stop(radio); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return v4l2_fh_release(file); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/************************************************************************** 2158c2ecf20Sopenharmony_ci * Video4Linux Interface 2168c2ecf20Sopenharmony_ci **************************************************************************/ 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* 2198c2ecf20Sopenharmony_ci * si470x_vidioc_querycap - query device capabilities 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_cistatic int si470x_vidioc_querycap(struct file *file, void *priv, 2228c2ecf20Sopenharmony_ci struct v4l2_capability *capability) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci strscpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); 2258c2ecf20Sopenharmony_ci strscpy(capability->card, DRIVER_CARD, sizeof(capability->card)); 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/************************************************************************** 2328c2ecf20Sopenharmony_ci * I2C Interface 2338c2ecf20Sopenharmony_ci **************************************************************************/ 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* 2368c2ecf20Sopenharmony_ci * si470x_i2c_interrupt - interrupt handler 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_cistatic irqreturn_t si470x_i2c_interrupt(int irq, void *dev_id) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct si470x_device *radio = dev_id; 2418c2ecf20Sopenharmony_ci unsigned char regnr; 2428c2ecf20Sopenharmony_ci unsigned char blocknum; 2438c2ecf20Sopenharmony_ci unsigned short bler; /* rds block errors */ 2448c2ecf20Sopenharmony_ci unsigned short rds; 2458c2ecf20Sopenharmony_ci unsigned char tmpbuf[3]; 2468c2ecf20Sopenharmony_ci int retval = 0; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* check Seek/Tune Complete */ 2498c2ecf20Sopenharmony_ci retval = si470x_get_register(radio, STATUSRSSI); 2508c2ecf20Sopenharmony_ci if (retval < 0) 2518c2ecf20Sopenharmony_ci goto end; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (radio->registers[STATUSRSSI] & STATUSRSSI_STC) 2548c2ecf20Sopenharmony_ci complete(&radio->completion); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* safety checks */ 2578c2ecf20Sopenharmony_ci if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) 2588c2ecf20Sopenharmony_ci goto end; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Update RDS registers */ 2618c2ecf20Sopenharmony_ci for (regnr = 1; regnr < RDS_REGISTER_NUM; regnr++) { 2628c2ecf20Sopenharmony_ci retval = si470x_get_register(radio, STATUSRSSI + regnr); 2638c2ecf20Sopenharmony_ci if (retval < 0) 2648c2ecf20Sopenharmony_ci goto end; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* get rds blocks */ 2688c2ecf20Sopenharmony_ci if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) 2698c2ecf20Sopenharmony_ci /* No RDS group ready, better luck next time */ 2708c2ecf20Sopenharmony_ci goto end; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci for (blocknum = 0; blocknum < 4; blocknum++) { 2738c2ecf20Sopenharmony_ci switch (blocknum) { 2748c2ecf20Sopenharmony_ci default: 2758c2ecf20Sopenharmony_ci bler = (radio->registers[STATUSRSSI] & 2768c2ecf20Sopenharmony_ci STATUSRSSI_BLERA) >> 9; 2778c2ecf20Sopenharmony_ci rds = radio->registers[RDSA]; 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci case 1: 2808c2ecf20Sopenharmony_ci bler = (radio->registers[READCHAN] & 2818c2ecf20Sopenharmony_ci READCHAN_BLERB) >> 14; 2828c2ecf20Sopenharmony_ci rds = radio->registers[RDSB]; 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case 2: 2858c2ecf20Sopenharmony_ci bler = (radio->registers[READCHAN] & 2868c2ecf20Sopenharmony_ci READCHAN_BLERC) >> 12; 2878c2ecf20Sopenharmony_ci rds = radio->registers[RDSC]; 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case 3: 2908c2ecf20Sopenharmony_ci bler = (radio->registers[READCHAN] & 2918c2ecf20Sopenharmony_ci READCHAN_BLERD) >> 10; 2928c2ecf20Sopenharmony_ci rds = radio->registers[RDSD]; 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Fill the V4L2 RDS buffer */ 2978c2ecf20Sopenharmony_ci put_unaligned_le16(rds, &tmpbuf); 2988c2ecf20Sopenharmony_ci tmpbuf[2] = blocknum; /* offset name */ 2998c2ecf20Sopenharmony_ci tmpbuf[2] |= blocknum << 3; /* received offset */ 3008c2ecf20Sopenharmony_ci if (bler > max_rds_errors) 3018c2ecf20Sopenharmony_ci tmpbuf[2] |= 0x80; /* uncorrectable errors */ 3028c2ecf20Sopenharmony_ci else if (bler > 0) 3038c2ecf20Sopenharmony_ci tmpbuf[2] |= 0x40; /* corrected error(s) */ 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* copy RDS block to internal buffer */ 3068c2ecf20Sopenharmony_ci memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); 3078c2ecf20Sopenharmony_ci radio->wr_index += 3; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* wrap write pointer */ 3108c2ecf20Sopenharmony_ci if (radio->wr_index >= radio->buf_size) 3118c2ecf20Sopenharmony_ci radio->wr_index = 0; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* check for overflow */ 3148c2ecf20Sopenharmony_ci if (radio->wr_index == radio->rd_index) { 3158c2ecf20Sopenharmony_ci /* increment and wrap read pointer */ 3168c2ecf20Sopenharmony_ci radio->rd_index += 3; 3178c2ecf20Sopenharmony_ci if (radio->rd_index >= radio->buf_size) 3188c2ecf20Sopenharmony_ci radio->rd_index = 0; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (radio->wr_index != radio->rd_index) 3238c2ecf20Sopenharmony_ci wake_up_interruptible(&radio->read_queue); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ciend: 3268c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* 3318c2ecf20Sopenharmony_ci * si470x_i2c_probe - probe for the device 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_cistatic int si470x_i2c_probe(struct i2c_client *client) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct si470x_device *radio; 3368c2ecf20Sopenharmony_ci int retval = 0; 3378c2ecf20Sopenharmony_ci unsigned char version_warning = 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* private data allocation and initialization */ 3408c2ecf20Sopenharmony_ci radio = devm_kzalloc(&client->dev, sizeof(*radio), GFP_KERNEL); 3418c2ecf20Sopenharmony_ci if (!radio) { 3428c2ecf20Sopenharmony_ci retval = -ENOMEM; 3438c2ecf20Sopenharmony_ci goto err_initial; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci radio->client = client; 3478c2ecf20Sopenharmony_ci radio->band = 1; /* Default to 76 - 108 MHz */ 3488c2ecf20Sopenharmony_ci mutex_init(&radio->lock); 3498c2ecf20Sopenharmony_ci init_completion(&radio->completion); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci radio->get_register = si470x_get_register; 3528c2ecf20Sopenharmony_ci radio->set_register = si470x_set_register; 3538c2ecf20Sopenharmony_ci radio->fops_open = si470x_fops_open; 3548c2ecf20Sopenharmony_ci radio->fops_release = si470x_fops_release; 3558c2ecf20Sopenharmony_ci radio->vidioc_querycap = si470x_vidioc_querycap; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci retval = v4l2_device_register(&client->dev, &radio->v4l2_dev); 3588c2ecf20Sopenharmony_ci if (retval < 0) { 3598c2ecf20Sopenharmony_ci dev_err(&client->dev, "couldn't register v4l2_device\n"); 3608c2ecf20Sopenharmony_ci goto err_initial; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&radio->hdl, 2); 3648c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, 3658c2ecf20Sopenharmony_ci V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); 3668c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, 3678c2ecf20Sopenharmony_ci V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 15); 3688c2ecf20Sopenharmony_ci if (radio->hdl.error) { 3698c2ecf20Sopenharmony_ci retval = radio->hdl.error; 3708c2ecf20Sopenharmony_ci dev_err(&client->dev, "couldn't register control\n"); 3718c2ecf20Sopenharmony_ci goto err_all; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* video device initialization */ 3758c2ecf20Sopenharmony_ci radio->videodev = si470x_viddev_template; 3768c2ecf20Sopenharmony_ci radio->videodev.ctrl_handler = &radio->hdl; 3778c2ecf20Sopenharmony_ci radio->videodev.lock = &radio->lock; 3788c2ecf20Sopenharmony_ci radio->videodev.v4l2_dev = &radio->v4l2_dev; 3798c2ecf20Sopenharmony_ci radio->videodev.release = video_device_release_empty; 3808c2ecf20Sopenharmony_ci radio->videodev.device_caps = 3818c2ecf20Sopenharmony_ci V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_READWRITE | V4L2_CAP_TUNER | 3828c2ecf20Sopenharmony_ci V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; 3838c2ecf20Sopenharmony_ci video_set_drvdata(&radio->videodev, radio); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci radio->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset", 3868c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 3878c2ecf20Sopenharmony_ci if (IS_ERR(radio->gpio_reset)) { 3888c2ecf20Sopenharmony_ci retval = PTR_ERR(radio->gpio_reset); 3898c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to request gpio: %d\n", retval); 3908c2ecf20Sopenharmony_ci goto err_all; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (radio->gpio_reset) 3948c2ecf20Sopenharmony_ci gpiod_set_value(radio->gpio_reset, 1); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* power up : need 110ms */ 3978c2ecf20Sopenharmony_ci radio->registers[POWERCFG] = POWERCFG_ENABLE; 3988c2ecf20Sopenharmony_ci if (si470x_set_register(radio, POWERCFG) < 0) { 3998c2ecf20Sopenharmony_ci retval = -EIO; 4008c2ecf20Sopenharmony_ci goto err_all; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci msleep(110); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* get device and chip versions */ 4058c2ecf20Sopenharmony_ci if (si470x_get_all_registers(radio) < 0) { 4068c2ecf20Sopenharmony_ci retval = -EIO; 4078c2ecf20Sopenharmony_ci goto err_all; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", 4108c2ecf20Sopenharmony_ci radio->registers[DEVICEID], radio->registers[SI_CHIPID]); 4118c2ecf20Sopenharmony_ci if ((radio->registers[SI_CHIPID] & SI_CHIPID_FIRMWARE) < RADIO_FW_VERSION) { 4128c2ecf20Sopenharmony_ci dev_warn(&client->dev, 4138c2ecf20Sopenharmony_ci "This driver is known to work with firmware version %hu,\n", 4148c2ecf20Sopenharmony_ci RADIO_FW_VERSION); 4158c2ecf20Sopenharmony_ci dev_warn(&client->dev, 4168c2ecf20Sopenharmony_ci "but the device has firmware version %hu.\n", 4178c2ecf20Sopenharmony_ci radio->registers[SI_CHIPID] & SI_CHIPID_FIRMWARE); 4188c2ecf20Sopenharmony_ci version_warning = 1; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* give out version warning */ 4228c2ecf20Sopenharmony_ci if (version_warning == 1) { 4238c2ecf20Sopenharmony_ci dev_warn(&client->dev, 4248c2ecf20Sopenharmony_ci "If you have some trouble using this driver,\n"); 4258c2ecf20Sopenharmony_ci dev_warn(&client->dev, 4268c2ecf20Sopenharmony_ci "please report to V4L ML at linux-media@vger.kernel.org\n"); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* set initial frequency */ 4308c2ecf20Sopenharmony_ci si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* rds buffer allocation */ 4338c2ecf20Sopenharmony_ci radio->buf_size = rds_buf * 3; 4348c2ecf20Sopenharmony_ci radio->buffer = devm_kmalloc(&client->dev, radio->buf_size, GFP_KERNEL); 4358c2ecf20Sopenharmony_ci if (!radio->buffer) { 4368c2ecf20Sopenharmony_ci retval = -EIO; 4378c2ecf20Sopenharmony_ci goto err_all; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* rds buffer configuration */ 4418c2ecf20Sopenharmony_ci radio->wr_index = 0; 4428c2ecf20Sopenharmony_ci radio->rd_index = 0; 4438c2ecf20Sopenharmony_ci init_waitqueue_head(&radio->read_queue); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci retval = devm_request_threaded_irq(&client->dev, client->irq, NULL, 4468c2ecf20Sopenharmony_ci si470x_i2c_interrupt, 4478c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 4488c2ecf20Sopenharmony_ci DRIVER_NAME, radio); 4498c2ecf20Sopenharmony_ci if (retval) { 4508c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to register interrupt\n"); 4518c2ecf20Sopenharmony_ci goto err_all; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* register video device */ 4558c2ecf20Sopenharmony_ci retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, 4568c2ecf20Sopenharmony_ci radio_nr); 4578c2ecf20Sopenharmony_ci if (retval) { 4588c2ecf20Sopenharmony_ci dev_warn(&client->dev, "Could not register video device\n"); 4598c2ecf20Sopenharmony_ci goto err_all; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci i2c_set_clientdata(client, radio); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_cierr_all: 4658c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&radio->hdl); 4668c2ecf20Sopenharmony_ci v4l2_device_unregister(&radio->v4l2_dev); 4678c2ecf20Sopenharmony_cierr_initial: 4688c2ecf20Sopenharmony_ci return retval; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/* 4738c2ecf20Sopenharmony_ci * si470x_i2c_remove - remove the device 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_cistatic int si470x_i2c_remove(struct i2c_client *client) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct si470x_device *radio = i2c_get_clientdata(client); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci video_unregister_device(&radio->videodev); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (radio->gpio_reset) 4828c2ecf20Sopenharmony_ci gpiod_set_value(radio->gpio_reset, 0); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&radio->hdl); 4858c2ecf20Sopenharmony_ci v4l2_device_unregister(&radio->v4l2_dev); 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4918c2ecf20Sopenharmony_ci/* 4928c2ecf20Sopenharmony_ci * si470x_i2c_suspend - suspend the device 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_cistatic int si470x_i2c_suspend(struct device *dev) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 4978c2ecf20Sopenharmony_ci struct si470x_device *radio = i2c_get_clientdata(client); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* power down */ 5008c2ecf20Sopenharmony_ci radio->registers[POWERCFG] |= POWERCFG_DISABLE; 5018c2ecf20Sopenharmony_ci if (si470x_set_register(radio, POWERCFG) < 0) 5028c2ecf20Sopenharmony_ci return -EIO; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci/* 5098c2ecf20Sopenharmony_ci * si470x_i2c_resume - resume the device 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_cistatic int si470x_i2c_resume(struct device *dev) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 5148c2ecf20Sopenharmony_ci struct si470x_device *radio = i2c_get_clientdata(client); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* power up : need 110ms */ 5178c2ecf20Sopenharmony_ci radio->registers[POWERCFG] |= POWERCFG_ENABLE; 5188c2ecf20Sopenharmony_ci if (si470x_set_register(radio, POWERCFG) < 0) 5198c2ecf20Sopenharmony_ci return -EIO; 5208c2ecf20Sopenharmony_ci msleep(110); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci return 0; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(si470x_i2c_pm, si470x_i2c_suspend, si470x_i2c_resume); 5268c2ecf20Sopenharmony_ci#endif 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF) 5298c2ecf20Sopenharmony_cistatic const struct of_device_id si470x_of_match[] = { 5308c2ecf20Sopenharmony_ci { .compatible = "silabs,si470x" }, 5318c2ecf20Sopenharmony_ci { }, 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, si470x_of_match); 5348c2ecf20Sopenharmony_ci#endif 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci/* 5378c2ecf20Sopenharmony_ci * si470x_i2c_driver - i2c driver interface 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_cistatic struct i2c_driver si470x_i2c_driver = { 5408c2ecf20Sopenharmony_ci .driver = { 5418c2ecf20Sopenharmony_ci .name = "si470x", 5428c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(si470x_of_match), 5438c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5448c2ecf20Sopenharmony_ci .pm = &si470x_i2c_pm, 5458c2ecf20Sopenharmony_ci#endif 5468c2ecf20Sopenharmony_ci }, 5478c2ecf20Sopenharmony_ci .probe_new = si470x_i2c_probe, 5488c2ecf20Sopenharmony_ci .remove = si470x_i2c_remove, 5498c2ecf20Sopenharmony_ci .id_table = si470x_i2c_id, 5508c2ecf20Sopenharmony_ci}; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cimodule_i2c_driver(si470x_i2c_driver); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5558c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 5568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 5578c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION); 558