162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * GemTek radio card driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 1998 Jonas Munsin <jmunsin@iki.fi> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * GemTek hasn't released any specs on the card, so the protocol had to 862306a36Sopenharmony_ci * be reverse engineered with dosemu. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Besides the protocol changes, this is mostly a copy of: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood 1562306a36Sopenharmony_ci * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> 1662306a36Sopenharmony_ci * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> 1962306a36Sopenharmony_ci * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@kernel.org> 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Note: this card seems to swap the left and right audio channels! 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/module.h> /* Modules */ 2762306a36Sopenharmony_ci#include <linux/init.h> /* Initdata */ 2862306a36Sopenharmony_ci#include <linux/ioport.h> /* request_region */ 2962306a36Sopenharmony_ci#include <linux/delay.h> /* udelay */ 3062306a36Sopenharmony_ci#include <linux/videodev2.h> /* kernel radio structs */ 3162306a36Sopenharmony_ci#include <linux/mutex.h> 3262306a36Sopenharmony_ci#include <linux/io.h> /* outb, outb_p */ 3362306a36Sopenharmony_ci#include <linux/pnp.h> 3462306a36Sopenharmony_ci#include <linux/slab.h> 3562306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3662306a36Sopenharmony_ci#include <media/v4l2-device.h> 3762306a36Sopenharmony_ci#include "radio-isa.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * Module info. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciMODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>"); 4462306a36Sopenharmony_ciMODULE_DESCRIPTION("A driver for the GemTek Radio card."); 4562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4662306a36Sopenharmony_ciMODULE_VERSION("1.0.0"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * Module params. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#ifndef CONFIG_RADIO_GEMTEK_PORT 5362306a36Sopenharmony_ci#define CONFIG_RADIO_GEMTEK_PORT -1 5462306a36Sopenharmony_ci#endif 5562306a36Sopenharmony_ci#ifndef CONFIG_RADIO_GEMTEK_PROBE 5662306a36Sopenharmony_ci#define CONFIG_RADIO_GEMTEK_PROBE 1 5762306a36Sopenharmony_ci#endif 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define GEMTEK_MAX 4 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic bool probe = CONFIG_RADIO_GEMTEK_PROBE; 6262306a36Sopenharmony_cistatic bool hardmute; 6362306a36Sopenharmony_cistatic int io[GEMTEK_MAX] = { [0] = CONFIG_RADIO_GEMTEK_PORT, 6462306a36Sopenharmony_ci [1 ... (GEMTEK_MAX - 1)] = -1 }; 6562306a36Sopenharmony_cistatic int radio_nr[GEMTEK_MAX] = { [0 ... (GEMTEK_MAX - 1)] = -1 }; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cimodule_param(probe, bool, 0444); 6862306a36Sopenharmony_ciMODULE_PARM_DESC(probe, "Enable automatic device probing."); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cimodule_param(hardmute, bool, 0644); 7162306a36Sopenharmony_ciMODULE_PARM_DESC(hardmute, "Enable 'hard muting' by shutting down PLL, may reduce static noise."); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cimodule_param_array(io, int, NULL, 0444); 7462306a36Sopenharmony_ciMODULE_PARM_DESC(io, "Force I/O ports for the GemTek Radio card if automatic probing is disabled or fails. The most common I/O ports are: 0x20c 0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to work for the combined sound/radiocard)."); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cimodule_param_array(radio_nr, int, NULL, 0444); 7762306a36Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "Radio device numbers"); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * Frequency calculation constants. Intermediate frequency 10.52 MHz (nominal 8162306a36Sopenharmony_ci * value 10.7 MHz), reference divisor 6.39 kHz (nominal 6.25 kHz). 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci#define FSCALE 8 8462306a36Sopenharmony_ci#define IF_OFFSET ((unsigned int)(10.52 * 16000 * (1<<FSCALE))) 8562306a36Sopenharmony_ci#define REF_FREQ ((unsigned int)(6.39 * 16 * (1<<FSCALE))) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define GEMTEK_CK 0x01 /* Clock signal */ 8862306a36Sopenharmony_ci#define GEMTEK_DA 0x02 /* Serial data */ 8962306a36Sopenharmony_ci#define GEMTEK_CE 0x04 /* Chip enable */ 9062306a36Sopenharmony_ci#define GEMTEK_NS 0x08 /* No signal */ 9162306a36Sopenharmony_ci#define GEMTEK_MT 0x10 /* Line mute */ 9262306a36Sopenharmony_ci#define GEMTEK_STDF_3_125_KHZ 0x01 /* Standard frequency 3.125 kHz */ 9362306a36Sopenharmony_ci#define GEMTEK_PLL_OFF 0x07 /* PLL off */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define BU2614_BUS_SIZE 32 /* BU2614 / BU2614FS bus size */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define SHORT_DELAY 5 /* usec */ 9862306a36Sopenharmony_ci#define LONG_DELAY 75 /* usec */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistruct gemtek { 10162306a36Sopenharmony_ci struct radio_isa_card isa; 10262306a36Sopenharmony_ci bool muted; 10362306a36Sopenharmony_ci u32 bu2614data; 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */ 10762306a36Sopenharmony_ci#define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */ 10862306a36Sopenharmony_ci#define BU2614_VOID_BITS 4 /* unused */ 10962306a36Sopenharmony_ci#define BU2614_FMES_BITS 1 /* CT, Frequency measurement beginning data */ 11062306a36Sopenharmony_ci#define BU2614_STDF_BITS 3 /* R0..R2, Standard frequency data */ 11162306a36Sopenharmony_ci#define BU2614_SWIN_BITS 1 /* S, Switch between FMIN / AMIN */ 11262306a36Sopenharmony_ci#define BU2614_SWAL_BITS 1 /* PS, Swallow counter division (AMIN only)*/ 11362306a36Sopenharmony_ci#define BU2614_VOID2_BITS 1 /* unused */ 11462306a36Sopenharmony_ci#define BU2614_FMUN_BITS 1 /* GT, Frequency measurement time & unlock */ 11562306a36Sopenharmony_ci#define BU2614_TEST_BITS 1 /* TS, Test data is input */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define BU2614_FREQ_SHIFT 0 11862306a36Sopenharmony_ci#define BU2614_PORT_SHIFT (BU2614_FREQ_BITS + BU2614_FREQ_SHIFT) 11962306a36Sopenharmony_ci#define BU2614_VOID_SHIFT (BU2614_PORT_BITS + BU2614_PORT_SHIFT) 12062306a36Sopenharmony_ci#define BU2614_FMES_SHIFT (BU2614_VOID_BITS + BU2614_VOID_SHIFT) 12162306a36Sopenharmony_ci#define BU2614_STDF_SHIFT (BU2614_FMES_BITS + BU2614_FMES_SHIFT) 12262306a36Sopenharmony_ci#define BU2614_SWIN_SHIFT (BU2614_STDF_BITS + BU2614_STDF_SHIFT) 12362306a36Sopenharmony_ci#define BU2614_SWAL_SHIFT (BU2614_SWIN_BITS + BU2614_SWIN_SHIFT) 12462306a36Sopenharmony_ci#define BU2614_VOID2_SHIFT (BU2614_SWAL_BITS + BU2614_SWAL_SHIFT) 12562306a36Sopenharmony_ci#define BU2614_FMUN_SHIFT (BU2614_VOID2_BITS + BU2614_VOID2_SHIFT) 12662306a36Sopenharmony_ci#define BU2614_TEST_SHIFT (BU2614_FMUN_BITS + BU2614_FMUN_SHIFT) 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#define MKMASK(field) (((1UL<<BU2614_##field##_BITS) - 1) << \ 12962306a36Sopenharmony_ci BU2614_##field##_SHIFT) 13062306a36Sopenharmony_ci#define BU2614_PORT_MASK MKMASK(PORT) 13162306a36Sopenharmony_ci#define BU2614_FREQ_MASK MKMASK(FREQ) 13262306a36Sopenharmony_ci#define BU2614_VOID_MASK MKMASK(VOID) 13362306a36Sopenharmony_ci#define BU2614_FMES_MASK MKMASK(FMES) 13462306a36Sopenharmony_ci#define BU2614_STDF_MASK MKMASK(STDF) 13562306a36Sopenharmony_ci#define BU2614_SWIN_MASK MKMASK(SWIN) 13662306a36Sopenharmony_ci#define BU2614_SWAL_MASK MKMASK(SWAL) 13762306a36Sopenharmony_ci#define BU2614_VOID2_MASK MKMASK(VOID2) 13862306a36Sopenharmony_ci#define BU2614_FMUN_MASK MKMASK(FMUN) 13962306a36Sopenharmony_ci#define BU2614_TEST_MASK MKMASK(TEST) 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* 14262306a36Sopenharmony_ci * Set data which will be sent to BU2614FS. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci#define gemtek_bu2614_set(dev, field, data) ((dev)->bu2614data = \ 14562306a36Sopenharmony_ci ((dev)->bu2614data & ~field##_MASK) | ((data) << field##_SHIFT)) 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* 14862306a36Sopenharmony_ci * Transmit settings to BU2614FS over GemTek IC. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistatic void gemtek_bu2614_transmit(struct gemtek *gt) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct radio_isa_card *isa = >->isa; 15362306a36Sopenharmony_ci int i, bit, q, mute; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci mute = gt->muted ? GEMTEK_MT : 0x00; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, isa->io); 15862306a36Sopenharmony_ci udelay(LONG_DELAY); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) { 16162306a36Sopenharmony_ci bit = (q & 1) ? GEMTEK_DA : 0; 16262306a36Sopenharmony_ci outb_p(mute | GEMTEK_CE | bit, isa->io); 16362306a36Sopenharmony_ci udelay(SHORT_DELAY); 16462306a36Sopenharmony_ci outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, isa->io); 16562306a36Sopenharmony_ci udelay(SHORT_DELAY); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci outb_p(mute | GEMTEK_DA | GEMTEK_CK, isa->io); 16962306a36Sopenharmony_ci udelay(SHORT_DELAY); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* 17362306a36Sopenharmony_ci * Calculate divisor from FM-frequency for BU2614FS (3.125 KHz STDF expected). 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_cistatic unsigned long gemtek_convfreq(unsigned long freq) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci return ((freq << FSCALE) + IF_OFFSET + REF_FREQ / 2) / REF_FREQ; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic struct radio_isa_card *gemtek_alloc(void) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct gemtek *gt = kzalloc(sizeof(*gt), GFP_KERNEL); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (gt) 18562306a36Sopenharmony_ci gt->muted = true; 18662306a36Sopenharmony_ci return gt ? >->isa : NULL; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* 19062306a36Sopenharmony_ci * Set FM-frequency. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_cistatic int gemtek_s_frequency(struct radio_isa_card *isa, u32 freq) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct gemtek *gt = container_of(isa, struct gemtek, isa); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (hardmute && gt->muted) 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_PORT, 0); 20062306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_FMES, 0); 20162306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_SWIN, 0); /* FM-mode */ 20262306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_SWAL, 0); 20362306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */ 20462306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_TEST, 0); 20562306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ); 20662306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq)); 20762306a36Sopenharmony_ci gemtek_bu2614_transmit(gt); 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* 21262306a36Sopenharmony_ci * Set mute flag. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_cistatic int gemtek_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct gemtek *gt = container_of(isa, struct gemtek, isa); 21762306a36Sopenharmony_ci int i; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci gt->muted = mute; 22062306a36Sopenharmony_ci if (hardmute) { 22162306a36Sopenharmony_ci if (!mute) 22262306a36Sopenharmony_ci return gemtek_s_frequency(isa, isa->freq); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Turn off PLL, disable data output */ 22562306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_PORT, 0); 22662306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */ 22762306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_SWIN, 0); /* FM-mode */ 22862306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_SWAL, 0); 22962306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_FMUN, 0); /* GT bit off */ 23062306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_TEST, 0); 23162306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF); 23262306a36Sopenharmony_ci gemtek_bu2614_set(gt, BU2614_FREQ, 0); 23362306a36Sopenharmony_ci gemtek_bu2614_transmit(gt); 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Read bus contents (CE, CK and DA). */ 23862306a36Sopenharmony_ci i = inb_p(isa->io); 23962306a36Sopenharmony_ci /* Write it back with mute flag set. */ 24062306a36Sopenharmony_ci outb_p((i >> 5) | (mute ? GEMTEK_MT : 0), isa->io); 24162306a36Sopenharmony_ci udelay(SHORT_DELAY); 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic u32 gemtek_g_rxsubchans(struct radio_isa_card *isa) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci if (inb_p(isa->io) & GEMTEK_NS) 24862306a36Sopenharmony_ci return V4L2_TUNER_SUB_MONO; 24962306a36Sopenharmony_ci return V4L2_TUNER_SUB_STEREO; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* 25362306a36Sopenharmony_ci * Check if requested card acts like GemTek Radio card. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_cistatic bool gemtek_probe(struct radio_isa_card *isa, int io) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci int i, q; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci q = inb_p(io); /* Read bus contents before probing. */ 26062306a36Sopenharmony_ci /* Try to turn on CE, CK and DA respectively and check if card responds 26162306a36Sopenharmony_ci properly. */ 26262306a36Sopenharmony_ci for (i = 0; i < 3; ++i) { 26362306a36Sopenharmony_ci outb_p(1 << i, io); 26462306a36Sopenharmony_ci udelay(SHORT_DELAY); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if ((inb_p(io) & ~GEMTEK_NS) != (0x17 | (1 << (i + 5)))) 26762306a36Sopenharmony_ci return false; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci outb_p(q >> 5, io); /* Write bus contents back. */ 27062306a36Sopenharmony_ci udelay(SHORT_DELAY); 27162306a36Sopenharmony_ci return true; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic const struct radio_isa_ops gemtek_ops = { 27562306a36Sopenharmony_ci .alloc = gemtek_alloc, 27662306a36Sopenharmony_ci .probe = gemtek_probe, 27762306a36Sopenharmony_ci .s_mute_volume = gemtek_s_mute_volume, 27862306a36Sopenharmony_ci .s_frequency = gemtek_s_frequency, 27962306a36Sopenharmony_ci .g_rxsubchans = gemtek_g_rxsubchans, 28062306a36Sopenharmony_ci}; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci#ifdef CONFIG_PNP 28562306a36Sopenharmony_cistatic const struct pnp_device_id gemtek_pnp_devices[] = { 28662306a36Sopenharmony_ci /* AOpen FX-3D/Pro Radio */ 28762306a36Sopenharmony_ci {.id = "ADS7183", .driver_data = 0}, 28862306a36Sopenharmony_ci {.id = ""} 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp, gemtek_pnp_devices); 29262306a36Sopenharmony_ci#endif 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic struct radio_isa_driver gemtek_driver = { 29562306a36Sopenharmony_ci .driver = { 29662306a36Sopenharmony_ci .match = radio_isa_match, 29762306a36Sopenharmony_ci .probe = radio_isa_probe, 29862306a36Sopenharmony_ci .remove = radio_isa_remove, 29962306a36Sopenharmony_ci .driver = { 30062306a36Sopenharmony_ci .name = "radio-gemtek", 30162306a36Sopenharmony_ci }, 30262306a36Sopenharmony_ci }, 30362306a36Sopenharmony_ci#ifdef CONFIG_PNP 30462306a36Sopenharmony_ci .pnp_driver = { 30562306a36Sopenharmony_ci .name = "radio-gemtek", 30662306a36Sopenharmony_ci .id_table = gemtek_pnp_devices, 30762306a36Sopenharmony_ci .probe = radio_isa_pnp_probe, 30862306a36Sopenharmony_ci .remove = radio_isa_pnp_remove, 30962306a36Sopenharmony_ci }, 31062306a36Sopenharmony_ci#endif 31162306a36Sopenharmony_ci .io_params = io, 31262306a36Sopenharmony_ci .radio_nr_params = radio_nr, 31362306a36Sopenharmony_ci .io_ports = gemtek_ioports, 31462306a36Sopenharmony_ci .num_of_io_ports = ARRAY_SIZE(gemtek_ioports), 31562306a36Sopenharmony_ci .region_size = 1, 31662306a36Sopenharmony_ci .card = "GemTek Radio", 31762306a36Sopenharmony_ci .ops = &gemtek_ops, 31862306a36Sopenharmony_ci .has_stereo = true, 31962306a36Sopenharmony_ci}; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int __init gemtek_init(void) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci gemtek_driver.probe = probe; 32462306a36Sopenharmony_ci#ifdef CONFIG_PNP 32562306a36Sopenharmony_ci pnp_register_driver(&gemtek_driver.pnp_driver); 32662306a36Sopenharmony_ci#endif 32762306a36Sopenharmony_ci return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void __exit gemtek_exit(void) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci hardmute = true; /* Turn off PLL */ 33362306a36Sopenharmony_ci#ifdef CONFIG_PNP 33462306a36Sopenharmony_ci pnp_unregister_driver(&gemtek_driver.pnp_driver); 33562306a36Sopenharmony_ci#endif 33662306a36Sopenharmony_ci isa_unregister_driver(&gemtek_driver.driver); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cimodule_init(gemtek_init); 34062306a36Sopenharmony_cimodule_exit(gemtek_exit); 341