1/* 2 *************************************************************************** 3 * 4 * radio-gemtek-pci.c - Gemtek PCI Radio driver 5 * (C) 2001 Vladimir Shebordaev <vshebordaev@mail.ru> 6 * 7 *************************************************************************** 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License as 11 * published by the Free Software Foundation; either version 2 of 12 * the License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public 20 * License along with this program; if not, write to the Free 21 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 22 * USA. 23 * 24 *************************************************************************** 25 * 26 * Gemtek Corp still silently refuses to release any specifications 27 * of their multimedia devices, so the protocol still has to be 28 * reverse engineered. 29 * 30 * The v4l code was inspired by Jonas Munsin's Gemtek serial line 31 * radio device driver. 32 * 33 * Please, let me know if this piece of code was useful :) 34 * 35 * TODO: multiple device support and portability were not tested 36 * 37 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 38 * 39 *************************************************************************** 40 */ 41 42#include <linux/types.h> 43#include <linux/list.h> 44#include <linux/module.h> 45#include <linux/init.h> 46#include <linux/pci.h> 47#include <linux/videodev2.h> 48#include <media/v4l2-common.h> 49#include <linux/errno.h> 50 51#include <linux/version.h> /* for KERNEL_VERSION MACRO */ 52#define RADIO_VERSION KERNEL_VERSION(0,0,2) 53 54static struct v4l2_queryctrl radio_qctrl[] = { 55 { 56 .id = V4L2_CID_AUDIO_MUTE, 57 .name = "Mute", 58 .minimum = 0, 59 .maximum = 1, 60 .default_value = 1, 61 .type = V4L2_CTRL_TYPE_BOOLEAN, 62 },{ 63 .id = V4L2_CID_AUDIO_VOLUME, 64 .name = "Volume", 65 .minimum = 0, 66 .maximum = 65535, 67 .step = 65535, 68 .default_value = 0xff, 69 .type = V4L2_CTRL_TYPE_INTEGER, 70 } 71}; 72 73#include <asm/io.h> 74#include <asm/uaccess.h> 75 76#ifndef PCI_VENDOR_ID_GEMTEK 77#define PCI_VENDOR_ID_GEMTEK 0x5046 78#endif 79 80#ifndef PCI_DEVICE_ID_GEMTEK_PR103 81#define PCI_DEVICE_ID_GEMTEK_PR103 0x1001 82#endif 83 84#ifndef GEMTEK_PCI_RANGE_LOW 85#define GEMTEK_PCI_RANGE_LOW (87*16000) 86#endif 87 88#ifndef GEMTEK_PCI_RANGE_HIGH 89#define GEMTEK_PCI_RANGE_HIGH (108*16000) 90#endif 91 92struct gemtek_pci_card { 93 struct video_device *videodev; 94 95 u32 iobase; 96 u32 length; 97 u8 chiprev; 98 u16 model; 99 100 u32 current_frequency; 101 u8 mute; 102}; 103 104static const char rcsid[] = "$Id: radio-gemtek-pci.c,v 1.1.1.1 2007-08-03 18:52:39 $"; 105 106static int nr_radio = -1; 107 108static inline u8 gemtek_pci_out( u16 value, u32 port ) 109{ 110 outw( value, port ); 111 112 return (u8)value; 113} 114 115#define _b0( v ) *((u8 *)&v) 116static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep ) 117{ 118 register u8 byte = *last_byte; 119 120 if ( !value ) { 121 if ( !keep ) 122 value = (u16)port; 123 byte &= 0xfd; 124 } else 125 byte |= 2; 126 127 _b0( value ) = byte; 128 outw( value, port ); 129 byte |= 1; 130 _b0( value ) = byte; 131 outw( value, port ); 132 byte &= 0xfe; 133 _b0( value ) = byte; 134 outw( value, port ); 135 136 *last_byte = byte; 137} 138 139static inline void gemtek_pci_nil( u32 port, u8 *last_byte ) 140{ 141 __gemtek_pci_cmd( 0x00, port, last_byte, false ); 142} 143 144static inline void gemtek_pci_cmd( u16 cmd, u32 port, u8 *last_byte ) 145{ 146 __gemtek_pci_cmd( cmd, port, last_byte, true ); 147} 148 149static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long frequency ) 150{ 151 register int i; 152 register u32 value = frequency / 200 + 856; 153 register u16 mask = 0x8000; 154 u8 last_byte; 155 u32 port = card->iobase; 156 157 last_byte = gemtek_pci_out( 0x06, port ); 158 159 i = 0; 160 do { 161 gemtek_pci_nil( port, &last_byte ); 162 i++; 163 } while ( i < 9 ); 164 165 i = 0; 166 do { 167 gemtek_pci_cmd( value & mask, port, &last_byte ); 168 mask >>= 1; 169 i++; 170 } while ( i < 16 ); 171 172 outw( 0x10, port ); 173} 174 175 176static inline void gemtek_pci_mute( struct gemtek_pci_card *card ) 177{ 178 outb( 0x1f, card->iobase ); 179 card->mute = true; 180} 181 182static inline void gemtek_pci_unmute( struct gemtek_pci_card *card ) 183{ 184 if ( card->mute ) { 185 gemtek_pci_setfrequency( card, card->current_frequency ); 186 card->mute = false; 187 } 188} 189 190static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card ) 191{ 192 return ( inb( card->iobase ) & 0x08 ) ? 0 : 1; 193} 194 195static int vidioc_querycap(struct file *file, void *priv, 196 struct v4l2_capability *v) 197{ 198 strlcpy(v->driver, "radio-gemtek-pci", sizeof(v->driver)); 199 strlcpy(v->card, "GemTek PCI Radio", sizeof(v->card)); 200 sprintf(v->bus_info, "ISA"); 201 v->version = RADIO_VERSION; 202 v->capabilities = V4L2_CAP_TUNER; 203 return 0; 204} 205 206static int vidioc_g_tuner(struct file *file, void *priv, 207 struct v4l2_tuner *v) 208{ 209 struct video_device *dev = video_devdata(file); 210 struct gemtek_pci_card *card = dev->priv; 211 212 if (v->index > 0) 213 return -EINVAL; 214 215 strcpy(v->name, "FM"); 216 v->type = V4L2_TUNER_RADIO; 217 v->rangelow = GEMTEK_PCI_RANGE_LOW; 218 v->rangehigh = GEMTEK_PCI_RANGE_HIGH; 219 v->rxsubchans = V4L2_TUNER_SUB_MONO; 220 v->capability = V4L2_TUNER_CAP_LOW; 221 v->audmode = V4L2_TUNER_MODE_MONO; 222 v->signal = 0xffff * gemtek_pci_getsignal(card); 223 return 0; 224} 225 226static int vidioc_s_tuner(struct file *file, void *priv, 227 struct v4l2_tuner *v) 228{ 229 if (v->index > 0) 230 return -EINVAL; 231 return 0; 232} 233 234static int vidioc_s_frequency(struct file *file, void *priv, 235 struct v4l2_frequency *f) 236{ 237 struct video_device *dev = video_devdata(file); 238 struct gemtek_pci_card *card = dev->priv; 239 240 if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) || 241 (f->frequency > GEMTEK_PCI_RANGE_HIGH) ) 242 return -EINVAL; 243 gemtek_pci_setfrequency(card, f->frequency); 244 card->current_frequency = f->frequency; 245 card->mute = false; 246 return 0; 247} 248 249static int vidioc_g_frequency(struct file *file, void *priv, 250 struct v4l2_frequency *f) 251{ 252 struct video_device *dev = video_devdata(file); 253 struct gemtek_pci_card *card = dev->priv; 254 255 f->type = V4L2_TUNER_RADIO; 256 f->frequency = card->current_frequency; 257 return 0; 258} 259 260static int vidioc_queryctrl(struct file *file, void *priv, 261 struct v4l2_queryctrl *qc) 262{ 263 int i; 264 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 265 if (qc->id && qc->id == radio_qctrl[i].id) { 266 memcpy(qc, &(radio_qctrl[i]), 267 sizeof(*qc)); 268 return 0; 269 } 270 } 271 return -EINVAL; 272} 273 274static int vidioc_g_ctrl(struct file *file, void *priv, 275 struct v4l2_control *ctrl) 276{ 277 struct video_device *dev = video_devdata(file); 278 struct gemtek_pci_card *card = dev->priv; 279 280 switch (ctrl->id) { 281 case V4L2_CID_AUDIO_MUTE: 282 ctrl->value = card->mute; 283 return 0; 284 case V4L2_CID_AUDIO_VOLUME: 285 if (card->mute) 286 ctrl->value = 0; 287 else 288 ctrl->value = 65535; 289 return 0; 290 } 291 return -EINVAL; 292} 293 294static int vidioc_s_ctrl(struct file *file, void *priv, 295 struct v4l2_control *ctrl) 296{ 297 struct video_device *dev = video_devdata(file); 298 struct gemtek_pci_card *card = dev->priv; 299 300 switch (ctrl->id) { 301 case V4L2_CID_AUDIO_MUTE: 302 if (ctrl->value) 303 gemtek_pci_mute(card); 304 else 305 gemtek_pci_unmute(card); 306 return 0; 307 case V4L2_CID_AUDIO_VOLUME: 308 if (ctrl->value) 309 gemtek_pci_unmute(card); 310 else 311 gemtek_pci_mute(card); 312 return 0; 313 } 314 return -EINVAL; 315} 316 317static int vidioc_g_audio(struct file *file, void *priv, 318 struct v4l2_audio *a) 319{ 320 if (a->index > 1) 321 return -EINVAL; 322 323 strcpy(a->name, "Radio"); 324 a->capability = V4L2_AUDCAP_STEREO; 325 return 0; 326} 327 328static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 329{ 330 *i = 0; 331 return 0; 332} 333 334static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 335{ 336 if (i != 0) 337 return -EINVAL; 338 return 0; 339} 340 341static int vidioc_s_audio(struct file *file, void *priv, 342 struct v4l2_audio *a) 343{ 344 if (a->index != 0) 345 return -EINVAL; 346 return 0; 347} 348 349enum { 350 GEMTEK_PR103 351}; 352 353static char *card_names[] __devinitdata = { 354 "GEMTEK_PR103" 355}; 356 357static struct pci_device_id gemtek_pci_id[] = 358{ 359 { PCI_VENDOR_ID_GEMTEK, PCI_DEVICE_ID_GEMTEK_PR103, 360 PCI_ANY_ID, PCI_ANY_ID, 0, 0, GEMTEK_PR103 }, 361 { 0 } 362}; 363 364MODULE_DEVICE_TABLE( pci, gemtek_pci_id ); 365 366static int mx = 1; 367 368static const struct file_operations gemtek_pci_fops = { 369 .owner = THIS_MODULE, 370 .open = video_exclusive_open, 371 .release = video_exclusive_release, 372 .ioctl = video_ioctl2, 373 .compat_ioctl = v4l_compat_ioctl32, 374 .llseek = no_llseek, 375}; 376 377static struct video_device vdev_template = { 378 .owner = THIS_MODULE, 379 .name = "Gemtek PCI Radio", 380 .type = VID_TYPE_TUNER, 381 .hardware = 0, 382 .fops = &gemtek_pci_fops, 383 .vidioc_querycap = vidioc_querycap, 384 .vidioc_g_tuner = vidioc_g_tuner, 385 .vidioc_s_tuner = vidioc_s_tuner, 386 .vidioc_g_audio = vidioc_g_audio, 387 .vidioc_s_audio = vidioc_s_audio, 388 .vidioc_g_input = vidioc_g_input, 389 .vidioc_s_input = vidioc_s_input, 390 .vidioc_g_frequency = vidioc_g_frequency, 391 .vidioc_s_frequency = vidioc_s_frequency, 392 .vidioc_queryctrl = vidioc_queryctrl, 393 .vidioc_g_ctrl = vidioc_g_ctrl, 394 .vidioc_s_ctrl = vidioc_s_ctrl, 395}; 396 397static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id ) 398{ 399 struct gemtek_pci_card *card; 400 struct video_device *devradio; 401 402 if ( (card = kzalloc( sizeof( struct gemtek_pci_card ), GFP_KERNEL )) == NULL ) { 403 printk( KERN_ERR "gemtek_pci: out of memory\n" ); 404 return -ENOMEM; 405 } 406 407 if ( pci_enable_device( pci_dev ) ) 408 goto err_pci; 409 410 card->iobase = pci_resource_start( pci_dev, 0 ); 411 card->length = pci_resource_len( pci_dev, 0 ); 412 413 if ( request_region( card->iobase, card->length, card_names[pci_id->driver_data] ) == NULL ) { 414 printk( KERN_ERR "gemtek_pci: i/o port already in use\n" ); 415 goto err_pci; 416 } 417 418 pci_read_config_byte( pci_dev, PCI_REVISION_ID, &card->chiprev ); 419 pci_read_config_word( pci_dev, PCI_SUBSYSTEM_ID, &card->model ); 420 421 pci_set_drvdata( pci_dev, card ); 422 423 if ( (devradio = kmalloc( sizeof( struct video_device ), GFP_KERNEL )) == NULL ) { 424 printk( KERN_ERR "gemtek_pci: out of memory\n" ); 425 goto err_video; 426 } 427 *devradio = vdev_template; 428 429 if ( video_register_device( devradio, VFL_TYPE_RADIO , nr_radio) == -1 ) { 430 kfree( devradio ); 431 goto err_video; 432 } 433 434 card->videodev = devradio; 435 devradio->priv = card; 436 gemtek_pci_mute( card ); 437 438 printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n", 439 card->chiprev, card->iobase, card->iobase + card->length - 1 ); 440 441 return 0; 442 443err_video: 444 release_region( card->iobase, card->length ); 445 446err_pci: 447 kfree( card ); 448 return -ENODEV; 449} 450 451static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev ) 452{ 453 struct gemtek_pci_card *card = pci_get_drvdata( pci_dev ); 454 455 video_unregister_device( card->videodev ); 456 kfree( card->videodev ); 457 458 release_region( card->iobase, card->length ); 459 460 if ( mx ) 461 gemtek_pci_mute( card ); 462 463 kfree( card ); 464 465 pci_set_drvdata( pci_dev, NULL ); 466} 467 468static struct pci_driver gemtek_pci_driver = 469{ 470 .name = "gemtek_pci", 471 .id_table = gemtek_pci_id, 472 .probe = gemtek_pci_probe, 473 .remove = __devexit_p(gemtek_pci_remove), 474}; 475 476static int __init gemtek_pci_init_module( void ) 477{ 478 return pci_register_driver( &gemtek_pci_driver ); 479} 480 481static void __exit gemtek_pci_cleanup_module( void ) 482{ 483 pci_unregister_driver(&gemtek_pci_driver); 484} 485 486MODULE_AUTHOR( "Vladimir Shebordaev <vshebordaev@mail.ru>" ); 487MODULE_DESCRIPTION( "The video4linux driver for the Gemtek PCI Radio Card" ); 488MODULE_LICENSE("GPL"); 489 490module_param(mx, bool, 0); 491MODULE_PARM_DESC( mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not" ); 492module_param(nr_radio, int, 0); 493MODULE_PARM_DESC( nr_radio, "video4linux device number to use"); 494 495module_init( gemtek_pci_init_module ); 496module_exit( gemtek_pci_cleanup_module ); 497