18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Linux V4L2 radio driver for the Griffin radioSHARK USB radio receiver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Note the radioSHARK offers the audio through a regular USB audio device, 58c2ecf20Sopenharmony_ci * this driver only handles the tuning. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * The info necessary to drive the shark was taken from the small userspace 88c2ecf20Sopenharmony_ci * shark.c program by Michael Rolig, which he kindly placed in the Public 98c2ecf20Sopenharmony_ci * Domain. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 148c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 158c2ecf20Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or 168c2ecf20Sopenharmony_ci * (at your option) any later version. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 198c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 208c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 218c2ecf20Sopenharmony_ci * GNU General Public License for more details. 228c2ecf20Sopenharmony_ci*/ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/kernel.h> 268c2ecf20Sopenharmony_ci#include <linux/leds.h> 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <linux/usb.h> 308c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 318c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 328c2ecf20Sopenharmony_ci#include <media/drv-intf/tea575x.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#if defined(CONFIG_LEDS_CLASS) || \ 358c2ecf20Sopenharmony_ci (defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK_MODULE)) 368c2ecf20Sopenharmony_ci#define SHARK_USE_LEDS 1 378c2ecf20Sopenharmony_ci#endif 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * Version Information 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Griffin radioSHARK, USB radio receiver driver"); 448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define SHARK_IN_EP 0x83 478c2ecf20Sopenharmony_ci#define SHARK_OUT_EP 0x05 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */ 508c2ecf20Sopenharmony_ci#define TEA575X_BIT_BAND_MASK (3<<20) 518c2ecf20Sopenharmony_ci#define TEA575X_BIT_BAND_FM (0<<20) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define TB_LEN 6 548c2ecf20Sopenharmony_ci#define DRV_NAME "radioshark" 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* Note BLUE_IS_PULSE comes after NO_LEDS as it is a status bit, not a LED */ 598c2ecf20Sopenharmony_cienum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS, BLUE_IS_PULSE }; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct shark_device { 628c2ecf20Sopenharmony_ci struct usb_device *usbdev; 638c2ecf20Sopenharmony_ci struct v4l2_device v4l2_dev; 648c2ecf20Sopenharmony_ci struct snd_tea575x tea; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#ifdef SHARK_USE_LEDS 678c2ecf20Sopenharmony_ci struct work_struct led_work; 688c2ecf20Sopenharmony_ci struct led_classdev leds[NO_LEDS]; 698c2ecf20Sopenharmony_ci char led_names[NO_LEDS][32]; 708c2ecf20Sopenharmony_ci atomic_t brightness[NO_LEDS]; 718c2ecf20Sopenharmony_ci unsigned long brightness_new; 728c2ecf20Sopenharmony_ci#endif 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci u8 *transfer_buffer; 758c2ecf20Sopenharmony_ci u32 last_val; 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic atomic_t shark_instance = ATOMIC_INIT(0); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void shark_write_val(struct snd_tea575x *tea, u32 val) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct shark_device *shark = tea->private_data; 838c2ecf20Sopenharmony_ci int i, res, actual_len; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* Avoid unnecessary (slow) USB transfers */ 868c2ecf20Sopenharmony_ci if (shark->last_val == val) 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci memset(shark->transfer_buffer, 0, TB_LEN); 908c2ecf20Sopenharmony_ci shark->transfer_buffer[0] = 0xc0; /* Write shift register command */ 918c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 928c2ecf20Sopenharmony_ci shark->transfer_buffer[i] |= (val >> (24 - i * 8)) & 0xff; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci res = usb_interrupt_msg(shark->usbdev, 958c2ecf20Sopenharmony_ci usb_sndintpipe(shark->usbdev, SHARK_OUT_EP), 968c2ecf20Sopenharmony_ci shark->transfer_buffer, TB_LEN, 978c2ecf20Sopenharmony_ci &actual_len, 1000); 988c2ecf20Sopenharmony_ci if (res >= 0) 998c2ecf20Sopenharmony_ci shark->last_val = val; 1008c2ecf20Sopenharmony_ci else 1018c2ecf20Sopenharmony_ci v4l2_err(&shark->v4l2_dev, "set-freq error: %d\n", res); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic u32 shark_read_val(struct snd_tea575x *tea) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct shark_device *shark = tea->private_data; 1078c2ecf20Sopenharmony_ci int i, res, actual_len; 1088c2ecf20Sopenharmony_ci u32 val = 0; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci memset(shark->transfer_buffer, 0, TB_LEN); 1118c2ecf20Sopenharmony_ci shark->transfer_buffer[0] = 0x80; 1128c2ecf20Sopenharmony_ci res = usb_interrupt_msg(shark->usbdev, 1138c2ecf20Sopenharmony_ci usb_sndintpipe(shark->usbdev, SHARK_OUT_EP), 1148c2ecf20Sopenharmony_ci shark->transfer_buffer, TB_LEN, 1158c2ecf20Sopenharmony_ci &actual_len, 1000); 1168c2ecf20Sopenharmony_ci if (res < 0) { 1178c2ecf20Sopenharmony_ci v4l2_err(&shark->v4l2_dev, "request-status error: %d\n", res); 1188c2ecf20Sopenharmony_ci return shark->last_val; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci res = usb_interrupt_msg(shark->usbdev, 1228c2ecf20Sopenharmony_ci usb_rcvintpipe(shark->usbdev, SHARK_IN_EP), 1238c2ecf20Sopenharmony_ci shark->transfer_buffer, TB_LEN, 1248c2ecf20Sopenharmony_ci &actual_len, 1000); 1258c2ecf20Sopenharmony_ci if (res < 0) { 1268c2ecf20Sopenharmony_ci v4l2_err(&shark->v4l2_dev, "get-status error: %d\n", res); 1278c2ecf20Sopenharmony_ci return shark->last_val; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 1318c2ecf20Sopenharmony_ci val |= shark->transfer_buffer[i] << (24 - i * 8); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci shark->last_val = val; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* 1368c2ecf20Sopenharmony_ci * The shark does not allow actually reading the stereo / mono pin :( 1378c2ecf20Sopenharmony_ci * So assume that when we're tuned to an FM station and mono has not 1388c2ecf20Sopenharmony_ci * been requested, that we're receiving stereo. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci if (((val & TEA575X_BIT_BAND_MASK) == TEA575X_BIT_BAND_FM) && 1418c2ecf20Sopenharmony_ci !(val & TEA575X_BIT_MONO)) 1428c2ecf20Sopenharmony_ci shark->tea.stereo = true; 1438c2ecf20Sopenharmony_ci else 1448c2ecf20Sopenharmony_ci shark->tea.stereo = false; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return val; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic const struct snd_tea575x_ops shark_tea_ops = { 1508c2ecf20Sopenharmony_ci .write_val = shark_write_val, 1518c2ecf20Sopenharmony_ci .read_val = shark_read_val, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#ifdef SHARK_USE_LEDS 1558c2ecf20Sopenharmony_cistatic void shark_led_work(struct work_struct *work) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct shark_device *shark = 1588c2ecf20Sopenharmony_ci container_of(work, struct shark_device, led_work); 1598c2ecf20Sopenharmony_ci int i, res, brightness, actual_len; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 1628c2ecf20Sopenharmony_ci if (!test_and_clear_bit(i, &shark->brightness_new)) 1638c2ecf20Sopenharmony_ci continue; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci brightness = atomic_read(&shark->brightness[i]); 1668c2ecf20Sopenharmony_ci memset(shark->transfer_buffer, 0, TB_LEN); 1678c2ecf20Sopenharmony_ci if (i != RED_LED) { 1688c2ecf20Sopenharmony_ci shark->transfer_buffer[0] = 0xA0 + i; 1698c2ecf20Sopenharmony_ci shark->transfer_buffer[1] = brightness; 1708c2ecf20Sopenharmony_ci } else 1718c2ecf20Sopenharmony_ci shark->transfer_buffer[0] = brightness ? 0xA9 : 0xA8; 1728c2ecf20Sopenharmony_ci res = usb_interrupt_msg(shark->usbdev, 1738c2ecf20Sopenharmony_ci usb_sndintpipe(shark->usbdev, 0x05), 1748c2ecf20Sopenharmony_ci shark->transfer_buffer, TB_LEN, 1758c2ecf20Sopenharmony_ci &actual_len, 1000); 1768c2ecf20Sopenharmony_ci if (res < 0) 1778c2ecf20Sopenharmony_ci v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n", 1788c2ecf20Sopenharmony_ci shark->led_names[i], res); 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void shark_led_set_blue(struct led_classdev *led_cdev, 1838c2ecf20Sopenharmony_ci enum led_brightness value) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct shark_device *shark = 1868c2ecf20Sopenharmony_ci container_of(led_cdev, struct shark_device, leds[BLUE_LED]); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci atomic_set(&shark->brightness[BLUE_LED], value); 1898c2ecf20Sopenharmony_ci set_bit(BLUE_LED, &shark->brightness_new); 1908c2ecf20Sopenharmony_ci clear_bit(BLUE_IS_PULSE, &shark->brightness_new); 1918c2ecf20Sopenharmony_ci schedule_work(&shark->led_work); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void shark_led_set_blue_pulse(struct led_classdev *led_cdev, 1958c2ecf20Sopenharmony_ci enum led_brightness value) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct shark_device *shark = container_of(led_cdev, 1988c2ecf20Sopenharmony_ci struct shark_device, leds[BLUE_PULSE_LED]); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci atomic_set(&shark->brightness[BLUE_PULSE_LED], 256 - value); 2018c2ecf20Sopenharmony_ci set_bit(BLUE_PULSE_LED, &shark->brightness_new); 2028c2ecf20Sopenharmony_ci set_bit(BLUE_IS_PULSE, &shark->brightness_new); 2038c2ecf20Sopenharmony_ci schedule_work(&shark->led_work); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void shark_led_set_red(struct led_classdev *led_cdev, 2078c2ecf20Sopenharmony_ci enum led_brightness value) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct shark_device *shark = 2108c2ecf20Sopenharmony_ci container_of(led_cdev, struct shark_device, leds[RED_LED]); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci atomic_set(&shark->brightness[RED_LED], value); 2138c2ecf20Sopenharmony_ci set_bit(RED_LED, &shark->brightness_new); 2148c2ecf20Sopenharmony_ci schedule_work(&shark->led_work); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic const struct led_classdev shark_led_templates[NO_LEDS] = { 2188c2ecf20Sopenharmony_ci [BLUE_LED] = { 2198c2ecf20Sopenharmony_ci .name = "%s:blue:", 2208c2ecf20Sopenharmony_ci .brightness = LED_OFF, 2218c2ecf20Sopenharmony_ci .max_brightness = 127, 2228c2ecf20Sopenharmony_ci .brightness_set = shark_led_set_blue, 2238c2ecf20Sopenharmony_ci }, 2248c2ecf20Sopenharmony_ci [BLUE_PULSE_LED] = { 2258c2ecf20Sopenharmony_ci .name = "%s:blue-pulse:", 2268c2ecf20Sopenharmony_ci .brightness = LED_OFF, 2278c2ecf20Sopenharmony_ci .max_brightness = 255, 2288c2ecf20Sopenharmony_ci .brightness_set = shark_led_set_blue_pulse, 2298c2ecf20Sopenharmony_ci }, 2308c2ecf20Sopenharmony_ci [RED_LED] = { 2318c2ecf20Sopenharmony_ci .name = "%s:red:", 2328c2ecf20Sopenharmony_ci .brightness = LED_OFF, 2338c2ecf20Sopenharmony_ci .max_brightness = 1, 2348c2ecf20Sopenharmony_ci .brightness_set = shark_led_set_red, 2358c2ecf20Sopenharmony_ci }, 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int shark_register_leds(struct shark_device *shark, struct device *dev) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int i, retval; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci atomic_set(&shark->brightness[BLUE_LED], 127); 2438c2ecf20Sopenharmony_ci INIT_WORK(&shark->led_work, shark_led_work); 2448c2ecf20Sopenharmony_ci for (i = 0; i < NO_LEDS; i++) { 2458c2ecf20Sopenharmony_ci shark->leds[i] = shark_led_templates[i]; 2468c2ecf20Sopenharmony_ci snprintf(shark->led_names[i], sizeof(shark->led_names[0]), 2478c2ecf20Sopenharmony_ci shark->leds[i].name, shark->v4l2_dev.name); 2488c2ecf20Sopenharmony_ci shark->leds[i].name = shark->led_names[i]; 2498c2ecf20Sopenharmony_ci retval = led_classdev_register(dev, &shark->leds[i]); 2508c2ecf20Sopenharmony_ci if (retval) { 2518c2ecf20Sopenharmony_ci v4l2_err(&shark->v4l2_dev, 2528c2ecf20Sopenharmony_ci "couldn't register led: %s\n", 2538c2ecf20Sopenharmony_ci shark->led_names[i]); 2548c2ecf20Sopenharmony_ci return retval; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void shark_unregister_leds(struct shark_device *shark) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci int i; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci for (i = 0; i < NO_LEDS; i++) 2658c2ecf20Sopenharmony_ci led_classdev_unregister(&shark->leds[i]); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci cancel_work_sync(&shark->led_work); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic inline void shark_resume_leds(struct shark_device *shark) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci if (test_bit(BLUE_IS_PULSE, &shark->brightness_new)) 2738c2ecf20Sopenharmony_ci set_bit(BLUE_PULSE_LED, &shark->brightness_new); 2748c2ecf20Sopenharmony_ci else 2758c2ecf20Sopenharmony_ci set_bit(BLUE_LED, &shark->brightness_new); 2768c2ecf20Sopenharmony_ci set_bit(RED_LED, &shark->brightness_new); 2778c2ecf20Sopenharmony_ci schedule_work(&shark->led_work); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci#else 2808c2ecf20Sopenharmony_cistatic int shark_register_leds(struct shark_device *shark, struct device *dev) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci v4l2_warn(&shark->v4l2_dev, 2838c2ecf20Sopenharmony_ci "CONFIG_LEDS_CLASS not enabled, LED support disabled\n"); 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_cistatic inline void shark_unregister_leds(struct shark_device *shark) { } 2878c2ecf20Sopenharmony_cistatic inline void shark_resume_leds(struct shark_device *shark) { } 2888c2ecf20Sopenharmony_ci#endif 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic void usb_shark_disconnect(struct usb_interface *intf) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); 2938c2ecf20Sopenharmony_ci struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci mutex_lock(&shark->tea.mutex); 2968c2ecf20Sopenharmony_ci v4l2_device_disconnect(&shark->v4l2_dev); 2978c2ecf20Sopenharmony_ci snd_tea575x_exit(&shark->tea); 2988c2ecf20Sopenharmony_ci mutex_unlock(&shark->tea.mutex); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci shark_unregister_leds(shark); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci v4l2_device_put(&shark->v4l2_dev); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic void usb_shark_release(struct v4l2_device *v4l2_dev) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci v4l2_device_unregister(&shark->v4l2_dev); 3108c2ecf20Sopenharmony_ci kfree(shark->transfer_buffer); 3118c2ecf20Sopenharmony_ci kfree(shark); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int usb_shark_probe(struct usb_interface *intf, 3158c2ecf20Sopenharmony_ci const struct usb_device_id *id) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct shark_device *shark; 3188c2ecf20Sopenharmony_ci int retval = -ENOMEM; 3198c2ecf20Sopenharmony_ci static const u8 ep_addresses[] = { 3208c2ecf20Sopenharmony_ci SHARK_IN_EP | USB_DIR_IN, 3218c2ecf20Sopenharmony_ci SHARK_OUT_EP | USB_DIR_OUT, 3228c2ecf20Sopenharmony_ci 0}; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* Are the expected endpoints present? */ 3258c2ecf20Sopenharmony_ci if (!usb_check_int_endpoints(intf, ep_addresses)) { 3268c2ecf20Sopenharmony_ci dev_err(&intf->dev, "Invalid radioSHARK device\n"); 3278c2ecf20Sopenharmony_ci return -EINVAL; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL); 3318c2ecf20Sopenharmony_ci if (!shark) 3328c2ecf20Sopenharmony_ci return retval; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL); 3358c2ecf20Sopenharmony_ci if (!shark->transfer_buffer) 3368c2ecf20Sopenharmony_ci goto err_alloc_buffer; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci retval = shark_register_leds(shark, &intf->dev); 3418c2ecf20Sopenharmony_ci if (retval) 3428c2ecf20Sopenharmony_ci goto err_reg_leds; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci shark->v4l2_dev.release = usb_shark_release; 3458c2ecf20Sopenharmony_ci retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev); 3468c2ecf20Sopenharmony_ci if (retval) { 3478c2ecf20Sopenharmony_ci v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n"); 3488c2ecf20Sopenharmony_ci goto err_reg_dev; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci shark->usbdev = interface_to_usbdev(intf); 3528c2ecf20Sopenharmony_ci shark->tea.v4l2_dev = &shark->v4l2_dev; 3538c2ecf20Sopenharmony_ci shark->tea.private_data = shark; 3548c2ecf20Sopenharmony_ci shark->tea.radio_nr = -1; 3558c2ecf20Sopenharmony_ci shark->tea.ops = &shark_tea_ops; 3568c2ecf20Sopenharmony_ci shark->tea.cannot_mute = true; 3578c2ecf20Sopenharmony_ci shark->tea.has_am = true; 3588c2ecf20Sopenharmony_ci strscpy(shark->tea.card, "Griffin radioSHARK", 3598c2ecf20Sopenharmony_ci sizeof(shark->tea.card)); 3608c2ecf20Sopenharmony_ci usb_make_path(shark->usbdev, shark->tea.bus_info, 3618c2ecf20Sopenharmony_ci sizeof(shark->tea.bus_info)); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci retval = snd_tea575x_init(&shark->tea, THIS_MODULE); 3648c2ecf20Sopenharmony_ci if (retval) { 3658c2ecf20Sopenharmony_ci v4l2_err(&shark->v4l2_dev, "couldn't init tea5757\n"); 3668c2ecf20Sopenharmony_ci goto err_init_tea; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cierr_init_tea: 3728c2ecf20Sopenharmony_ci v4l2_device_unregister(&shark->v4l2_dev); 3738c2ecf20Sopenharmony_cierr_reg_dev: 3748c2ecf20Sopenharmony_ci shark_unregister_leds(shark); 3758c2ecf20Sopenharmony_cierr_reg_leds: 3768c2ecf20Sopenharmony_ci kfree(shark->transfer_buffer); 3778c2ecf20Sopenharmony_cierr_alloc_buffer: 3788c2ecf20Sopenharmony_ci kfree(shark); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return retval; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3848c2ecf20Sopenharmony_cistatic int usb_shark_suspend(struct usb_interface *intf, pm_message_t message) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic int usb_shark_resume(struct usb_interface *intf) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); 3928c2ecf20Sopenharmony_ci struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci mutex_lock(&shark->tea.mutex); 3958c2ecf20Sopenharmony_ci snd_tea575x_set_freq(&shark->tea); 3968c2ecf20Sopenharmony_ci mutex_unlock(&shark->tea.mutex); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci shark_resume_leds(shark); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci return 0; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci#endif 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */ 4058c2ecf20Sopenharmony_cistatic const struct usb_device_id usb_shark_device_table[] = { 4068c2ecf20Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | 4078c2ecf20Sopenharmony_ci USB_DEVICE_ID_MATCH_INT_CLASS, 4088c2ecf20Sopenharmony_ci .idVendor = 0x077d, 4098c2ecf20Sopenharmony_ci .idProduct = 0x627a, 4108c2ecf20Sopenharmony_ci .bcdDevice_lo = 0x0001, 4118c2ecf20Sopenharmony_ci .bcdDevice_hi = 0x0001, 4128c2ecf20Sopenharmony_ci .bInterfaceClass = 3, 4138c2ecf20Sopenharmony_ci }, 4148c2ecf20Sopenharmony_ci { } 4158c2ecf20Sopenharmony_ci}; 4168c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usb_shark_device_table); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic struct usb_driver usb_shark_driver = { 4198c2ecf20Sopenharmony_ci .name = DRV_NAME, 4208c2ecf20Sopenharmony_ci .probe = usb_shark_probe, 4218c2ecf20Sopenharmony_ci .disconnect = usb_shark_disconnect, 4228c2ecf20Sopenharmony_ci .id_table = usb_shark_device_table, 4238c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4248c2ecf20Sopenharmony_ci .suspend = usb_shark_suspend, 4258c2ecf20Sopenharmony_ci .resume = usb_shark_resume, 4268c2ecf20Sopenharmony_ci .reset_resume = usb_shark_resume, 4278c2ecf20Sopenharmony_ci#endif 4288c2ecf20Sopenharmony_ci}; 4298c2ecf20Sopenharmony_cimodule_usb_driver(usb_shark_driver); 430