13088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil/* 23088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil * radio-aztech.c - Aztech radio card driver 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 43088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@xs4all.nl> 5a4366af40d7d2021499b84b8311336c944a9a71cMauro Carvalho Chehab * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 64286c6f65ec01efa8f5108cadea402ecf3b12279Mauro Carvalho Chehab * Adapted to support the Video for Linux API by 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Quay Ly 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Donald Song 114286c6f65ec01efa8f5108cadea402ecf3b12279Mauro Carvalho Chehab * Jason Lewis (jlewis@twilight.vtc.vsc.edu) 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * William McGrath (wmcgrath@twilight.vtc.vsc.edu) 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 153088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/ 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> /* Modules */ 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> /* Initdata */ 20fb911ee849756fc6c609dddded92d9207ff3fb29Peter Osterlund#include <linux/ioport.h> /* request_region */ 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h> /* udelay */ 22a4366af40d7d2021499b84b8311336c944a9a71cMauro Carvalho Chehab#include <linux/videodev2.h> /* kernel radio structs */ 23e697e12ecede8ee4f5beb11ed4981d7254be0125Hans Verkuil#include <linux/io.h> /* outb, outb_p */ 249f1dfccf6607822f556698f0940ead57e6e42d5fHans Verkuil#include <linux/slab.h> 25e697e12ecede8ee4f5beb11ed4981d7254be0125Hans Verkuil#include <media/v4l2-device.h> 2635ea11ff84719b1bfab2909903a9640a86552fd1Hans Verkuil#include <media/v4l2-ioctl.h> 273088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil#include <media/v4l2-ctrls.h> 283088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil#include "radio-isa.h" 29eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary#include "lm7000.h" 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 31e697e12ecede8ee4f5beb11ed4981d7254be0125Hans VerkuilMODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); 32e697e12ecede8ee4f5beb11ed4981d7254be0125Hans VerkuilMODULE_DESCRIPTION("A driver for the Aztech radio card."); 33e697e12ecede8ee4f5beb11ed4981d7254be0125Hans VerkuilMODULE_LICENSE("GPL"); 343088fba877ee8bf284b12a73332b813b5478a64dHans VerkuilMODULE_VERSION("1.0.0"); 35a4366af40d7d2021499b84b8311336c944a9a71cMauro Carvalho Chehab 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CONFIG_RADIO_AZTECH_PORT 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_RADIO_AZTECH_PORT -1 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 413088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil#define AZTECH_MAX 2 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 433088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilstatic int io[AZTECH_MAX] = { [0] = CONFIG_RADIO_AZTECH_PORT, 443088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil [1 ... (AZTECH_MAX - 1)] = -1 }; 453088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilstatic int radio_nr[AZTECH_MAX] = { [0 ... (AZTECH_MAX - 1)] = -1 }; 463088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilstatic const int radio_wait_time = 1000; 47e697e12ecede8ee4f5beb11ed4981d7254be0125Hans Verkuil 483088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilmodule_param_array(io, int, NULL, 0444); 493088fba877ee8bf284b12a73332b813b5478a64dHans VerkuilMODULE_PARM_DESC(io, "I/O addresses of the Aztech card (0x350 or 0x358)"); 503088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilmodule_param_array(radio_nr, int, NULL, 0444); 513088fba877ee8bf284b12a73332b813b5478a64dHans VerkuilMODULE_PARM_DESC(radio_nr, "Radio device numbers"); 523088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil 533088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilstruct aztech { 543088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil struct radio_isa_card isa; 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int curvol; 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 58eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary/* bit definitions for register read */ 59eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary#define AZTECH_BIT_NOT_TUNED (1 << 0) 60eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary#define AZTECH_BIT_MONO (1 << 1) 61eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary/* bit definitions for register write */ 62eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary#define AZTECH_BIT_TUN_CE (1 << 1) 63eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary#define AZTECH_BIT_TUN_CLK (1 << 6) 64eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary#define AZTECH_BIT_TUN_DATA (1 << 7) 65eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary/* bits 0 and 2 are volume control, bits 3..5 are not connected */ 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 67eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zarystatic void aztech_set_pins(void *handle, u8 pins) 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 69eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary struct radio_isa_card *isa = handle; 70eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary struct aztech *az = container_of(isa, struct aztech, isa); 71eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary u8 bits = az->curvol; 72eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary 73eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary if (pins & LM7000_DATA) 74eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary bits |= AZTECH_BIT_TUN_DATA; 75eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary if (pins & LM7000_CLK) 76eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary bits |= AZTECH_BIT_TUN_CLK; 77eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary if (pins & LM7000_CE) 78eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary bits |= AZTECH_BIT_TUN_CE; 79eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary 80eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary outb_p(bits, az->isa.io); 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 833088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilstatic struct radio_isa_card *aztech_alloc(void) 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 853088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil struct aztech *az = kzalloc(sizeof(*az), GFP_KERNEL); 86e697e12ecede8ee4f5beb11ed4981d7254be0125Hans Verkuil 873088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil return az ? &az->isa : NULL; 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 903088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilstatic int aztech_s_frequency(struct radio_isa_card *isa, u32 freq) 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 92eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary lm7000_set_freq(freq, isa, aztech_set_pins); 93676b0ac7aa409d326d92ca46c65bba20bebb3c1cMauro Carvalho Chehab 94a0c05ab9762560cf12733181d19b6529bb7231d2Mauro Carvalho Chehab return 0; 95a0c05ab9762560cf12733181d19b6529bb7231d2Mauro Carvalho Chehab} 96a0c05ab9762560cf12733181d19b6529bb7231d2Mauro Carvalho Chehab 973088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilstatic u32 aztech_g_rxsubchans(struct radio_isa_card *isa) 98e697e12ecede8ee4f5beb11ed4981d7254be0125Hans Verkuil{ 991c9f11ed21816290a7cea36fc2dc3b16d5590340Ondrej Zary if (inb(isa->io) & AZTECH_BIT_MONO) 1003088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil return V4L2_TUNER_SUB_MONO; 1013088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil return V4L2_TUNER_SUB_STEREO; 102e697e12ecede8ee4f5beb11ed4981d7254be0125Hans Verkuil} 103e697e12ecede8ee4f5beb11ed4981d7254be0125Hans Verkuil 1041c9f11ed21816290a7cea36fc2dc3b16d5590340Ondrej Zarystatic u32 aztech_g_signal(struct radio_isa_card *isa) 10599218fe478e2ca6d5ee660a655690ab6496e6ab5Mauro Carvalho Chehab{ 1061c9f11ed21816290a7cea36fc2dc3b16d5590340Ondrej Zary return (inb(isa->io) & AZTECH_BIT_NOT_TUNED) ? 0 : 0xffff; 10799218fe478e2ca6d5ee660a655690ab6496e6ab5Mauro Carvalho Chehab} 10899218fe478e2ca6d5ee660a655690ab6496e6ab5Mauro Carvalho Chehab 1093088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilstatic int aztech_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) 11099218fe478e2ca6d5ee660a655690ab6496e6ab5Mauro Carvalho Chehab{ 1113088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil struct aztech *az = container_of(isa, struct aztech, isa); 11299218fe478e2ca6d5ee660a655690ab6496e6ab5Mauro Carvalho Chehab 1133088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil if (mute) 1143088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil vol = 0; 1153088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil az->curvol = (vol & 1) + ((vol & 2) << 1); 1163088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil outb(az->curvol, isa->io); 11799218fe478e2ca6d5ee660a655690ab6496e6ab5Mauro Carvalho Chehab return 0; 11899218fe478e2ca6d5ee660a655690ab6496e6ab5Mauro Carvalho Chehab} 11999218fe478e2ca6d5ee660a655690ab6496e6ab5Mauro Carvalho Chehab 1203088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilstatic const struct radio_isa_ops aztech_ops = { 1213088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .alloc = aztech_alloc, 1223088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .s_mute_volume = aztech_s_mute_volume, 1233088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .s_frequency = aztech_s_frequency, 1243088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .g_rxsubchans = aztech_g_rxsubchans, 1251c9f11ed21816290a7cea36fc2dc3b16d5590340Ondrej Zary .g_signal = aztech_g_signal, 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1283088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilstatic const int aztech_ioports[] = { 0x350, 0x358 }; 1293088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil 1303088fba877ee8bf284b12a73332b813b5478a64dHans Verkuilstatic struct radio_isa_driver aztech_driver = { 1313088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .driver = { 1323088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .match = radio_isa_match, 1333088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .probe = radio_isa_probe, 1343088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .remove = radio_isa_remove, 1353088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .driver = { 1363088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .name = "radio-aztech", 1373088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil }, 1383088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil }, 1393088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .io_params = io, 1403088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .radio_nr_params = radio_nr, 1413088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .io_ports = aztech_ioports, 1423088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .num_of_io_ports = ARRAY_SIZE(aztech_ioports), 143eb27fafef69568a82c46c27a1761f037e4272b87Ondrej Zary .region_size = 8, 1443088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .card = "Aztech Radio", 1453088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .ops = &aztech_ops, 1463088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .has_stereo = true, 1473088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil .max_volume = 3, 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init aztech_init(void) 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1523088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil return isa_register_driver(&aztech_driver.driver, AZTECH_MAX); 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 155e697e12ecede8ee4f5beb11ed4981d7254be0125Hans Verkuilstatic void __exit aztech_exit(void) 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1573088fba877ee8bf284b12a73332b813b5478a64dHans Verkuil isa_unregister_driver(&aztech_driver.driver); 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(aztech_init); 161e697e12ecede8ee4f5beb11ed4981d7254be0125Hans Verkuilmodule_exit(aztech_exit); 162