1b09cd163SJoonyoung Shim /* 2b09cd163SJoonyoung Shim * drivers/media/radio/si470x/radio-si470x-usb.c 3b09cd163SJoonyoung Shim * 4b09cd163SJoonyoung Shim * USB driver for radios with Silicon Labs Si470x FM Radio Receivers 5b09cd163SJoonyoung Shim * 6b09cd163SJoonyoung Shim * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net> 7b09cd163SJoonyoung Shim * 8b09cd163SJoonyoung Shim * This program is free software; you can redistribute it and/or modify 9b09cd163SJoonyoung Shim * it under the terms of the GNU General Public License as published by 10b09cd163SJoonyoung Shim * the Free Software Foundation; either version 2 of the License, or 11b09cd163SJoonyoung Shim * (at your option) any later version. 12b09cd163SJoonyoung Shim * 13b09cd163SJoonyoung Shim * This program is distributed in the hope that it will be useful, 14b09cd163SJoonyoung Shim * but WITHOUT ANY WARRANTY; without even the implied warranty of 15b09cd163SJoonyoung Shim * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16b09cd163SJoonyoung Shim * GNU General Public License for more details. 17b09cd163SJoonyoung Shim */ 18b09cd163SJoonyoung Shim 19b09cd163SJoonyoung Shim 20b09cd163SJoonyoung Shim /* 21b09cd163SJoonyoung Shim * ToDo: 22b09cd163SJoonyoung Shim * - add firmware download/update support 23b09cd163SJoonyoung Shim */ 24b09cd163SJoonyoung Shim 25b09cd163SJoonyoung Shim 26b09cd163SJoonyoung Shim /* driver definitions */ 27b09cd163SJoonyoung Shim #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" 28b09cd163SJoonyoung Shim #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" 29b09cd163SJoonyoung Shim #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" 30b09cd163SJoonyoung Shim #define DRIVER_VERSION "1.0.10" 31b09cd163SJoonyoung Shim 32b09cd163SJoonyoung Shim /* kernel includes */ 33b09cd163SJoonyoung Shim #include <linux/usb.h> 34b09cd163SJoonyoung Shim #include <linux/hid.h> 355a0e3ad6STejun Heo #include <linux/slab.h> 36b09cd163SJoonyoung Shim 37b09cd163SJoonyoung Shim #include "radio-si470x.h" 38b09cd163SJoonyoung Shim 39b09cd163SJoonyoung Shim 40b09cd163SJoonyoung Shim /* USB Device ID List */ 411ab2234eSArvind Yadav static const struct usb_device_id si470x_usb_driver_id_table[] = { 42b09cd163SJoonyoung Shim /* Silicon Labs USB FM Radio Reference Design */ 43b09cd163SJoonyoung Shim { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) }, 44b09cd163SJoonyoung Shim /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */ 45b09cd163SJoonyoung Shim { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) }, 46b09cd163SJoonyoung Shim /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */ 47b09cd163SJoonyoung Shim { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) }, 48b09cd163SJoonyoung Shim /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */ 49b09cd163SJoonyoung Shim { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) }, 5011030183SHans de Goede /* Axentia ALERT FM USB Receiver */ 5111030183SHans de Goede { USB_DEVICE_AND_INTERFACE_INFO(0x12cf, 0x7111, USB_CLASS_HID, 0, 0) }, 52b09cd163SJoonyoung Shim /* Terminating entry */ 53b09cd163SJoonyoung Shim { } 54b09cd163SJoonyoung Shim }; 55b09cd163SJoonyoung Shim MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table); 56b09cd163SJoonyoung Shim 57b09cd163SJoonyoung Shim 58b09cd163SJoonyoung Shim 59b09cd163SJoonyoung Shim /************************************************************************** 60b09cd163SJoonyoung Shim * Module Parameters 61b09cd163SJoonyoung Shim **************************************************************************/ 62b09cd163SJoonyoung Shim 63b09cd163SJoonyoung Shim /* Radio Nr */ 64b09cd163SJoonyoung Shim static int radio_nr = -1; 65b09cd163SJoonyoung Shim module_param(radio_nr, int, 0444); 66b09cd163SJoonyoung Shim MODULE_PARM_DESC(radio_nr, "Radio Nr"); 67b09cd163SJoonyoung Shim 68b09cd163SJoonyoung Shim /* USB timeout */ 69b09cd163SJoonyoung Shim static unsigned int usb_timeout = 500; 70b09cd163SJoonyoung Shim module_param(usb_timeout, uint, 0644); 71b09cd163SJoonyoung Shim MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*"); 72b09cd163SJoonyoung Shim 73b09cd163SJoonyoung Shim /* RDS buffer blocks */ 74b09cd163SJoonyoung Shim static unsigned int rds_buf = 100; 75b09cd163SJoonyoung Shim module_param(rds_buf, uint, 0444); 76b09cd163SJoonyoung Shim MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*"); 77b09cd163SJoonyoung Shim 78b09cd163SJoonyoung Shim /* RDS maximum block errors */ 79b09cd163SJoonyoung Shim static unsigned short max_rds_errors = 1; 80b09cd163SJoonyoung Shim /* 0 means 0 errors requiring correction */ 81b09cd163SJoonyoung Shim /* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */ 82b09cd163SJoonyoung Shim /* 2 means 3-5 errors requiring correction */ 83b09cd163SJoonyoung Shim /* 3 means 6+ errors or errors in checkword, correction not possible */ 84b09cd163SJoonyoung Shim module_param(max_rds_errors, ushort, 0644); 85b09cd163SJoonyoung Shim MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); 86b09cd163SJoonyoung Shim 87b09cd163SJoonyoung Shim 88b09cd163SJoonyoung Shim 89b09cd163SJoonyoung Shim /************************************************************************** 90b09cd163SJoonyoung Shim * USB HID Reports 91b09cd163SJoonyoung Shim **************************************************************************/ 92b09cd163SJoonyoung Shim 93b09cd163SJoonyoung Shim /* Reports 1-16 give direct read/write access to the 16 Si470x registers */ 94b09cd163SJoonyoung Shim /* with the (REPORT_ID - 1) corresponding to the register address across USB */ 95b09cd163SJoonyoung Shim /* endpoint 0 using GET_REPORT and SET_REPORT */ 96b09cd163SJoonyoung Shim #define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1) 97b09cd163SJoonyoung Shim #define REGISTER_REPORT(reg) ((reg) + 1) 98b09cd163SJoonyoung Shim 99b09cd163SJoonyoung Shim /* Report 17 gives direct read/write access to the entire Si470x register */ 100b09cd163SJoonyoung Shim /* map across endpoint 0 using GET_REPORT and SET_REPORT */ 101b09cd163SJoonyoung Shim #define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) 102b09cd163SJoonyoung Shim #define ENTIRE_REPORT 17 103b09cd163SJoonyoung Shim 104b09cd163SJoonyoung Shim /* Report 18 is used to send the lowest 6 Si470x registers up the HID */ 105b09cd163SJoonyoung Shim /* interrupt endpoint 1 to Windows every 20 milliseconds for status */ 106b09cd163SJoonyoung Shim #define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) 107b09cd163SJoonyoung Shim #define RDS_REPORT 18 108b09cd163SJoonyoung Shim 109b09cd163SJoonyoung Shim /* Report 19: LED state */ 110b09cd163SJoonyoung Shim #define LED_REPORT_SIZE 3 111b09cd163SJoonyoung Shim #define LED_REPORT 19 112b09cd163SJoonyoung Shim 113b09cd163SJoonyoung Shim /* Report 19: stream */ 114b09cd163SJoonyoung Shim #define STREAM_REPORT_SIZE 3 115b09cd163SJoonyoung Shim #define STREAM_REPORT 19 116b09cd163SJoonyoung Shim 117b09cd163SJoonyoung Shim /* Report 20: scratch */ 118b09cd163SJoonyoung Shim #define SCRATCH_PAGE_SIZE 63 119b09cd163SJoonyoung Shim #define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1) 120b09cd163SJoonyoung Shim #define SCRATCH_REPORT 20 121b09cd163SJoonyoung Shim 122b09cd163SJoonyoung Shim /* Reports 19-22: flash upgrade of the C8051F321 */ 123b09cd163SJoonyoung Shim #define WRITE_REPORT_SIZE 4 124b09cd163SJoonyoung Shim #define WRITE_REPORT 19 125b09cd163SJoonyoung Shim #define FLASH_REPORT_SIZE 64 126b09cd163SJoonyoung Shim #define FLASH_REPORT 20 127b09cd163SJoonyoung Shim #define CRC_REPORT_SIZE 3 128b09cd163SJoonyoung Shim #define CRC_REPORT 21 129b09cd163SJoonyoung Shim #define RESPONSE_REPORT_SIZE 2 130b09cd163SJoonyoung Shim #define RESPONSE_REPORT 22 131b09cd163SJoonyoung Shim 132b09cd163SJoonyoung Shim /* Report 23: currently unused, but can accept 60 byte reports on the HID */ 133b09cd163SJoonyoung Shim /* interrupt out endpoint 2 every 1 millisecond */ 134b09cd163SJoonyoung Shim #define UNUSED_REPORT 23 135b09cd163SJoonyoung Shim 136567e2e96SHans Verkuil #define MAX_REPORT_SIZE 64 137567e2e96SHans Verkuil 138b09cd163SJoonyoung Shim 139b09cd163SJoonyoung Shim 140b09cd163SJoonyoung Shim /************************************************************************** 1419dcb79c2STobias Lorenz * Software/Hardware Versions from Scratch Page 142b09cd163SJoonyoung Shim **************************************************************************/ 143b09cd163SJoonyoung Shim #define RADIO_HW_VERSION 1 144b09cd163SJoonyoung Shim 145b09cd163SJoonyoung Shim 146b09cd163SJoonyoung Shim 147b09cd163SJoonyoung Shim /************************************************************************** 148b09cd163SJoonyoung Shim * LED State Definitions 149b09cd163SJoonyoung Shim **************************************************************************/ 150b09cd163SJoonyoung Shim #define LED_COMMAND 0x35 151b09cd163SJoonyoung Shim 152b09cd163SJoonyoung Shim #define NO_CHANGE_LED 0x00 153b09cd163SJoonyoung Shim #define ALL_COLOR_LED 0x01 /* streaming state */ 154b09cd163SJoonyoung Shim #define BLINK_GREEN_LED 0x02 /* connect state */ 155b09cd163SJoonyoung Shim #define BLINK_RED_LED 0x04 156b09cd163SJoonyoung Shim #define BLINK_ORANGE_LED 0x10 /* disconnect state */ 157b09cd163SJoonyoung Shim #define SOLID_GREEN_LED 0x20 /* tuning/seeking state */ 158b09cd163SJoonyoung Shim #define SOLID_RED_LED 0x40 /* bootload state */ 159b09cd163SJoonyoung Shim #define SOLID_ORANGE_LED 0x80 160b09cd163SJoonyoung Shim 161b09cd163SJoonyoung Shim 162b09cd163SJoonyoung Shim 163b09cd163SJoonyoung Shim /************************************************************************** 164b09cd163SJoonyoung Shim * Stream State Definitions 165b09cd163SJoonyoung Shim **************************************************************************/ 166b09cd163SJoonyoung Shim #define STREAM_COMMAND 0x36 167b09cd163SJoonyoung Shim #define STREAM_VIDPID 0x00 168b09cd163SJoonyoung Shim #define STREAM_AUDIO 0xff 169b09cd163SJoonyoung Shim 170b09cd163SJoonyoung Shim 171b09cd163SJoonyoung Shim 172b09cd163SJoonyoung Shim /************************************************************************** 173b09cd163SJoonyoung Shim * Bootloader / Flash Commands 174b09cd163SJoonyoung Shim **************************************************************************/ 175b09cd163SJoonyoung Shim 176b09cd163SJoonyoung Shim /* unique id sent to bootloader and required to put into a bootload state */ 177b09cd163SJoonyoung Shim #define UNIQUE_BL_ID 0x34 178b09cd163SJoonyoung Shim 179b09cd163SJoonyoung Shim /* mask for the flash data */ 180b09cd163SJoonyoung Shim #define FLASH_DATA_MASK 0x55 181b09cd163SJoonyoung Shim 182b09cd163SJoonyoung Shim /* bootloader commands */ 183b09cd163SJoonyoung Shim #define GET_SW_VERSION_COMMAND 0x00 184b09cd163SJoonyoung Shim #define SET_PAGE_COMMAND 0x01 185b09cd163SJoonyoung Shim #define ERASE_PAGE_COMMAND 0x02 186b09cd163SJoonyoung Shim #define WRITE_PAGE_COMMAND 0x03 187b09cd163SJoonyoung Shim #define CRC_ON_PAGE_COMMAND 0x04 188b09cd163SJoonyoung Shim #define READ_FLASH_BYTE_COMMAND 0x05 189b09cd163SJoonyoung Shim #define RESET_DEVICE_COMMAND 0x06 190b09cd163SJoonyoung Shim #define GET_HW_VERSION_COMMAND 0x07 191b09cd163SJoonyoung Shim #define BLANK 0xff 192b09cd163SJoonyoung Shim 193b09cd163SJoonyoung Shim /* bootloader command responses */ 194b09cd163SJoonyoung Shim #define COMMAND_OK 0x01 195b09cd163SJoonyoung Shim #define COMMAND_FAILED 0x02 196b09cd163SJoonyoung Shim #define COMMAND_PENDING 0x03 197b09cd163SJoonyoung Shim 198b09cd163SJoonyoung Shim 199b09cd163SJoonyoung Shim 200b09cd163SJoonyoung Shim /************************************************************************** 201b09cd163SJoonyoung Shim * General Driver Functions - REGISTER_REPORTs 202b09cd163SJoonyoung Shim **************************************************************************/ 203b09cd163SJoonyoung Shim 204b09cd163SJoonyoung Shim /* 205b09cd163SJoonyoung Shim * si470x_get_report - receive a HID report 206b09cd163SJoonyoung Shim */ 207b09cd163SJoonyoung Shim static int si470x_get_report(struct si470x_device *radio, void *buf, int size) 208b09cd163SJoonyoung Shim { 209567e2e96SHans Verkuil unsigned char *report = buf; 210b09cd163SJoonyoung Shim int retval; 211b09cd163SJoonyoung Shim 212b09cd163SJoonyoung Shim retval = usb_control_msg(radio->usbdev, 213b09cd163SJoonyoung Shim usb_rcvctrlpipe(radio->usbdev, 0), 214b09cd163SJoonyoung Shim HID_REQ_GET_REPORT, 215b09cd163SJoonyoung Shim USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, 216b09cd163SJoonyoung Shim report[0], 2, 217b09cd163SJoonyoung Shim buf, size, usb_timeout); 218b09cd163SJoonyoung Shim 219b09cd163SJoonyoung Shim if (retval < 0) 220a9d6fd5eSJoonyoung Shim dev_warn(&radio->intf->dev, 221a9d6fd5eSJoonyoung Shim "si470x_get_report: usb_control_msg returned %d\n", 222b09cd163SJoonyoung Shim retval); 223b09cd163SJoonyoung Shim return retval; 224b09cd163SJoonyoung Shim } 225b09cd163SJoonyoung Shim 226b09cd163SJoonyoung Shim 227b09cd163SJoonyoung Shim /* 228b09cd163SJoonyoung Shim * si470x_set_report - send a HID report 229b09cd163SJoonyoung Shim */ 230b09cd163SJoonyoung Shim static int si470x_set_report(struct si470x_device *radio, void *buf, int size) 231b09cd163SJoonyoung Shim { 232567e2e96SHans Verkuil unsigned char *report = buf; 233b09cd163SJoonyoung Shim int retval; 234b09cd163SJoonyoung Shim 235b09cd163SJoonyoung Shim retval = usb_control_msg(radio->usbdev, 236b09cd163SJoonyoung Shim usb_sndctrlpipe(radio->usbdev, 0), 237b09cd163SJoonyoung Shim HID_REQ_SET_REPORT, 238b09cd163SJoonyoung Shim USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, 239b09cd163SJoonyoung Shim report[0], 2, 240b09cd163SJoonyoung Shim buf, size, usb_timeout); 241b09cd163SJoonyoung Shim 242b09cd163SJoonyoung Shim if (retval < 0) 243a9d6fd5eSJoonyoung Shim dev_warn(&radio->intf->dev, 244a9d6fd5eSJoonyoung Shim "si470x_set_report: usb_control_msg returned %d\n", 245b09cd163SJoonyoung Shim retval); 246b09cd163SJoonyoung Shim return retval; 247b09cd163SJoonyoung Shim } 248b09cd163SJoonyoung Shim 249b09cd163SJoonyoung Shim 250b09cd163SJoonyoung Shim /* 251b09cd163SJoonyoung Shim * si470x_get_register - read register 252b09cd163SJoonyoung Shim */ 25358757984SMauro Carvalho Chehab static int si470x_get_register(struct si470x_device *radio, int regnr) 254b09cd163SJoonyoung Shim { 255b09cd163SJoonyoung Shim int retval; 256b09cd163SJoonyoung Shim 257567e2e96SHans Verkuil radio->usb_buf[0] = REGISTER_REPORT(regnr); 258b09cd163SJoonyoung Shim 259567e2e96SHans Verkuil retval = si470x_get_report(radio, radio->usb_buf, REGISTER_REPORT_SIZE); 260b09cd163SJoonyoung Shim 261b09cd163SJoonyoung Shim if (retval >= 0) 262567e2e96SHans Verkuil radio->registers[regnr] = get_unaligned_be16(&radio->usb_buf[1]); 263b09cd163SJoonyoung Shim 264b09cd163SJoonyoung Shim return (retval < 0) ? -EINVAL : 0; 265b09cd163SJoonyoung Shim } 266b09cd163SJoonyoung Shim 267b09cd163SJoonyoung Shim 268b09cd163SJoonyoung Shim /* 269b09cd163SJoonyoung Shim * si470x_set_register - write register 270b09cd163SJoonyoung Shim */ 27158757984SMauro Carvalho Chehab static int si470x_set_register(struct si470x_device *radio, int regnr) 272b09cd163SJoonyoung Shim { 273b09cd163SJoonyoung Shim int retval; 274b09cd163SJoonyoung Shim 275567e2e96SHans Verkuil radio->usb_buf[0] = REGISTER_REPORT(regnr); 276567e2e96SHans Verkuil put_unaligned_be16(radio->registers[regnr], &radio->usb_buf[1]); 277b09cd163SJoonyoung Shim 278567e2e96SHans Verkuil retval = si470x_set_report(radio, radio->usb_buf, REGISTER_REPORT_SIZE); 279b09cd163SJoonyoung Shim 280b09cd163SJoonyoung Shim return (retval < 0) ? -EINVAL : 0; 281b09cd163SJoonyoung Shim } 282b09cd163SJoonyoung Shim 283b09cd163SJoonyoung Shim 284b09cd163SJoonyoung Shim 285b09cd163SJoonyoung Shim /************************************************************************** 286b09cd163SJoonyoung Shim * General Driver Functions - ENTIRE_REPORT 287b09cd163SJoonyoung Shim **************************************************************************/ 288b09cd163SJoonyoung Shim 289b09cd163SJoonyoung Shim /* 290b09cd163SJoonyoung Shim * si470x_get_all_registers - read entire registers 291b09cd163SJoonyoung Shim */ 292b09cd163SJoonyoung Shim static int si470x_get_all_registers(struct si470x_device *radio) 293b09cd163SJoonyoung Shim { 294b09cd163SJoonyoung Shim int retval; 295b09cd163SJoonyoung Shim unsigned char regnr; 296b09cd163SJoonyoung Shim 297567e2e96SHans Verkuil radio->usb_buf[0] = ENTIRE_REPORT; 298b09cd163SJoonyoung Shim 299567e2e96SHans Verkuil retval = si470x_get_report(radio, radio->usb_buf, ENTIRE_REPORT_SIZE); 300b09cd163SJoonyoung Shim 301b09cd163SJoonyoung Shim if (retval >= 0) 302b09cd163SJoonyoung Shim for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) 303b09cd163SJoonyoung Shim radio->registers[regnr] = get_unaligned_be16( 304567e2e96SHans Verkuil &radio->usb_buf[regnr * RADIO_REGISTER_SIZE + 1]); 305b09cd163SJoonyoung Shim 306b09cd163SJoonyoung Shim return (retval < 0) ? -EINVAL : 0; 307b09cd163SJoonyoung Shim } 308b09cd163SJoonyoung Shim 309b09cd163SJoonyoung Shim 310b09cd163SJoonyoung Shim 311b09cd163SJoonyoung Shim /************************************************************************** 312b09cd163SJoonyoung Shim * General Driver Functions - LED_REPORT 313b09cd163SJoonyoung Shim **************************************************************************/ 314b09cd163SJoonyoung Shim 315b09cd163SJoonyoung Shim /* 316b09cd163SJoonyoung Shim * si470x_set_led_state - sets the led state 317b09cd163SJoonyoung Shim */ 318b09cd163SJoonyoung Shim static int si470x_set_led_state(struct si470x_device *radio, 319b09cd163SJoonyoung Shim unsigned char led_state) 320b09cd163SJoonyoung Shim { 321b09cd163SJoonyoung Shim int retval; 322b09cd163SJoonyoung Shim 323567e2e96SHans Verkuil radio->usb_buf[0] = LED_REPORT; 324567e2e96SHans Verkuil radio->usb_buf[1] = LED_COMMAND; 325567e2e96SHans Verkuil radio->usb_buf[2] = led_state; 326b09cd163SJoonyoung Shim 327567e2e96SHans Verkuil retval = si470x_set_report(radio, radio->usb_buf, LED_REPORT_SIZE); 328b09cd163SJoonyoung Shim 329b09cd163SJoonyoung Shim return (retval < 0) ? -EINVAL : 0; 330b09cd163SJoonyoung Shim } 331b09cd163SJoonyoung Shim 332b09cd163SJoonyoung Shim 333b09cd163SJoonyoung Shim 334b09cd163SJoonyoung Shim /************************************************************************** 335b09cd163SJoonyoung Shim * General Driver Functions - SCRATCH_REPORT 336b09cd163SJoonyoung Shim **************************************************************************/ 337b09cd163SJoonyoung Shim 338b09cd163SJoonyoung Shim /* 339b09cd163SJoonyoung Shim * si470x_get_scratch_versions - gets the scratch page and version infos 340b09cd163SJoonyoung Shim */ 341b09cd163SJoonyoung Shim static int si470x_get_scratch_page_versions(struct si470x_device *radio) 342b09cd163SJoonyoung Shim { 343b09cd163SJoonyoung Shim int retval; 344b09cd163SJoonyoung Shim 345567e2e96SHans Verkuil radio->usb_buf[0] = SCRATCH_REPORT; 346b09cd163SJoonyoung Shim 347567e2e96SHans Verkuil retval = si470x_get_report(radio, radio->usb_buf, SCRATCH_REPORT_SIZE); 348b09cd163SJoonyoung Shim 349b09cd163SJoonyoung Shim if (retval < 0) 350cbfc9080SMauro Carvalho Chehab dev_warn(&radio->intf->dev, "si470x_get_scratch: si470x_get_report returned %d\n", 351cbfc9080SMauro Carvalho Chehab retval); 352b09cd163SJoonyoung Shim else { 353567e2e96SHans Verkuil radio->software_version = radio->usb_buf[1]; 354567e2e96SHans Verkuil radio->hardware_version = radio->usb_buf[2]; 355b09cd163SJoonyoung Shim } 356b09cd163SJoonyoung Shim 357b09cd163SJoonyoung Shim return (retval < 0) ? -EINVAL : 0; 358b09cd163SJoonyoung Shim } 359b09cd163SJoonyoung Shim 360b09cd163SJoonyoung Shim 361b09cd163SJoonyoung Shim 362b09cd163SJoonyoung Shim /************************************************************************** 363b09cd163SJoonyoung Shim * RDS Driver Functions 364b09cd163SJoonyoung Shim **************************************************************************/ 365b09cd163SJoonyoung Shim 366b09cd163SJoonyoung Shim /* 367b09cd163SJoonyoung Shim * si470x_int_in_callback - rds callback and processing function 368b09cd163SJoonyoung Shim * 369b09cd163SJoonyoung Shim * TODO: do we need to use mutex locks in some sections? 370b09cd163SJoonyoung Shim */ 371b09cd163SJoonyoung Shim static void si470x_int_in_callback(struct urb *urb) 372b09cd163SJoonyoung Shim { 373b09cd163SJoonyoung Shim struct si470x_device *radio = urb->context; 374b09cd163SJoonyoung Shim int retval; 375b09cd163SJoonyoung Shim unsigned char regnr; 376b09cd163SJoonyoung Shim unsigned char blocknum; 377b09cd163SJoonyoung Shim unsigned short bler; /* rds block errors */ 378b09cd163SJoonyoung Shim unsigned short rds; 379b09cd163SJoonyoung Shim unsigned char tmpbuf[3]; 380b09cd163SJoonyoung Shim 381b09cd163SJoonyoung Shim if (urb->status) { 382b09cd163SJoonyoung Shim if (urb->status == -ENOENT || 383b09cd163SJoonyoung Shim urb->status == -ECONNRESET || 384b09cd163SJoonyoung Shim urb->status == -ESHUTDOWN) { 385b09cd163SJoonyoung Shim return; 386b09cd163SJoonyoung Shim } else { 387a9d6fd5eSJoonyoung Shim dev_warn(&radio->intf->dev, 388a9d6fd5eSJoonyoung Shim "non-zero urb status (%d)\n", urb->status); 389b09cd163SJoonyoung Shim goto resubmit; /* Maybe we can recover. */ 390b09cd163SJoonyoung Shim } 391b09cd163SJoonyoung Shim } 392b09cd163SJoonyoung Shim 39386ef3f78SHans de Goede /* Sometimes the device returns len 0 packets */ 39486ef3f78SHans de Goede if (urb->actual_length != RDS_REPORT_SIZE) 395b09cd163SJoonyoung Shim goto resubmit; 396b09cd163SJoonyoung Shim 39786ef3f78SHans de Goede radio->registers[STATUSRSSI] = 39886ef3f78SHans de Goede get_unaligned_be16(&radio->int_in_buffer[1]); 39986ef3f78SHans de Goede 40077947111SHans de Goede if (radio->registers[STATUSRSSI] & STATUSRSSI_STC) 40177947111SHans de Goede complete(&radio->completion); 40277947111SHans de Goede 40386ef3f78SHans de Goede if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS)) { 404b09cd163SJoonyoung Shim /* Update RDS registers with URB data */ 40586ef3f78SHans de Goede for (regnr = 1; regnr < RDS_REGISTER_NUM; regnr++) 406b09cd163SJoonyoung Shim radio->registers[STATUSRSSI + regnr] = 407b09cd163SJoonyoung Shim get_unaligned_be16(&radio->int_in_buffer[ 408b09cd163SJoonyoung Shim regnr * RADIO_REGISTER_SIZE + 1]); 409b09cd163SJoonyoung Shim /* get rds blocks */ 410b09cd163SJoonyoung Shim if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) { 411b09cd163SJoonyoung Shim /* No RDS group ready, better luck next time */ 412b09cd163SJoonyoung Shim goto resubmit; 413b09cd163SJoonyoung Shim } 414b09cd163SJoonyoung Shim if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) { 415b09cd163SJoonyoung Shim /* RDS decoder not synchronized */ 416b09cd163SJoonyoung Shim goto resubmit; 417b09cd163SJoonyoung Shim } 418b09cd163SJoonyoung Shim for (blocknum = 0; blocknum < 4; blocknum++) { 419b09cd163SJoonyoung Shim switch (blocknum) { 420b09cd163SJoonyoung Shim default: 421b09cd163SJoonyoung Shim bler = (radio->registers[STATUSRSSI] & 422b09cd163SJoonyoung Shim STATUSRSSI_BLERA) >> 9; 423b09cd163SJoonyoung Shim rds = radio->registers[RDSA]; 424b09cd163SJoonyoung Shim break; 425b09cd163SJoonyoung Shim case 1: 426b09cd163SJoonyoung Shim bler = (radio->registers[READCHAN] & 427b09cd163SJoonyoung Shim READCHAN_BLERB) >> 14; 428b09cd163SJoonyoung Shim rds = radio->registers[RDSB]; 429b09cd163SJoonyoung Shim break; 430b09cd163SJoonyoung Shim case 2: 431b09cd163SJoonyoung Shim bler = (radio->registers[READCHAN] & 432b09cd163SJoonyoung Shim READCHAN_BLERC) >> 12; 433b09cd163SJoonyoung Shim rds = radio->registers[RDSC]; 434b09cd163SJoonyoung Shim break; 435b09cd163SJoonyoung Shim case 3: 436b09cd163SJoonyoung Shim bler = (radio->registers[READCHAN] & 437b09cd163SJoonyoung Shim READCHAN_BLERD) >> 10; 438b09cd163SJoonyoung Shim rds = radio->registers[RDSD]; 439b09cd163SJoonyoung Shim break; 440c2c1b415SPeter Senna Tschudin } 441b09cd163SJoonyoung Shim 442b09cd163SJoonyoung Shim /* Fill the V4L2 RDS buffer */ 443b09cd163SJoonyoung Shim put_unaligned_le16(rds, &tmpbuf); 444b09cd163SJoonyoung Shim tmpbuf[2] = blocknum; /* offset name */ 445b09cd163SJoonyoung Shim tmpbuf[2] |= blocknum << 3; /* received offset */ 446b09cd163SJoonyoung Shim if (bler > max_rds_errors) 447b09cd163SJoonyoung Shim tmpbuf[2] |= 0x80; /* uncorrectable errors */ 448b09cd163SJoonyoung Shim else if (bler > 0) 449b09cd163SJoonyoung Shim tmpbuf[2] |= 0x40; /* corrected error(s) */ 450b09cd163SJoonyoung Shim 451b09cd163SJoonyoung Shim /* copy RDS block to internal buffer */ 452b09cd163SJoonyoung Shim memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); 453b09cd163SJoonyoung Shim radio->wr_index += 3; 454b09cd163SJoonyoung Shim 455b09cd163SJoonyoung Shim /* wrap write pointer */ 456b09cd163SJoonyoung Shim if (radio->wr_index >= radio->buf_size) 457b09cd163SJoonyoung Shim radio->wr_index = 0; 458b09cd163SJoonyoung Shim 459b09cd163SJoonyoung Shim /* check for overflow */ 460b09cd163SJoonyoung Shim if (radio->wr_index == radio->rd_index) { 461b09cd163SJoonyoung Shim /* increment and wrap read pointer */ 462b09cd163SJoonyoung Shim radio->rd_index += 3; 463b09cd163SJoonyoung Shim if (radio->rd_index >= radio->buf_size) 464b09cd163SJoonyoung Shim radio->rd_index = 0; 465b09cd163SJoonyoung Shim } 466b09cd163SJoonyoung Shim } 467b09cd163SJoonyoung Shim if (radio->wr_index != radio->rd_index) 468b09cd163SJoonyoung Shim wake_up_interruptible(&radio->read_queue); 469b09cd163SJoonyoung Shim } 470b09cd163SJoonyoung Shim 471b09cd163SJoonyoung Shim resubmit: 472b09cd163SJoonyoung Shim /* Resubmit if we're still running. */ 473b09cd163SJoonyoung Shim if (radio->int_in_running && radio->usbdev) { 474b09cd163SJoonyoung Shim retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC); 475b09cd163SJoonyoung Shim if (retval) { 476a9d6fd5eSJoonyoung Shim dev_warn(&radio->intf->dev, 477a9d6fd5eSJoonyoung Shim "resubmitting urb failed (%d)", retval); 478b09cd163SJoonyoung Shim radio->int_in_running = 0; 479b09cd163SJoonyoung Shim } 480b09cd163SJoonyoung Shim } 48186ef3f78SHans de Goede radio->status_rssi_auto_update = radio->int_in_running; 482b09cd163SJoonyoung Shim } 483b09cd163SJoonyoung Shim 484b09cd163SJoonyoung Shim 48558757984SMauro Carvalho Chehab static int si470x_fops_open(struct file *file) 486b09cd163SJoonyoung Shim { 4876fd522a6SHans Verkuil return v4l2_fh_open(file); 488b09cd163SJoonyoung Shim } 489b09cd163SJoonyoung Shim 49058757984SMauro Carvalho Chehab static int si470x_fops_release(struct file *file) 491b09cd163SJoonyoung Shim { 4924967d53dSHans Verkuil return v4l2_fh_release(file); 493b09cd163SJoonyoung Shim } 494b09cd163SJoonyoung Shim 4956fd522a6SHans Verkuil static void si470x_usb_release(struct v4l2_device *v4l2_dev) 4964967d53dSHans Verkuil { 4976fd522a6SHans Verkuil struct si470x_device *radio = 4986fd522a6SHans Verkuil container_of(v4l2_dev, struct si470x_device, v4l2_dev); 4994967d53dSHans Verkuil 5004967d53dSHans Verkuil usb_free_urb(radio->int_in_urb); 5014967d53dSHans Verkuil v4l2_ctrl_handler_free(&radio->hdl); 5024967d53dSHans Verkuil v4l2_device_unregister(&radio->v4l2_dev); 5034967d53dSHans Verkuil kfree(radio->int_in_buffer); 5044967d53dSHans Verkuil kfree(radio->buffer); 505567e2e96SHans Verkuil kfree(radio->usb_buf); 5064967d53dSHans Verkuil kfree(radio); 5074967d53dSHans Verkuil } 508b09cd163SJoonyoung Shim 509b09cd163SJoonyoung Shim 510b09cd163SJoonyoung Shim /************************************************************************** 511b09cd163SJoonyoung Shim * Video4Linux Interface 512b09cd163SJoonyoung Shim **************************************************************************/ 513b09cd163SJoonyoung Shim 514b09cd163SJoonyoung Shim /* 515b09cd163SJoonyoung Shim * si470x_vidioc_querycap - query device capabilities 516b09cd163SJoonyoung Shim */ 51758757984SMauro Carvalho Chehab static int si470x_vidioc_querycap(struct file *file, void *priv, 518b09cd163SJoonyoung Shim struct v4l2_capability *capability) 519b09cd163SJoonyoung Shim { 520b09cd163SJoonyoung Shim struct si470x_device *radio = video_drvdata(file); 521b09cd163SJoonyoung Shim 522c0decac1SMauro Carvalho Chehab strscpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); 523c0decac1SMauro Carvalho Chehab strscpy(capability->card, DRIVER_CARD, sizeof(capability->card)); 524b09cd163SJoonyoung Shim usb_make_path(radio->usbdev, capability->bus_info, 525b09cd163SJoonyoung Shim sizeof(capability->bus_info)); 526b09cd163SJoonyoung Shim return 0; 527b09cd163SJoonyoung Shim } 528b09cd163SJoonyoung Shim 529b09cd163SJoonyoung Shim 5306fd522a6SHans Verkuil static int si470x_start_usb(struct si470x_device *radio) 5316fd522a6SHans Verkuil { 5326fd522a6SHans Verkuil int retval; 5336fd522a6SHans Verkuil 5346fd522a6SHans Verkuil /* initialize interrupt urb */ 5356fd522a6SHans Verkuil usb_fill_int_urb(radio->int_in_urb, radio->usbdev, 5366fd522a6SHans Verkuil usb_rcvintpipe(radio->usbdev, 5376fd522a6SHans Verkuil radio->int_in_endpoint->bEndpointAddress), 5386fd522a6SHans Verkuil radio->int_in_buffer, 5396fd522a6SHans Verkuil le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), 5406fd522a6SHans Verkuil si470x_int_in_callback, 5416fd522a6SHans Verkuil radio, 5426fd522a6SHans Verkuil radio->int_in_endpoint->bInterval); 5436fd522a6SHans Verkuil 5446fd522a6SHans Verkuil radio->int_in_running = 1; 5456fd522a6SHans Verkuil mb(); 5466fd522a6SHans Verkuil 5476fd522a6SHans Verkuil retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); 5486fd522a6SHans Verkuil if (retval) { 5496fd522a6SHans Verkuil dev_info(&radio->intf->dev, 5506fd522a6SHans Verkuil "submitting int urb failed (%d)\n", retval); 5516fd522a6SHans Verkuil radio->int_in_running = 0; 5526fd522a6SHans Verkuil } 55386ef3f78SHans de Goede radio->status_rssi_auto_update = radio->int_in_running; 55477947111SHans de Goede 55577947111SHans de Goede /* start radio */ 55677947111SHans de Goede retval = si470x_start(radio); 55777947111SHans de Goede if (retval < 0) 55877947111SHans de Goede return retval; 55977947111SHans de Goede 56077947111SHans de Goede v4l2_ctrl_handler_setup(&radio->hdl); 56177947111SHans de Goede 5626fd522a6SHans Verkuil return retval; 5636fd522a6SHans Verkuil } 564b09cd163SJoonyoung Shim 565b09cd163SJoonyoung Shim /************************************************************************** 566b09cd163SJoonyoung Shim * USB Interface 567b09cd163SJoonyoung Shim **************************************************************************/ 568b09cd163SJoonyoung Shim 569b09cd163SJoonyoung Shim /* 570b09cd163SJoonyoung Shim * si470x_usb_driver_probe - probe for the device 571b09cd163SJoonyoung Shim */ 572b09cd163SJoonyoung Shim static int si470x_usb_driver_probe(struct usb_interface *intf, 573b09cd163SJoonyoung Shim const struct usb_device_id *id) 574b09cd163SJoonyoung Shim { 575b09cd163SJoonyoung Shim struct si470x_device *radio; 576b09cd163SJoonyoung Shim struct usb_host_interface *iface_desc; 577b09cd163SJoonyoung Shim struct usb_endpoint_descriptor *endpoint; 5789aa4d4eaSMarkus Elfring int i, int_end_size, retval; 5799dcb79c2STobias Lorenz unsigned char version_warning = 0; 580b09cd163SJoonyoung Shim 581b09cd163SJoonyoung Shim /* private data allocation and initialization */ 582b09cd163SJoonyoung Shim radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); 583b09cd163SJoonyoung Shim if (!radio) { 584b09cd163SJoonyoung Shim retval = -ENOMEM; 585b09cd163SJoonyoung Shim goto err_initial; 586b09cd163SJoonyoung Shim } 587567e2e96SHans Verkuil radio->usb_buf = kmalloc(MAX_REPORT_SIZE, GFP_KERNEL); 588567e2e96SHans Verkuil if (radio->usb_buf == NULL) { 589567e2e96SHans Verkuil retval = -ENOMEM; 590567e2e96SHans Verkuil goto err_radio; 591567e2e96SHans Verkuil } 592b09cd163SJoonyoung Shim radio->usbdev = interface_to_usbdev(intf); 593b09cd163SJoonyoung Shim radio->intf = intf; 594f140612dSHans de Goede radio->band = 1; /* Default to 76 - 108 MHz */ 595b09cd163SJoonyoung Shim mutex_init(&radio->lock); 59677947111SHans de Goede init_completion(&radio->completion); 597b09cd163SJoonyoung Shim 59858757984SMauro Carvalho Chehab radio->get_register = si470x_get_register; 59958757984SMauro Carvalho Chehab radio->set_register = si470x_set_register; 60058757984SMauro Carvalho Chehab radio->fops_open = si470x_fops_open; 60158757984SMauro Carvalho Chehab radio->fops_release = si470x_fops_release; 60258757984SMauro Carvalho Chehab radio->vidioc_querycap = si470x_vidioc_querycap; 60358757984SMauro Carvalho Chehab 604b09cd163SJoonyoung Shim iface_desc = intf->cur_altsetting; 605b09cd163SJoonyoung Shim 606b09cd163SJoonyoung Shim /* Set up interrupt endpoint information. */ 607b09cd163SJoonyoung Shim for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 608b09cd163SJoonyoung Shim endpoint = &iface_desc->endpoint[i].desc; 609a0ffe4c0SHimangi Saraogi if (usb_endpoint_is_int_in(endpoint)) 610b09cd163SJoonyoung Shim radio->int_in_endpoint = endpoint; 611b09cd163SJoonyoung Shim } 612b09cd163SJoonyoung Shim if (!radio->int_in_endpoint) { 613a9d6fd5eSJoonyoung Shim dev_info(&intf->dev, "could not find interrupt in endpoint\n"); 614b09cd163SJoonyoung Shim retval = -EIO; 615567e2e96SHans Verkuil goto err_usbbuf; 616b09cd163SJoonyoung Shim } 617b09cd163SJoonyoung Shim 618b09cd163SJoonyoung Shim int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize); 619b09cd163SJoonyoung Shim 620b09cd163SJoonyoung Shim radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL); 621b09cd163SJoonyoung Shim if (!radio->int_in_buffer) { 622a9d6fd5eSJoonyoung Shim dev_info(&intf->dev, "could not allocate int_in_buffer"); 623b09cd163SJoonyoung Shim retval = -ENOMEM; 624567e2e96SHans Verkuil goto err_usbbuf; 625b09cd163SJoonyoung Shim } 626b09cd163SJoonyoung Shim 627b09cd163SJoonyoung Shim radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); 628b09cd163SJoonyoung Shim if (!radio->int_in_urb) { 629b09cd163SJoonyoung Shim retval = -ENOMEM; 630b09cd163SJoonyoung Shim goto err_intbuffer; 631b09cd163SJoonyoung Shim } 632b09cd163SJoonyoung Shim 6336fd522a6SHans Verkuil radio->v4l2_dev.release = si470x_usb_release; 6345df2def5SHans Verkuil 6355df2def5SHans Verkuil /* 6365df2def5SHans Verkuil * The si470x SiLabs reference design uses the same USB IDs as 6375df2def5SHans Verkuil * 'Thanko's Raremono' si4734 based receiver. So check here which we 6385df2def5SHans Verkuil * have: attempt to read the device ID from the si470x: the lower 12 6395df2def5SHans Verkuil * bits should be 0x0242 for the si470x. 6405df2def5SHans Verkuil * 6415df2def5SHans Verkuil * We use this check to determine which device we are dealing with. 6425df2def5SHans Verkuil */ 6435df2def5SHans Verkuil if (id->idVendor == 0x10c4 && id->idProduct == 0x818a) { 6445df2def5SHans Verkuil retval = usb_control_msg(radio->usbdev, 6455df2def5SHans Verkuil usb_rcvctrlpipe(radio->usbdev, 0), 6465df2def5SHans Verkuil HID_REQ_GET_REPORT, 6475df2def5SHans Verkuil USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, 6485df2def5SHans Verkuil 1, 2, 6495df2def5SHans Verkuil radio->usb_buf, 3, 500); 6505df2def5SHans Verkuil if (retval != 3 || 6515df2def5SHans Verkuil (get_unaligned_be16(&radio->usb_buf[1]) & 0xfff) != 0x0242) { 6525df2def5SHans Verkuil dev_info(&intf->dev, "this is not a si470x device.\n"); 6535df2def5SHans Verkuil retval = -ENODEV; 6545df2def5SHans Verkuil goto err_urb; 6555df2def5SHans Verkuil } 6565df2def5SHans Verkuil } 6575df2def5SHans Verkuil 6584967d53dSHans Verkuil retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); 6594967d53dSHans Verkuil if (retval < 0) { 6604967d53dSHans Verkuil dev_err(&intf->dev, "couldn't register v4l2_device\n"); 661f54ba7f1SAlexey Khoroshilov goto err_urb; 662b09cd163SJoonyoung Shim } 6634967d53dSHans Verkuil 6644967d53dSHans Verkuil v4l2_ctrl_handler_init(&radio->hdl, 2); 6654967d53dSHans Verkuil v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, 6664967d53dSHans Verkuil V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); 6674967d53dSHans Verkuil v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, 6684967d53dSHans Verkuil V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 15); 6694967d53dSHans Verkuil if (radio->hdl.error) { 6704967d53dSHans Verkuil retval = radio->hdl.error; 6714967d53dSHans Verkuil dev_err(&intf->dev, "couldn't register control\n"); 6724967d53dSHans Verkuil goto err_dev; 6734967d53dSHans Verkuil } 6744967d53dSHans Verkuil radio->videodev = si470x_viddev_template; 6754967d53dSHans Verkuil radio->videodev.ctrl_handler = &radio->hdl; 6764967d53dSHans Verkuil radio->videodev.lock = &radio->lock; 6774967d53dSHans Verkuil radio->videodev.v4l2_dev = &radio->v4l2_dev; 6786fd522a6SHans Verkuil radio->videodev.release = video_device_release_empty; 679e83ce300SHans Verkuil radio->videodev.device_caps = 680e83ce300SHans Verkuil V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_READWRITE | V4L2_CAP_TUNER | 681e83ce300SHans Verkuil V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; 6824967d53dSHans Verkuil video_set_drvdata(&radio->videodev, radio); 683b09cd163SJoonyoung Shim 6849dcb79c2STobias Lorenz /* get device and chip versions */ 685b09cd163SJoonyoung Shim if (si470x_get_all_registers(radio) < 0) { 686b09cd163SJoonyoung Shim retval = -EIO; 6874967d53dSHans Verkuil goto err_ctrl; 688b09cd163SJoonyoung Shim } 689a9d6fd5eSJoonyoung Shim dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", 690dd7a2acfSMauro Carvalho Chehab radio->registers[DEVICEID], radio->registers[SI_CHIPID]); 691dd7a2acfSMauro Carvalho Chehab if ((radio->registers[SI_CHIPID] & SI_CHIPID_FIRMWARE) < RADIO_FW_VERSION) { 6929dcb79c2STobias Lorenz dev_warn(&intf->dev, 693cbfc9080SMauro Carvalho Chehab "This driver is known to work with firmware version %hu,\n", 694cbfc9080SMauro Carvalho Chehab RADIO_FW_VERSION); 6959dcb79c2STobias Lorenz dev_warn(&intf->dev, 6969dcb79c2STobias Lorenz "but the device has firmware version %hu.\n", 697dd7a2acfSMauro Carvalho Chehab radio->registers[SI_CHIPID] & SI_CHIPID_FIRMWARE); 6989dcb79c2STobias Lorenz version_warning = 1; 6999dcb79c2STobias Lorenz } 700b09cd163SJoonyoung Shim 701b09cd163SJoonyoung Shim /* get software and hardware versions */ 702b09cd163SJoonyoung Shim if (si470x_get_scratch_page_versions(radio) < 0) { 703b09cd163SJoonyoung Shim retval = -EIO; 7044967d53dSHans Verkuil goto err_ctrl; 705b09cd163SJoonyoung Shim } 706a9d6fd5eSJoonyoung Shim dev_info(&intf->dev, "software version %d, hardware version %d\n", 707b09cd163SJoonyoung Shim radio->software_version, radio->hardware_version); 7089dcb79c2STobias Lorenz if (radio->hardware_version < RADIO_HW_VERSION) { 7099dcb79c2STobias Lorenz dev_warn(&intf->dev, 710cbfc9080SMauro Carvalho Chehab "This driver is known to work with hardware version %hu,\n", 711cbfc9080SMauro Carvalho Chehab RADIO_HW_VERSION); 7129dcb79c2STobias Lorenz dev_warn(&intf->dev, 7139dcb79c2STobias Lorenz "but the device has hardware version %hu.\n", 7149dcb79c2STobias Lorenz radio->hardware_version); 7159dcb79c2STobias Lorenz version_warning = 1; 7169dcb79c2STobias Lorenz } 7179dcb79c2STobias Lorenz 7189dcb79c2STobias Lorenz /* give out version warning */ 7199dcb79c2STobias Lorenz if (version_warning == 1) { 720a9d6fd5eSJoonyoung Shim dev_warn(&intf->dev, 721a9d6fd5eSJoonyoung Shim "If you have some trouble using this driver,\n"); 722a9d6fd5eSJoonyoung Shim dev_warn(&intf->dev, 723cbfc9080SMauro Carvalho Chehab "please report to V4L ML at linux-media@vger.kernel.org\n"); 724b09cd163SJoonyoung Shim } 725b09cd163SJoonyoung Shim 726b09cd163SJoonyoung Shim /* set led to connect state */ 727b09cd163SJoonyoung Shim si470x_set_led_state(radio, BLINK_GREEN_LED); 728b09cd163SJoonyoung Shim 729b09cd163SJoonyoung Shim /* rds buffer allocation */ 730b09cd163SJoonyoung Shim radio->buf_size = rds_buf * 3; 731b09cd163SJoonyoung Shim radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); 732b09cd163SJoonyoung Shim if (!radio->buffer) { 733b09cd163SJoonyoung Shim retval = -EIO; 7344967d53dSHans Verkuil goto err_ctrl; 735b09cd163SJoonyoung Shim } 736b09cd163SJoonyoung Shim 737b09cd163SJoonyoung Shim /* rds buffer configuration */ 738b09cd163SJoonyoung Shim radio->wr_index = 0; 739b09cd163SJoonyoung Shim radio->rd_index = 0; 740b09cd163SJoonyoung Shim init_waitqueue_head(&radio->read_queue); 7414967d53dSHans Verkuil usb_set_intfdata(intf, radio); 742b09cd163SJoonyoung Shim 7436fd522a6SHans Verkuil /* start radio */ 7446fd522a6SHans Verkuil retval = si470x_start_usb(radio); 7456fd522a6SHans Verkuil if (retval < 0) 7466fd522a6SHans Verkuil goto err_all; 7476fd522a6SHans Verkuil 74877947111SHans de Goede /* set initial frequency */ 74977947111SHans de Goede si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ 75077947111SHans de Goede 751b09cd163SJoonyoung Shim /* register video device */ 7524967d53dSHans Verkuil retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, 753b09cd163SJoonyoung Shim radio_nr); 754b09cd163SJoonyoung Shim if (retval) { 7556fd522a6SHans Verkuil dev_err(&intf->dev, "Could not register video device\n"); 756b09cd163SJoonyoung Shim goto err_all; 757b09cd163SJoonyoung Shim } 758b09cd163SJoonyoung Shim 759b09cd163SJoonyoung Shim return 0; 760b09cd163SJoonyoung Shim err_all: 761b09cd163SJoonyoung Shim kfree(radio->buffer); 7624967d53dSHans Verkuil err_ctrl: 7634967d53dSHans Verkuil v4l2_ctrl_handler_free(&radio->hdl); 7644967d53dSHans Verkuil err_dev: 7654967d53dSHans Verkuil v4l2_device_unregister(&radio->v4l2_dev); 766f54ba7f1SAlexey Khoroshilov err_urb: 767f54ba7f1SAlexey Khoroshilov usb_free_urb(radio->int_in_urb); 768b09cd163SJoonyoung Shim err_intbuffer: 769b09cd163SJoonyoung Shim kfree(radio->int_in_buffer); 770567e2e96SHans Verkuil err_usbbuf: 771567e2e96SHans Verkuil kfree(radio->usb_buf); 772b09cd163SJoonyoung Shim err_radio: 773b09cd163SJoonyoung Shim kfree(radio); 774b09cd163SJoonyoung Shim err_initial: 775b09cd163SJoonyoung Shim return retval; 776b09cd163SJoonyoung Shim } 777b09cd163SJoonyoung Shim 778b09cd163SJoonyoung Shim 779b09cd163SJoonyoung Shim /* 780b09cd163SJoonyoung Shim * si470x_usb_driver_suspend - suspend the device 781b09cd163SJoonyoung Shim */ 782b09cd163SJoonyoung Shim static int si470x_usb_driver_suspend(struct usb_interface *intf, 783b09cd163SJoonyoung Shim pm_message_t message) 784b09cd163SJoonyoung Shim { 7856fd522a6SHans Verkuil struct si470x_device *radio = usb_get_intfdata(intf); 7866fd522a6SHans Verkuil 787a9d6fd5eSJoonyoung Shim dev_info(&intf->dev, "suspending now...\n"); 788b09cd163SJoonyoung Shim 7896fd522a6SHans Verkuil /* shutdown interrupt handler */ 7906fd522a6SHans Verkuil if (radio->int_in_running) { 7916fd522a6SHans Verkuil radio->int_in_running = 0; 7926fd522a6SHans Verkuil if (radio->int_in_urb) 7936fd522a6SHans Verkuil usb_kill_urb(radio->int_in_urb); 7946fd522a6SHans Verkuil } 7956fd522a6SHans Verkuil 7966fd522a6SHans Verkuil /* cancel read processes */ 7976fd522a6SHans Verkuil wake_up_interruptible(&radio->read_queue); 7986fd522a6SHans Verkuil 7996fd522a6SHans Verkuil /* stop radio */ 8006fd522a6SHans Verkuil si470x_stop(radio); 801b09cd163SJoonyoung Shim return 0; 802b09cd163SJoonyoung Shim } 803b09cd163SJoonyoung Shim 804b09cd163SJoonyoung Shim 805b09cd163SJoonyoung Shim /* 806b09cd163SJoonyoung Shim * si470x_usb_driver_resume - resume the device 807b09cd163SJoonyoung Shim */ 808b09cd163SJoonyoung Shim static int si470x_usb_driver_resume(struct usb_interface *intf) 809b09cd163SJoonyoung Shim { 8106fd522a6SHans Verkuil struct si470x_device *radio = usb_get_intfdata(intf); 811c1af23c4SHans de Goede int ret; 8126fd522a6SHans Verkuil 813a9d6fd5eSJoonyoung Shim dev_info(&intf->dev, "resuming now...\n"); 814b09cd163SJoonyoung Shim 8156fd522a6SHans Verkuil /* start radio */ 816c1af23c4SHans de Goede ret = si470x_start_usb(radio); 817c1af23c4SHans de Goede if (ret == 0) 818c1af23c4SHans de Goede v4l2_ctrl_handler_setup(&radio->hdl); 819c1af23c4SHans de Goede 820c1af23c4SHans de Goede return ret; 821b09cd163SJoonyoung Shim } 822b09cd163SJoonyoung Shim 823b09cd163SJoonyoung Shim 824b09cd163SJoonyoung Shim /* 825b09cd163SJoonyoung Shim * si470x_usb_driver_disconnect - disconnect the device 826b09cd163SJoonyoung Shim */ 827b09cd163SJoonyoung Shim static void si470x_usb_driver_disconnect(struct usb_interface *intf) 828b09cd163SJoonyoung Shim { 829b09cd163SJoonyoung Shim struct si470x_device *radio = usb_get_intfdata(intf); 830b09cd163SJoonyoung Shim 831cf9b475dSMauro Carvalho Chehab mutex_lock(&radio->lock); 8324967d53dSHans Verkuil v4l2_device_disconnect(&radio->v4l2_dev); 8334967d53dSHans Verkuil video_unregister_device(&radio->videodev); 834b09cd163SJoonyoung Shim usb_set_intfdata(intf, NULL); 835cf9b475dSMauro Carvalho Chehab mutex_unlock(&radio->lock); 8366fd522a6SHans Verkuil v4l2_device_put(&radio->v4l2_dev); 837b09cd163SJoonyoung Shim } 838b09cd163SJoonyoung Shim 839b09cd163SJoonyoung Shim 840b09cd163SJoonyoung Shim /* 841b09cd163SJoonyoung Shim * si470x_usb_driver - usb driver interface 8426fd522a6SHans Verkuil * 8436fd522a6SHans Verkuil * A note on suspend/resume: this driver had only empty suspend/resume 8446fd522a6SHans Verkuil * functions, and when I tried to test suspend/resume it always disconnected 8456fd522a6SHans Verkuil * instead of resuming (using my ADS InstantFM stick). So I've decided to 8466fd522a6SHans Verkuil * remove these callbacks until someone else with better hardware can 8476fd522a6SHans Verkuil * implement and test this. 848b09cd163SJoonyoung Shim */ 849b09cd163SJoonyoung Shim static struct usb_driver si470x_usb_driver = { 850b09cd163SJoonyoung Shim .name = DRIVER_NAME, 851b09cd163SJoonyoung Shim .probe = si470x_usb_driver_probe, 852b09cd163SJoonyoung Shim .disconnect = si470x_usb_driver_disconnect, 853b09cd163SJoonyoung Shim .suspend = si470x_usb_driver_suspend, 854b09cd163SJoonyoung Shim .resume = si470x_usb_driver_resume, 8556fd522a6SHans Verkuil .reset_resume = si470x_usb_driver_resume, 856b09cd163SJoonyoung Shim .id_table = si470x_usb_driver_id_table, 857b09cd163SJoonyoung Shim }; 858b09cd163SJoonyoung Shim 859ecb3b2b3SGreg Kroah-Hartman module_usb_driver(si470x_usb_driver); 860b09cd163SJoonyoung Shim 861b09cd163SJoonyoung Shim MODULE_LICENSE("GPL"); 862b09cd163SJoonyoung Shim MODULE_AUTHOR(DRIVER_AUTHOR); 863b09cd163SJoonyoung Shim MODULE_DESCRIPTION(DRIVER_DESC); 864b09cd163SJoonyoung Shim MODULE_VERSION(DRIVER_VERSION); 865