Blame view
sound/pci/fm801.c
41 KB
c942fddf8
|
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4c
|
2 3 |
/* * The driver for the ForteMedia FM801 based soundcards |
c1017a4cd
|
4 |
* Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
1da177e4c
|
5 |
*/ |
1da177e4c
|
6 7 8 |
#include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> |
215dacc28
|
9 |
#include <linux/io.h> |
1da177e4c
|
10 11 |
#include <linux/pci.h> #include <linux/slab.h> |
65a772172
|
12 |
#include <linux/module.h> |
1da177e4c
|
13 14 |
#include <sound/core.h> #include <sound/pcm.h> |
666c70ffd
|
15 |
#include <sound/tlv.h> |
1da177e4c
|
16 17 18 19 |
#include <sound/ac97_codec.h> #include <sound/mpu401.h> #include <sound/opl3.h> #include <sound/initval.h> |
efce4bb9e
|
20 |
#ifdef CONFIG_SND_FM801_TEA575X_BOOL |
d647f0b70
|
21 |
#include <media/drv-intf/tea575x.h> |
1da177e4c
|
22 |
#endif |
c1017a4cd
|
23 |
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); |
1da177e4c
|
24 25 26 27 28 29 30 |
MODULE_DESCRIPTION("ForteMedia FM801"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{ForteMedia,FM801}," "{Genius,SoundMaker Live 5.1}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ |
a67ff6a54
|
31 |
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ |
1da177e4c
|
32 33 34 |
/* * Enable TEA575x tuner * 1 = MediaForte 256-PCS |
d7ba858a7
|
35 |
* 2 = MediaForte 256-PCP |
1da177e4c
|
36 |
* 3 = MediaForte 64-PCR |
fb716c0b7
|
37 |
* 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card |
1da177e4c
|
38 39 |
* High 16-bits are video (radio) device number + 1 */ |
6581f4e74
|
40 |
static int tea575x_tuner[SNDRV_CARDS]; |
d4ecc83b7
|
41 |
static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; |
1da177e4c
|
42 43 44 45 46 47 48 49 |
module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the FM801 soundcard."); module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for the FM801 soundcard."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable FM801 soundcard."); module_param_array(tea575x_tuner, int, NULL, 0444); |
d7ba858a7
|
50 |
MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only)."); |
d4ecc83b7
|
51 52 |
module_param_array(radio_nr, int, NULL, 0444); MODULE_PARM_DESC(radio_nr, "Radio device numbers"); |
fb716c0b7
|
53 |
|
c37279b92
|
54 |
#define TUNER_DISABLED (1<<3) |
fb716c0b7
|
55 56 |
#define TUNER_ONLY (1<<4) #define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF) |
1da177e4c
|
57 58 59 60 |
/* * Direct registers */ |
215dacc28
|
61 62 63 64 |
#define fm801_writew(chip,reg,value) outw((value), chip->port + FM801_##reg) #define fm801_readw(chip,reg) inw(chip->port + FM801_##reg) #define fm801_writel(chip,reg,value) outl((value), chip->port + FM801_##reg) |
1da177e4c
|
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
#define FM801_PCM_VOL 0x00 /* PCM Output Volume */ #define FM801_FM_VOL 0x02 /* FM Output Volume */ #define FM801_I2S_VOL 0x04 /* I2S Volume */ #define FM801_REC_SRC 0x06 /* Record Source */ #define FM801_PLY_CTRL 0x08 /* Playback Control */ #define FM801_PLY_COUNT 0x0a /* Playback Count */ #define FM801_PLY_BUF1 0x0c /* Playback Bufer I */ #define FM801_PLY_BUF2 0x10 /* Playback Buffer II */ #define FM801_CAP_CTRL 0x14 /* Capture Control */ #define FM801_CAP_COUNT 0x16 /* Capture Count */ #define FM801_CAP_BUF1 0x18 /* Capture Buffer I */ #define FM801_CAP_BUF2 0x1c /* Capture Buffer II */ #define FM801_CODEC_CTRL 0x22 /* Codec Control */ #define FM801_I2S_MODE 0x24 /* I2S Mode Control */ #define FM801_VOLUME 0x26 /* Volume Up/Down/Mute Status */ #define FM801_I2C_CTRL 0x29 /* I2C Control */ #define FM801_AC97_CMD 0x2a /* AC'97 Command */ #define FM801_AC97_DATA 0x2c /* AC'97 Data */ #define FM801_MPU401_DATA 0x30 /* MPU401 Data */ #define FM801_MPU401_CMD 0x31 /* MPU401 Command */ #define FM801_GPIO_CTRL 0x52 /* General Purpose I/O Control */ #define FM801_GEN_CTRL 0x54 /* General Control */ #define FM801_IRQ_MASK 0x56 /* Interrupt Mask */ #define FM801_IRQ_STATUS 0x5a /* Interrupt Status */ #define FM801_OPL3_BANK0 0x68 /* OPL3 Status Read / Bank 0 Write */ #define FM801_OPL3_DATA0 0x69 /* OPL3 Data 0 Write */ #define FM801_OPL3_BANK1 0x6a /* OPL3 Bank 1 Write */ #define FM801_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */ #define FM801_POWERDOWN 0x70 /* Blocks Power Down Control */ |
b1e9ed26a
|
95 96 97 98 99 |
/* codec access */ #define FM801_AC97_READ (1<<7) /* read=1, write=0 */ #define FM801_AC97_VALID (1<<8) /* port valid=1 */ #define FM801_AC97_BUSY (1<<9) /* busy=1 */ #define FM801_AC97_ADDR_SHIFT 10 /* codec id (2bit) */ |
1da177e4c
|
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
/* playback and record control register bits */ #define FM801_BUF1_LAST (1<<1) #define FM801_BUF2_LAST (1<<2) #define FM801_START (1<<5) #define FM801_PAUSE (1<<6) #define FM801_IMMED_STOP (1<<7) #define FM801_RATE_SHIFT 8 #define FM801_RATE_MASK (15 << FM801_RATE_SHIFT) #define FM801_CHANNELS_4 (1<<12) /* playback only */ #define FM801_CHANNELS_6 (2<<12) /* playback only */ #define FM801_CHANNELS_6MS (3<<12) /* playback only */ #define FM801_CHANNELS_MASK (3<<12) #define FM801_16BIT (1<<14) #define FM801_STEREO (1<<15) /* IRQ status bits */ #define FM801_IRQ_PLAYBACK (1<<8) #define FM801_IRQ_CAPTURE (1<<9) #define FM801_IRQ_VOLUME (1<<14) #define FM801_IRQ_MPU (1<<15) /* GPIO control register */ #define FM801_GPIO_GP0 (1<<0) /* read/write */ #define FM801_GPIO_GP1 (1<<1) #define FM801_GPIO_GP2 (1<<2) #define FM801_GPIO_GP3 (1<<3) #define FM801_GPIO_GP(x) (1<<(0+(x))) #define FM801_GPIO_GD0 (1<<8) /* directions: 1 = input, 0 = output*/ #define FM801_GPIO_GD1 (1<<9) #define FM801_GPIO_GD2 (1<<10) #define FM801_GPIO_GD3 (1<<11) #define FM801_GPIO_GD(x) (1<<(8+(x))) #define FM801_GPIO_GS0 (1<<12) /* function select: */ #define FM801_GPIO_GS1 (1<<13) /* 1 = GPIO */ #define FM801_GPIO_GS2 (1<<14) /* 0 = other (S/PDIF, VOL) */ #define FM801_GPIO_GS3 (1<<15) #define FM801_GPIO_GS(x) (1<<(12+(x))) |
052c233e9
|
139 140 141 142 143 144 145 146 147 |
/** * struct fm801 - describes FM801 chip * @port: I/O port number * @multichannel: multichannel support * @secondary: secondary codec * @secondary_addr: address of the secondary codec * @tea575x_tuner: tuner access method & flags * @ply_ctrl: playback control * @cap_ctrl: capture control |
1da177e4c
|
148 |
*/ |
a5f22156e
|
149 |
struct fm801 { |
d3d33aaba
|
150 |
struct device *dev; |
1da177e4c
|
151 |
int irq; |
052c233e9
|
152 153 154 155 156 |
unsigned long port; unsigned int multichannel: 1, secondary: 1; unsigned char secondary_addr; unsigned int tea575x_tuner; |
1da177e4c
|
157 |
|
052c233e9
|
158 159 |
unsigned short ply_ctrl; unsigned short cap_ctrl; |
1da177e4c
|
160 161 162 163 164 165 166 167 168 169 170 171 |
unsigned long ply_buffer; unsigned int ply_buf; unsigned int ply_count; unsigned int ply_size; unsigned int ply_pos; unsigned long cap_buffer; unsigned int cap_buf; unsigned int cap_count; unsigned int cap_size; unsigned int cap_pos; |
a5f22156e
|
172 173 174 |
struct snd_ac97_bus *ac97_bus; struct snd_ac97 *ac97; struct snd_ac97 *ac97_sec; |
1da177e4c
|
175 |
|
a5f22156e
|
176 177 178 179 180 |
struct snd_card *card; struct snd_pcm *pcm; struct snd_rawmidi *rmidi; struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *capture_substream; |
1da177e4c
|
181 182 183 184 |
unsigned int p_dma_size; unsigned int c_dma_size; spinlock_t reg_lock; |
a5f22156e
|
185 |
struct snd_info_entry *proc_entry; |
1da177e4c
|
186 |
|
fdb62b500
|
187 |
#ifdef CONFIG_SND_FM801_TEA575X_BOOL |
d4ecc83b7
|
188 |
struct v4l2_device v4l2_dev; |
a5f22156e
|
189 |
struct snd_tea575x tea; |
1da177e4c
|
190 |
#endif |
b1e9ed26a
|
191 |
|
c7561cd80
|
192 |
#ifdef CONFIG_PM_SLEEP |
b1e9ed26a
|
193 194 |
u16 saved_regs[0x20]; #endif |
1da177e4c
|
195 |
}; |
4b5c15f74
|
196 197 198 199 200 201 202 203 204 205 206 207 208 |
/* * IO accessors */ static inline void fm801_iowrite16(struct fm801 *chip, unsigned short offset, u16 value) { outw(value, chip->port + offset); } static inline u16 fm801_ioread16(struct fm801 *chip, unsigned short offset) { return inw(chip->port + offset); } |
9baa3c34a
|
209 |
static const struct pci_device_id snd_fm801_ids[] = { |
1da177e4c
|
210 |
{ 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* FM801 */ |
26be86592
|
211 |
{ 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* Gallant Odyssey Sound 4 */ |
1da177e4c
|
212 213 214 215 216 217 218 219 |
{ 0, } }; MODULE_DEVICE_TABLE(pci, snd_fm801_ids); /* * common I/O routines */ |
02fd1a76b
|
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
static bool fm801_ac97_is_ready(struct fm801 *chip, unsigned int iterations) { unsigned int idx; for (idx = 0; idx < iterations; idx++) { if (!(fm801_readw(chip, AC97_CMD) & FM801_AC97_BUSY)) return true; udelay(10); } return false; } static bool fm801_ac97_is_valid(struct fm801 *chip, unsigned int iterations) { unsigned int idx; for (idx = 0; idx < iterations; idx++) { if (fm801_readw(chip, AC97_CMD) & FM801_AC97_VALID) return true; udelay(10); } return false; } |
a5f22156e
|
243 |
static int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg, |
1da177e4c
|
244 245 246 247 248 249 250 |
unsigned short mask, unsigned short value) { int change; unsigned long flags; unsigned short old, new; spin_lock_irqsave(&chip->reg_lock, flags); |
4b5c15f74
|
251 |
old = fm801_ioread16(chip, reg); |
1da177e4c
|
252 253 254 |
new = (old & ~mask) | value; change = old != new; if (change) |
4b5c15f74
|
255 |
fm801_iowrite16(chip, reg, new); |
1da177e4c
|
256 257 258 |
spin_unlock_irqrestore(&chip->reg_lock, flags); return change; } |
a5f22156e
|
259 |
static void snd_fm801_codec_write(struct snd_ac97 *ac97, |
1da177e4c
|
260 261 262 |
unsigned short reg, unsigned short val) { |
a5f22156e
|
263 |
struct fm801 *chip = ac97->private_data; |
1da177e4c
|
264 265 266 267 |
/* * Wait until the codec interface is not ready.. */ |
02fd1a76b
|
268 269 270 271 |
if (!fm801_ac97_is_ready(chip, 100)) { dev_err(chip->card->dev, "AC'97 interface is busy (1) "); return; |
1da177e4c
|
272 |
} |
1da177e4c
|
273 |
|
1da177e4c
|
274 |
/* write data and address */ |
215dacc28
|
275 276 |
fm801_writew(chip, AC97_DATA, val); fm801_writew(chip, AC97_CMD, reg | (ac97->addr << FM801_AC97_ADDR_SHIFT)); |
1da177e4c
|
277 278 |
/* * Wait until the write command is not completed.. |
02fd1a76b
|
279 280 281 282 283 |
*/ if (!fm801_ac97_is_ready(chip, 1000)) dev_err(chip->card->dev, "AC'97 interface #%d is busy (2) ", ac97->num); |
1da177e4c
|
284 |
} |
a5f22156e
|
285 |
static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short reg) |
1da177e4c
|
286 |
{ |
a5f22156e
|
287 |
struct fm801 *chip = ac97->private_data; |
1da177e4c
|
288 289 290 291 |
/* * Wait until the codec interface is not ready.. */ |
02fd1a76b
|
292 293 294 295 |
if (!fm801_ac97_is_ready(chip, 100)) { dev_err(chip->card->dev, "AC'97 interface is busy (1) "); return 0; |
1da177e4c
|
296 |
} |
1da177e4c
|
297 |
|
1da177e4c
|
298 |
/* read command */ |
215dacc28
|
299 300 |
fm801_writew(chip, AC97_CMD, reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ); |
02fd1a76b
|
301 302 303 304 305 |
if (!fm801_ac97_is_ready(chip, 100)) { dev_err(chip->card->dev, "AC'97 interface #%d is busy (2) ", ac97->num); return 0; |
1da177e4c
|
306 |
} |
1da177e4c
|
307 |
|
02fd1a76b
|
308 309 310 311 312 |
if (!fm801_ac97_is_valid(chip, 1000)) { dev_err(chip->card->dev, "AC'97 interface #%d is not valid (2) ", ac97->num); return 0; |
1da177e4c
|
313 |
} |
1da177e4c
|
314 |
|
215dacc28
|
315 |
return fm801_readw(chip, AC97_DATA); |
1da177e4c
|
316 |
} |
d71a13f4c
|
317 |
static const unsigned int rates[] = { |
1da177e4c
|
318 319 320 321 |
5500, 8000, 9600, 11025, 16000, 19200, 22050, 32000, 38400, 44100, 48000 }; |
d71a13f4c
|
322 |
static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { |
1da177e4c
|
323 324 325 326 |
.count = ARRAY_SIZE(rates), .list = rates, .mask = 0, }; |
d71a13f4c
|
327 |
static const unsigned int channels[] = { |
1da177e4c
|
328 329 |
2, 4, 6 }; |
d71a13f4c
|
330 |
static const struct snd_pcm_hw_constraint_list hw_constraints_channels = { |
5e4968e24
|
331 |
.count = ARRAY_SIZE(channels), |
1da177e4c
|
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
.list = channels, .mask = 0, }; /* * Sample rate routines */ static unsigned short snd_fm801_rate_bits(unsigned int rate) { unsigned int idx; for (idx = 0; idx < ARRAY_SIZE(rates); idx++) if (rates[idx] == rate) return idx; snd_BUG(); return ARRAY_SIZE(rates) - 1; } /* * PCM part */ |
a5f22156e
|
354 |
static int snd_fm801_playback_trigger(struct snd_pcm_substream *substream, |
1da177e4c
|
355 356 |
int cmd) { |
a5f22156e
|
357 |
struct fm801 *chip = snd_pcm_substream_chip(substream); |
1da177e4c
|
358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: chip->ply_ctrl &= ~(FM801_BUF1_LAST | FM801_BUF2_LAST | FM801_PAUSE); chip->ply_ctrl |= FM801_START | FM801_IMMED_STOP; break; case SNDRV_PCM_TRIGGER_STOP: chip->ply_ctrl &= ~(FM801_START | FM801_PAUSE); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
b1e9ed26a
|
372 |
case SNDRV_PCM_TRIGGER_SUSPEND: |
1da177e4c
|
373 374 375 |
chip->ply_ctrl |= FM801_PAUSE; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
b1e9ed26a
|
376 |
case SNDRV_PCM_TRIGGER_RESUME: |
1da177e4c
|
377 378 379 380 381 382 383 |
chip->ply_ctrl &= ~FM801_PAUSE; break; default: spin_unlock(&chip->reg_lock); snd_BUG(); return -EINVAL; } |
215dacc28
|
384 |
fm801_writew(chip, PLY_CTRL, chip->ply_ctrl); |
1da177e4c
|
385 386 387 |
spin_unlock(&chip->reg_lock); return 0; } |
a5f22156e
|
388 |
static int snd_fm801_capture_trigger(struct snd_pcm_substream *substream, |
1da177e4c
|
389 390 |
int cmd) { |
a5f22156e
|
391 |
struct fm801 *chip = snd_pcm_substream_chip(substream); |
1da177e4c
|
392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: chip->cap_ctrl &= ~(FM801_BUF1_LAST | FM801_BUF2_LAST | FM801_PAUSE); chip->cap_ctrl |= FM801_START | FM801_IMMED_STOP; break; case SNDRV_PCM_TRIGGER_STOP: chip->cap_ctrl &= ~(FM801_START | FM801_PAUSE); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
b1e9ed26a
|
406 |
case SNDRV_PCM_TRIGGER_SUSPEND: |
1da177e4c
|
407 408 409 |
chip->cap_ctrl |= FM801_PAUSE; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
b1e9ed26a
|
410 |
case SNDRV_PCM_TRIGGER_RESUME: |
1da177e4c
|
411 412 413 414 415 416 417 |
chip->cap_ctrl &= ~FM801_PAUSE; break; default: spin_unlock(&chip->reg_lock); snd_BUG(); return -EINVAL; } |
215dacc28
|
418 |
fm801_writew(chip, CAP_CTRL, chip->cap_ctrl); |
1da177e4c
|
419 420 421 |
spin_unlock(&chip->reg_lock); return 0; } |
a5f22156e
|
422 423 |
static int snd_fm801_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) |
1da177e4c
|
424 425 426 |
{ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } |
a5f22156e
|
427 |
static int snd_fm801_hw_free(struct snd_pcm_substream *substream) |
1da177e4c
|
428 429 430 |
{ return snd_pcm_lib_free_pages(substream); } |
a5f22156e
|
431 |
static int snd_fm801_playback_prepare(struct snd_pcm_substream *substream) |
1da177e4c
|
432 |
{ |
a5f22156e
|
433 434 |
struct fm801 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; |
1da177e4c
|
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
chip->ply_size = snd_pcm_lib_buffer_bytes(substream); chip->ply_count = snd_pcm_lib_period_bytes(substream); spin_lock_irq(&chip->reg_lock); chip->ply_ctrl &= ~(FM801_START | FM801_16BIT | FM801_STEREO | FM801_RATE_MASK | FM801_CHANNELS_MASK); if (snd_pcm_format_width(runtime->format) == 16) chip->ply_ctrl |= FM801_16BIT; if (runtime->channels > 1) { chip->ply_ctrl |= FM801_STEREO; if (runtime->channels == 4) chip->ply_ctrl |= FM801_CHANNELS_4; else if (runtime->channels == 6) chip->ply_ctrl |= FM801_CHANNELS_6; } chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; chip->ply_buf = 0; |
215dacc28
|
453 454 |
fm801_writew(chip, PLY_CTRL, chip->ply_ctrl); fm801_writew(chip, PLY_COUNT, chip->ply_count - 1); |
1da177e4c
|
455 456 |
chip->ply_buffer = runtime->dma_addr; chip->ply_pos = 0; |
215dacc28
|
457 458 459 |
fm801_writel(chip, PLY_BUF1, chip->ply_buffer); fm801_writel(chip, PLY_BUF2, chip->ply_buffer + (chip->ply_count % chip->ply_size)); |
1da177e4c
|
460 461 462 |
spin_unlock_irq(&chip->reg_lock); return 0; } |
a5f22156e
|
463 |
static int snd_fm801_capture_prepare(struct snd_pcm_substream *substream) |
1da177e4c
|
464 |
{ |
a5f22156e
|
465 466 |
struct fm801 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; |
1da177e4c
|
467 468 469 470 471 472 473 474 475 476 477 478 |
chip->cap_size = snd_pcm_lib_buffer_bytes(substream); chip->cap_count = snd_pcm_lib_period_bytes(substream); spin_lock_irq(&chip->reg_lock); chip->cap_ctrl &= ~(FM801_START | FM801_16BIT | FM801_STEREO | FM801_RATE_MASK); if (snd_pcm_format_width(runtime->format) == 16) chip->cap_ctrl |= FM801_16BIT; if (runtime->channels > 1) chip->cap_ctrl |= FM801_STEREO; chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; chip->cap_buf = 0; |
215dacc28
|
479 480 |
fm801_writew(chip, CAP_CTRL, chip->cap_ctrl); fm801_writew(chip, CAP_COUNT, chip->cap_count - 1); |
1da177e4c
|
481 482 |
chip->cap_buffer = runtime->dma_addr; chip->cap_pos = 0; |
215dacc28
|
483 484 485 |
fm801_writel(chip, CAP_BUF1, chip->cap_buffer); fm801_writel(chip, CAP_BUF2, chip->cap_buffer + (chip->cap_count % chip->cap_size)); |
1da177e4c
|
486 487 488 |
spin_unlock_irq(&chip->reg_lock); return 0; } |
a5f22156e
|
489 |
static snd_pcm_uframes_t snd_fm801_playback_pointer(struct snd_pcm_substream *substream) |
1da177e4c
|
490 |
{ |
a5f22156e
|
491 |
struct fm801 *chip = snd_pcm_substream_chip(substream); |
1da177e4c
|
492 493 494 495 496 |
size_t ptr; if (!(chip->ply_ctrl & FM801_START)) return 0; spin_lock(&chip->reg_lock); |
215dacc28
|
497 498 |
ptr = chip->ply_pos + (chip->ply_count - 1) - fm801_readw(chip, PLY_COUNT); if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_PLAYBACK) { |
1da177e4c
|
499 500 501 502 503 504 |
ptr += chip->ply_count; ptr %= chip->ply_size; } spin_unlock(&chip->reg_lock); return bytes_to_frames(substream->runtime, ptr); } |
a5f22156e
|
505 |
static snd_pcm_uframes_t snd_fm801_capture_pointer(struct snd_pcm_substream *substream) |
1da177e4c
|
506 |
{ |
a5f22156e
|
507 |
struct fm801 *chip = snd_pcm_substream_chip(substream); |
1da177e4c
|
508 509 510 511 512 |
size_t ptr; if (!(chip->cap_ctrl & FM801_START)) return 0; spin_lock(&chip->reg_lock); |
215dacc28
|
513 514 |
ptr = chip->cap_pos + (chip->cap_count - 1) - fm801_readw(chip, CAP_COUNT); if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_CAPTURE) { |
1da177e4c
|
515 516 517 518 519 520 |
ptr += chip->cap_count; ptr %= chip->cap_size; } spin_unlock(&chip->reg_lock); return bytes_to_frames(substream->runtime, ptr); } |
7d12e780e
|
521 |
static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id) |
1da177e4c
|
522 |
{ |
a5f22156e
|
523 |
struct fm801 *chip = dev_id; |
1da177e4c
|
524 525 |
unsigned short status; unsigned int tmp; |
215dacc28
|
526 |
status = fm801_readw(chip, IRQ_STATUS); |
1da177e4c
|
527 528 529 530 |
status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME; if (! status) return IRQ_NONE; /* ack first */ |
215dacc28
|
531 |
fm801_writew(chip, IRQ_STATUS, status); |
1da177e4c
|
532 533 534 535 536 537 538 |
if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) { spin_lock(&chip->reg_lock); chip->ply_buf++; chip->ply_pos += chip->ply_count; chip->ply_pos %= chip->ply_size; tmp = chip->ply_pos + chip->ply_count; tmp %= chip->ply_size; |
215dacc28
|
539 540 541 542 |
if (chip->ply_buf & 1) fm801_writel(chip, PLY_BUF1, chip->ply_buffer + tmp); else fm801_writel(chip, PLY_BUF2, chip->ply_buffer + tmp); |
1da177e4c
|
543 544 545 546 547 548 549 550 551 552 |
spin_unlock(&chip->reg_lock); snd_pcm_period_elapsed(chip->playback_substream); } if (chip->pcm && (status & FM801_IRQ_CAPTURE) && chip->capture_substream) { spin_lock(&chip->reg_lock); chip->cap_buf++; chip->cap_pos += chip->cap_count; chip->cap_pos %= chip->cap_size; tmp = chip->cap_pos + chip->cap_count; tmp %= chip->cap_size; |
215dacc28
|
553 554 555 556 |
if (chip->cap_buf & 1) fm801_writel(chip, CAP_BUF1, chip->cap_buffer + tmp); else fm801_writel(chip, CAP_BUF2, chip->cap_buffer + tmp); |
1da177e4c
|
557 558 559 560 |
spin_unlock(&chip->reg_lock); snd_pcm_period_elapsed(chip->capture_substream); } if (chip->rmidi && (status & FM801_IRQ_MPU)) |
7d12e780e
|
561 |
snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); |
997c87dad
|
562 563 564 |
if (status & FM801_IRQ_VOLUME) { /* TODO */ } |
1da177e4c
|
565 566 567 |
return IRQ_HANDLED; } |
dee49895b
|
568 |
static const struct snd_pcm_hardware snd_fm801_playback = |
1da177e4c
|
569 570 571 |
{ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
b1e9ed26a
|
572 |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | |
1da177e4c
|
573 574 575 576 577 578 579 580 581 582 583 584 585 586 |
SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, .rate_min = 5500, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 1, .periods_max = 1024, .fifo_size = 0, }; |
dee49895b
|
587 |
static const struct snd_pcm_hardware snd_fm801_capture = |
1da177e4c
|
588 589 590 |
{ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
b1e9ed26a
|
591 |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | |
1da177e4c
|
592 593 594 595 596 597 598 599 600 601 602 603 604 605 |
SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, .rate_min = 5500, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 1, .periods_max = 1024, .fifo_size = 0, }; |
a5f22156e
|
606 |
static int snd_fm801_playback_open(struct snd_pcm_substream *substream) |
1da177e4c
|
607 |
{ |
a5f22156e
|
608 609 |
struct fm801 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; |
1da177e4c
|
610 611 612 613 |
int err; chip->playback_substream = substream; runtime->hw = snd_fm801_playback; |
a5f22156e
|
614 615 |
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); |
1da177e4c
|
616 617 |
if (chip->multichannel) { runtime->hw.channels_max = 6; |
a5f22156e
|
618 619 620 |
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); |
1da177e4c
|
621 622 623 624 625 |
} if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; return 0; } |
a5f22156e
|
626 |
static int snd_fm801_capture_open(struct snd_pcm_substream *substream) |
1da177e4c
|
627 |
{ |
a5f22156e
|
628 629 |
struct fm801 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; |
1da177e4c
|
630 631 632 633 |
int err; chip->capture_substream = substream; runtime->hw = snd_fm801_capture; |
a5f22156e
|
634 635 |
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); |
1da177e4c
|
636 637 638 639 |
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; return 0; } |
a5f22156e
|
640 |
static int snd_fm801_playback_close(struct snd_pcm_substream *substream) |
1da177e4c
|
641 |
{ |
a5f22156e
|
642 |
struct fm801 *chip = snd_pcm_substream_chip(substream); |
1da177e4c
|
643 644 645 646 |
chip->playback_substream = NULL; return 0; } |
a5f22156e
|
647 |
static int snd_fm801_capture_close(struct snd_pcm_substream *substream) |
1da177e4c
|
648 |
{ |
a5f22156e
|
649 |
struct fm801 *chip = snd_pcm_substream_chip(substream); |
1da177e4c
|
650 651 652 653 |
chip->capture_substream = NULL; return 0; } |
6769e988b
|
654 |
static const struct snd_pcm_ops snd_fm801_playback_ops = { |
1da177e4c
|
655 656 657 658 659 660 661 662 663 |
.open = snd_fm801_playback_open, .close = snd_fm801_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_fm801_hw_params, .hw_free = snd_fm801_hw_free, .prepare = snd_fm801_playback_prepare, .trigger = snd_fm801_playback_trigger, .pointer = snd_fm801_playback_pointer, }; |
6769e988b
|
664 |
static const struct snd_pcm_ops snd_fm801_capture_ops = { |
1da177e4c
|
665 666 667 668 669 670 671 672 673 |
.open = snd_fm801_capture_open, .close = snd_fm801_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_fm801_hw_params, .hw_free = snd_fm801_hw_free, .prepare = snd_fm801_capture_prepare, .trigger = snd_fm801_capture_trigger, .pointer = snd_fm801_capture_pointer, }; |
483337f90
|
674 |
static int snd_fm801_pcm(struct fm801 *chip, int device) |
1da177e4c
|
675 |
{ |
d3d33aaba
|
676 |
struct pci_dev *pdev = to_pci_dev(chip->dev); |
a5f22156e
|
677 |
struct snd_pcm *pcm; |
1da177e4c
|
678 |
int err; |
1da177e4c
|
679 680 681 682 683 684 685 |
if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_fm801_capture_ops); pcm->private_data = chip; |
1da177e4c
|
686 687 688 689 690 |
pcm->info_flags = 0; strcpy(pcm->name, "FM801"); chip->pcm = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, |
d3d33aaba
|
691 |
snd_dma_pci_data(pdev), |
1da177e4c
|
692 |
chip->multichannel ? 128*1024 : 64*1024, 128*1024); |
483337f90
|
693 |
return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, |
e36e3b86c
|
694 695 696 |
snd_pcm_alt_chmaps, chip->multichannel ? 6 : 2, 0, NULL); |
1da177e4c
|
697 698 699 700 701 |
} /* * TEA5757 radio */ |
fdb62b500
|
702 |
#ifdef CONFIG_SND_FM801_TEA575X_BOOL |
1da177e4c
|
703 |
|
938a1566b
|
704 705 706 |
/* GPIO to TEA575x maps */ struct snd_fm801_tea575x_gpio { u8 data, clk, wren, most; |
d7ba858a7
|
707 |
char *name; |
938a1566b
|
708 |
}; |
1da177e4c
|
709 |
|
938a1566b
|
710 |
static struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = { |
d7ba858a7
|
711 712 713 |
{ .data = 1, .clk = 3, .wren = 2, .most = 0, .name = "SF256-PCS" }, { .data = 1, .clk = 0, .wren = 2, .most = 3, .name = "SF256-PCP" }, { .data = 2, .clk = 0, .wren = 1, .most = 3, .name = "SF64-PCR" }, |
938a1566b
|
714 |
}; |
1da177e4c
|
715 |
|
8e699d2cc
|
716 717 |
#define get_tea575x_gpio(chip) \ (&snd_fm801_tea575x_gpios[((chip)->tea575x_tuner & TUNER_TYPE_MASK) - 1]) |
938a1566b
|
718 |
static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) |
1da177e4c
|
719 |
{ |
a5f22156e
|
720 |
struct fm801 *chip = tea->private_data; |
215dacc28
|
721 |
unsigned short reg = fm801_readw(chip, GPIO_CTRL); |
8e699d2cc
|
722 |
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip); |
1da177e4c
|
723 |
|
938a1566b
|
724 725 726 |
reg &= ~(FM801_GPIO_GP(gpio.data) | FM801_GPIO_GP(gpio.clk) | FM801_GPIO_GP(gpio.wren)); |
1da177e4c
|
727 |
|
938a1566b
|
728 729 730 731 |
reg |= (pins & TEA575X_DATA) ? FM801_GPIO_GP(gpio.data) : 0; reg |= (pins & TEA575X_CLK) ? FM801_GPIO_GP(gpio.clk) : 0; /* WRITE_ENABLE is inverted */ reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren); |
1da177e4c
|
732 |
|
215dacc28
|
733 |
fm801_writew(chip, GPIO_CTRL, reg); |
1da177e4c
|
734 |
} |
938a1566b
|
735 |
static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea) |
1da177e4c
|
736 |
{ |
a5f22156e
|
737 |
struct fm801 *chip = tea->private_data; |
215dacc28
|
738 |
unsigned short reg = fm801_readw(chip, GPIO_CTRL); |
8e699d2cc
|
739 |
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip); |
effded75e
|
740 741 742 743 744 745 746 747 |
u8 ret; ret = 0; if (reg & FM801_GPIO_GP(gpio.data)) ret |= TEA575X_DATA; if (reg & FM801_GPIO_GP(gpio.most)) ret |= TEA575X_MOST; return ret; |
1da177e4c
|
748 |
} |
938a1566b
|
749 |
static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output) |
1da177e4c
|
750 |
{ |
a5f22156e
|
751 |
struct fm801 *chip = tea->private_data; |
215dacc28
|
752 |
unsigned short reg = fm801_readw(chip, GPIO_CTRL); |
8e699d2cc
|
753 |
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip); |
1da177e4c
|
754 |
|
1da177e4c
|
755 |
/* use GPIO lines and set write enable bit */ |
938a1566b
|
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 |
reg |= FM801_GPIO_GS(gpio.data) | FM801_GPIO_GS(gpio.wren) | FM801_GPIO_GS(gpio.clk) | FM801_GPIO_GS(gpio.most); if (output) { /* all of lines are in the write direction */ /* clear data and clock lines */ reg &= ~(FM801_GPIO_GD(gpio.data) | FM801_GPIO_GD(gpio.wren) | FM801_GPIO_GD(gpio.clk) | FM801_GPIO_GP(gpio.data) | FM801_GPIO_GP(gpio.clk) | FM801_GPIO_GP(gpio.wren)); } else { /* use GPIO lines, set data direction to input */ reg |= FM801_GPIO_GD(gpio.data) | FM801_GPIO_GD(gpio.most) | FM801_GPIO_GP(gpio.data) | FM801_GPIO_GP(gpio.most) | FM801_GPIO_GP(gpio.wren); /* all of lines are in the write direction, except data */ /* clear data, write enable and clock lines */ reg &= ~(FM801_GPIO_GD(gpio.wren) | FM801_GPIO_GD(gpio.clk) | FM801_GPIO_GP(gpio.clk)); |
1da177e4c
|
781 |
} |
215dacc28
|
782 |
fm801_writew(chip, GPIO_CTRL, reg); |
69252128e
|
783 |
} |
22dbec265
|
784 |
static const struct snd_tea575x_ops snd_fm801_tea_ops = { |
938a1566b
|
785 786 787 |
.set_pins = snd_fm801_tea575x_set_pins, .get_pins = snd_fm801_tea575x_get_pins, .set_direction = snd_fm801_tea575x_set_direction, |
1da177e4c
|
788 789 790 791 792 793 794 795 796 797 798 |
}; #endif /* * Mixer routines */ #define FM801_SINGLE(xname, reg, shift, mask, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_single, \ .get = snd_fm801_get_single, .put = snd_fm801_put_single, \ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } |
a5f22156e
|
799 800 |
static int snd_fm801_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
1da177e4c
|
801 802 803 804 805 806 807 808 809 |
{ int mask = (kcontrol->private_value >> 16) & 0xff; uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = mask; return 0; } |
a5f22156e
|
810 811 |
static int snd_fm801_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4c
|
812 |
{ |
a5f22156e
|
813 |
struct fm801 *chip = snd_kcontrol_chip(kcontrol); |
1da177e4c
|
814 815 816 817 |
int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; |
4b5c15f74
|
818 |
long *value = ucontrol->value.integer.value; |
1da177e4c
|
819 |
|
4b5c15f74
|
820 |
value[0] = (fm801_ioread16(chip, reg) >> shift) & mask; |
1da177e4c
|
821 |
if (invert) |
4b5c15f74
|
822 |
value[0] = mask - value[0]; |
1da177e4c
|
823 824 |
return 0; } |
a5f22156e
|
825 826 |
static int snd_fm801_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4c
|
827 |
{ |
a5f22156e
|
828 |
struct fm801 *chip = snd_kcontrol_chip(kcontrol); |
1da177e4c
|
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 |
int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; unsigned short val; val = (ucontrol->value.integer.value[0] & mask); if (invert) val = mask - val; return snd_fm801_update_bits(chip, reg, mask << shift, val << shift); } #define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \ .get = snd_fm801_get_double, .put = snd_fm801_put_double, \ .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } |
666c70ffd
|
845 846 847 848 849 850 851 |
#define FM801_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ .name = xname, .info = snd_fm801_info_double, \ .get = snd_fm801_get_double, .put = snd_fm801_put_double, \ .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \ .tlv = { .p = (xtlv) } } |
1da177e4c
|
852 |
|
a5f22156e
|
853 854 |
static int snd_fm801_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
1da177e4c
|
855 856 857 858 859 860 861 862 863 |
{ int mask = (kcontrol->private_value >> 16) & 0xff; uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = mask; return 0; } |
a5f22156e
|
864 865 |
static int snd_fm801_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4c
|
866 |
{ |
a5f22156e
|
867 |
struct fm801 *chip = snd_kcontrol_chip(kcontrol); |
1da177e4c
|
868 869 870 871 872 |
int reg = kcontrol->private_value & 0xff; int shift_left = (kcontrol->private_value >> 8) & 0x0f; int shift_right = (kcontrol->private_value >> 12) & 0x0f; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; |
4b5c15f74
|
873 |
long *value = ucontrol->value.integer.value; |
1da177e4c
|
874 875 |
spin_lock_irq(&chip->reg_lock); |
4b5c15f74
|
876 877 |
value[0] = (fm801_ioread16(chip, reg) >> shift_left) & mask; value[1] = (fm801_ioread16(chip, reg) >> shift_right) & mask; |
1da177e4c
|
878 879 |
spin_unlock_irq(&chip->reg_lock); if (invert) { |
4b5c15f74
|
880 881 |
value[0] = mask - value[0]; value[1] = mask - value[1]; |
1da177e4c
|
882 883 884 |
} return 0; } |
a5f22156e
|
885 886 |
static int snd_fm801_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4c
|
887 |
{ |
a5f22156e
|
888 |
struct fm801 *chip = snd_kcontrol_chip(kcontrol); |
1da177e4c
|
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 |
int reg = kcontrol->private_value & 0xff; int shift_left = (kcontrol->private_value >> 8) & 0x0f; int shift_right = (kcontrol->private_value >> 12) & 0x0f; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; unsigned short val1, val2; val1 = ucontrol->value.integer.value[0] & mask; val2 = ucontrol->value.integer.value[1] & mask; if (invert) { val1 = mask - val1; val2 = mask - val2; } return snd_fm801_update_bits(chip, reg, (mask << shift_left) | (mask << shift_right), (val1 << shift_left ) | (val2 << shift_right)); } |
a5f22156e
|
906 907 |
static int snd_fm801_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
1da177e4c
|
908 |
{ |
ca776a28a
|
909 |
static const char * const texts[5] = { |
1da177e4c
|
910 911 912 |
"AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary" }; |
ca776a28a
|
913 |
return snd_ctl_enum_info(uinfo, 1, 5, texts); |
1da177e4c
|
914 |
} |
a5f22156e
|
915 916 |
static int snd_fm801_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4c
|
917 |
{ |
a5f22156e
|
918 |
struct fm801 *chip = snd_kcontrol_chip(kcontrol); |
1da177e4c
|
919 920 |
unsigned short val; |
215dacc28
|
921 |
val = fm801_readw(chip, REC_SRC) & 7; |
1da177e4c
|
922 923 924 925 926 |
if (val > 4) val = 4; ucontrol->value.enumerated.item[0] = val; return 0; } |
a5f22156e
|
927 928 |
static int snd_fm801_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4c
|
929 |
{ |
a5f22156e
|
930 |
struct fm801 *chip = snd_kcontrol_chip(kcontrol); |
1da177e4c
|
931 932 933 934 935 936 |
unsigned short val; if ((val = ucontrol->value.enumerated.item[0]) > 4) return -EINVAL; return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val); } |
0cb29ea0d
|
937 |
static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0); |
666c70ffd
|
938 |
|
a5f22156e
|
939 |
#define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls) |
1da177e4c
|
940 |
|
e23e7a143
|
941 |
static struct snd_kcontrol_new snd_fm801_controls[] = { |
666c70ffd
|
942 943 |
FM801_DOUBLE_TLV("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1, db_scale_dsp), |
1da177e4c
|
944 |
FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1), |
666c70ffd
|
945 946 |
FM801_DOUBLE_TLV("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1, db_scale_dsp), |
1da177e4c
|
947 |
FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1), |
666c70ffd
|
948 949 |
FM801_DOUBLE_TLV("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1, db_scale_dsp), |
1da177e4c
|
950 951 952 953 954 955 956 957 958 |
FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Digital Capture Source", .info = snd_fm801_info_mux, .get = snd_fm801_get_mux, .put = snd_fm801_put_mux, } }; |
a5f22156e
|
959 |
#define FM801_CONTROLS_MULTI ARRAY_SIZE(snd_fm801_controls_multi) |
1da177e4c
|
960 |
|
e23e7a143
|
961 |
static struct snd_kcontrol_new snd_fm801_controls_multi[] = { |
1da177e4c
|
962 963 |
FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0), FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0), |
10e8d78a9
|
964 965 966 967 |
FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), FM801_I2S_MODE, 8, 1, 0), FM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",PLAYBACK,SWITCH), FM801_I2S_MODE, 9, 1, 0), FM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",CAPTURE,SWITCH), FM801_I2S_MODE, 10, 1, 0), FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), FM801_GEN_CTRL, 2, 1, 0), |
1da177e4c
|
968 |
}; |
a5f22156e
|
969 |
static void snd_fm801_mixer_free_ac97_bus(struct snd_ac97_bus *bus) |
1da177e4c
|
970 |
{ |
a5f22156e
|
971 |
struct fm801 *chip = bus->private_data; |
1da177e4c
|
972 973 |
chip->ac97_bus = NULL; } |
a5f22156e
|
974 |
static void snd_fm801_mixer_free_ac97(struct snd_ac97 *ac97) |
1da177e4c
|
975 |
{ |
a5f22156e
|
976 |
struct fm801 *chip = ac97->private_data; |
1da177e4c
|
977 978 979 980 981 982 |
if (ac97->num == 0) { chip->ac97 = NULL; } else { chip->ac97_sec = NULL; } } |
e23e7a143
|
983 |
static int snd_fm801_mixer(struct fm801 *chip) |
1da177e4c
|
984 |
{ |
a5f22156e
|
985 |
struct snd_ac97_template ac97; |
1da177e4c
|
986 987 |
unsigned int i; int err; |
a5f22156e
|
988 |
static struct snd_ac97_bus_ops ops = { |
1da177e4c
|
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 |
.write = snd_fm801_codec_write, .read = snd_fm801_codec_read, }; if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0) return err; chip->ac97_bus->private_free = snd_fm801_mixer_free_ac97_bus; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; ac97.private_free = snd_fm801_mixer_free_ac97; if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0) return err; if (chip->secondary) { ac97.num = 1; ac97.addr = chip->secondary_addr; if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec)) < 0) return err; } |
ef1ffbe78
|
1008 1009 1010 1011 1012 1013 |
for (i = 0; i < FM801_CONTROLS; i++) { err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip)); if (err < 0) return err; } |
1da177e4c
|
1014 |
if (chip->multichannel) { |
ef1ffbe78
|
1015 1016 1017 1018 1019 1020 |
for (i = 0; i < FM801_CONTROLS_MULTI; i++) { err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip)); if (err < 0) return err; } |
1da177e4c
|
1021 1022 1023 1024 1025 1026 1027 |
} return 0; } /* * initialization routines */ |
b1e9ed26a
|
1028 1029 1030 1031 |
static int wait_for_codec(struct fm801 *chip, unsigned int codec_id, unsigned short reg, unsigned long waits) { unsigned long timeout = jiffies + waits; |
215dacc28
|
1032 1033 |
fm801_writew(chip, AC97_CMD, reg | (codec_id << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ); |
b1e9ed26a
|
1034 1035 |
udelay(5); do { |
215dacc28
|
1036 1037 |
if ((fm801_readw(chip, AC97_CMD) & (FM801_AC97_VALID | FM801_AC97_BUSY)) == FM801_AC97_VALID) |
b1e9ed26a
|
1038 1039 1040 1041 1042 |
return 0; schedule_timeout_uninterruptible(1); } while (time_after(timeout, jiffies)); return -EIO; } |
b56fa687e
|
1043 |
static int reset_codec(struct fm801 *chip) |
b1e9ed26a
|
1044 |
{ |
b1e9ed26a
|
1045 |
/* codec cold reset + AC'97 warm reset */ |
215dacc28
|
1046 1047 |
fm801_writew(chip, CODEC_CTRL, (1 << 5) | (1 << 6)); fm801_readw(chip, CODEC_CTRL); /* flush posting data */ |
b1e9ed26a
|
1048 |
udelay(100); |
215dacc28
|
1049 |
fm801_writew(chip, CODEC_CTRL, 0); |
b1e9ed26a
|
1050 |
|
b56fa687e
|
1051 1052 1053 1054 1055 1056 |
return wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)); } static void snd_fm801_chip_multichannel_init(struct fm801 *chip) { unsigned short cmdw; |
b1e9ed26a
|
1057 1058 1059 1060 1061 1062 1063 1064 |
if (chip->multichannel) { if (chip->secondary_addr) { wait_for_codec(chip, chip->secondary_addr, AC97_VENDOR_ID1, msecs_to_jiffies(50)); } else { /* my card has the secondary codec */ /* at address #3, so the loop is inverted */ |
58e4334e8
|
1065 1066 1067 |
int i; for (i = 3; i > 0; i--) { if (!wait_for_codec(chip, i, AC97_VENDOR_ID1, |
b1e9ed26a
|
1068 |
msecs_to_jiffies(50))) { |
215dacc28
|
1069 |
cmdw = fm801_readw(chip, AC97_DATA); |
b1e9ed26a
|
1070 1071 |
if (cmdw != 0xffff && cmdw != 0) { chip->secondary = 1; |
58e4334e8
|
1072 |
chip->secondary_addr = i; |
b1e9ed26a
|
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 |
break; } } } } /* the recovery phase, it seems that probing for non-existing codec might */ /* cause timeout problems */ wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750)); } |
b56fa687e
|
1083 |
} |
b1e9ed26a
|
1084 |
|
b56fa687e
|
1085 1086 1087 |
static void snd_fm801_chip_init(struct fm801 *chip) { unsigned short cmdw; |
6bbe13ecb
|
1088 |
|
b1e9ed26a
|
1089 |
/* init volume */ |
215dacc28
|
1090 1091 1092 |
fm801_writew(chip, PCM_VOL, 0x0808); fm801_writew(chip, FM_VOL, 0x9f1f); fm801_writew(chip, I2S_VOL, 0x8808); |
b1e9ed26a
|
1093 1094 |
/* I2S control - I2S mode */ |
215dacc28
|
1095 |
fm801_writew(chip, I2S_MODE, 0x0003); |
b1e9ed26a
|
1096 |
|
6bbe13ecb
|
1097 |
/* interrupt setup */ |
215dacc28
|
1098 |
cmdw = fm801_readw(chip, IRQ_MASK); |
6bbe13ecb
|
1099 1100 1101 1102 |
if (chip->irq < 0) cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */ else cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */ |
215dacc28
|
1103 |
fm801_writew(chip, IRQ_MASK, cmdw); |
b1e9ed26a
|
1104 1105 |
/* interrupt clear */ |
215dacc28
|
1106 1107 |
fm801_writew(chip, IRQ_STATUS, FM801_IRQ_PLAYBACK | FM801_IRQ_CAPTURE | FM801_IRQ_MPU); |
b1e9ed26a
|
1108 |
} |
a5f22156e
|
1109 |
static int snd_fm801_free(struct fm801 *chip) |
1da177e4c
|
1110 1111 1112 1113 1114 1115 1116 |
{ unsigned short cmdw; if (chip->irq < 0) goto __end_hw; /* interrupt setup - mask everything */ |
215dacc28
|
1117 |
cmdw = fm801_readw(chip, IRQ_MASK); |
1da177e4c
|
1118 |
cmdw |= 0x00c3; |
215dacc28
|
1119 |
fm801_writew(chip, IRQ_MASK, cmdw); |
1da177e4c
|
1120 |
|
d3d33aaba
|
1121 |
devm_free_irq(chip->dev, chip->irq, chip); |
e97e98c63
|
1122 |
|
1da177e4c
|
1123 |
__end_hw: |
fdb62b500
|
1124 |
#ifdef CONFIG_SND_FM801_TEA575X_BOOL |
d4ecc83b7
|
1125 |
if (!(chip->tea575x_tuner & TUNER_DISABLED)) { |
c37279b92
|
1126 |
snd_tea575x_exit(&chip->tea); |
d4ecc83b7
|
1127 1128 |
v4l2_device_unregister(&chip->v4l2_dev); } |
1da177e4c
|
1129 |
#endif |
1da177e4c
|
1130 1131 |
return 0; } |
a5f22156e
|
1132 |
static int snd_fm801_dev_free(struct snd_device *device) |
1da177e4c
|
1133 |
{ |
a5f22156e
|
1134 |
struct fm801 *chip = device->device_data; |
1da177e4c
|
1135 1136 |
return snd_fm801_free(chip); } |
e23e7a143
|
1137 1138 1139 1140 1141 |
static int snd_fm801_create(struct snd_card *card, struct pci_dev *pci, int tea575x_tuner, int radio_nr, struct fm801 **rchip) |
1da177e4c
|
1142 |
{ |
a5f22156e
|
1143 |
struct fm801 *chip; |
1da177e4c
|
1144 |
int err; |
a5f22156e
|
1145 |
static struct snd_device_ops ops = { |
1da177e4c
|
1146 1147 1148 1149 |
.dev_free = snd_fm801_dev_free, }; *rchip = NULL; |
5618955c4
|
1150 |
if ((err = pcim_enable_device(pci)) < 0) |
1da177e4c
|
1151 |
return err; |
5618955c4
|
1152 1153 |
chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL); if (chip == NULL) |
1da177e4c
|
1154 |
return -ENOMEM; |
1da177e4c
|
1155 1156 |
spin_lock_init(&chip->reg_lock); chip->card = card; |
d3d33aaba
|
1157 |
chip->dev = &pci->dev; |
1da177e4c
|
1158 |
chip->irq = -1; |
6bbe13ecb
|
1159 |
chip->tea575x_tuner = tea575x_tuner; |
5618955c4
|
1160 |
if ((err = pci_request_regions(pci, "FM801")) < 0) |
1da177e4c
|
1161 |
return err; |
1da177e4c
|
1162 |
chip->port = pci_resource_start(pci, 0); |
b56fa687e
|
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 |
if (pci->revision >= 0xb1) /* FM801-AU */ chip->multichannel = 1; if (!(chip->tea575x_tuner & TUNER_ONLY)) { if (reset_codec(chip) < 0) { dev_info(chip->card->dev, "Primary AC'97 codec not found, assume SF64-PCR (tuner-only) "); chip->tea575x_tuner = 3 | TUNER_ONLY; } else { snd_fm801_chip_multichannel_init(chip); } } |
b56fa687e
|
1177 |
if ((chip->tea575x_tuner & TUNER_ONLY) == 0) { |
5618955c4
|
1178 1179 1180 1181 |
if (devm_request_irq(&pci->dev, pci->irq, snd_fm801_interrupt, IRQF_SHARED, KBUILD_MODNAME, chip)) { dev_err(card->dev, "unable to grab IRQ %d ", pci->irq); |
6bbe13ecb
|
1182 1183 1184 1185 1186 |
snd_fm801_free(chip); return -EBUSY; } chip->irq = pci->irq; pci_set_master(pci); |
1da177e4c
|
1187 |
} |
1da177e4c
|
1188 |
|
610e1ae9b
|
1189 |
snd_fm801_chip_init(chip); |
1da177e4c
|
1190 1191 1192 1193 |
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_fm801_free(chip); return err; } |
fdb62b500
|
1194 |
#ifdef CONFIG_SND_FM801_TEA575X_BOOL |
d4ecc83b7
|
1195 1196 1197 1198 1199 1200 1201 |
err = v4l2_device_register(&pci->dev, &chip->v4l2_dev); if (err < 0) { snd_fm801_free(chip); return err; } chip->tea.v4l2_dev = &chip->v4l2_dev; chip->tea.radio_nr = radio_nr; |
d7ba858a7
|
1202 1203 |
chip->tea.private_data = chip; chip->tea.ops = &snd_fm801_tea_ops; |
10ca72014
|
1204 |
sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci)); |
b56fa687e
|
1205 1206 |
if ((chip->tea575x_tuner & TUNER_TYPE_MASK) > 0 && (chip->tea575x_tuner & TUNER_TYPE_MASK) < 4) { |
5daf53a6e
|
1207 |
if (snd_tea575x_init(&chip->tea, THIS_MODULE)) { |
9c7f9abf6
|
1208 1209 |
dev_err(card->dev, "TEA575x radio not found "); |
d4ecc83b7
|
1210 |
snd_fm801_free(chip); |
967600155
|
1211 1212 |
return -ENODEV; } |
b56fa687e
|
1213 1214 |
} else if ((chip->tea575x_tuner & TUNER_TYPE_MASK) == 0) { unsigned int tuner_only = chip->tea575x_tuner & TUNER_ONLY; |
dbec6719a
|
1215 |
|
d7ba858a7
|
1216 1217 1218 |
/* autodetect tuner connection */ for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) { chip->tea575x_tuner = tea575x_tuner; |
5daf53a6e
|
1219 |
if (!snd_tea575x_init(&chip->tea, THIS_MODULE)) { |
9c7f9abf6
|
1220 1221 1222 |
dev_info(card->dev, "detected TEA575x radio type %s ", |
8e699d2cc
|
1223 |
get_tea575x_gpio(chip)->name); |
d7ba858a7
|
1224 1225 1226 |
break; } } |
967600155
|
1227 |
if (tea575x_tuner == 4) { |
9c7f9abf6
|
1228 1229 |
dev_err(card->dev, "TEA575x radio not found "); |
c37279b92
|
1230 |
chip->tea575x_tuner = TUNER_DISABLED; |
967600155
|
1231 |
} |
dbec6719a
|
1232 1233 |
chip->tea575x_tuner |= tuner_only; |
967600155
|
1234 |
} |
c37279b92
|
1235 |
if (!(chip->tea575x_tuner & TUNER_DISABLED)) { |
8e699d2cc
|
1236 |
strlcpy(chip->tea.card, get_tea575x_gpio(chip)->name, |
c37279b92
|
1237 1238 |
sizeof(chip->tea.card)); } |
1da177e4c
|
1239 1240 1241 1242 1243 |
#endif *rchip = chip; return 0; } |
e23e7a143
|
1244 1245 |
static int snd_card_fm801_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) |
1da177e4c
|
1246 1247 |
{ static int dev; |
a5f22156e
|
1248 1249 1250 |
struct snd_card *card; struct fm801 *chip; struct snd_opl3 *opl3; |
1da177e4c
|
1251 1252 1253 1254 1255 1256 1257 1258 |
int err; if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { dev++; return -ENOENT; } |
60c5772b5
|
1259 1260 |
err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); |
e58de7baf
|
1261 1262 |
if (err < 0) return err; |
d4ecc83b7
|
1263 |
if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], radio_nr[dev], &chip)) < 0) { |
1da177e4c
|
1264 1265 1266 |
snd_card_free(card); return err; } |
b1e9ed26a
|
1267 |
card->private_data = chip; |
1da177e4c
|
1268 1269 1270 1271 1272 1273 |
strcpy(card->driver, "FM801"); strcpy(card->shortname, "ForteMedia FM801-"); strcat(card->shortname, chip->multichannel ? "AU" : "AS"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->port, chip->irq); |
fb716c0b7
|
1274 |
if (chip->tea575x_tuner & TUNER_ONLY) |
e0a5d82a9
|
1275 |
goto __fm801_tuner_only; |
483337f90
|
1276 |
if ((err = snd_fm801_pcm(chip, 0)) < 0) { |
1da177e4c
|
1277 1278 1279 1280 1281 1282 1283 1284 |
snd_card_free(card); return err; } if ((err = snd_fm801_mixer(chip)) < 0) { snd_card_free(card); return err; } if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801, |
215dacc28
|
1285 |
chip->port + FM801_MPU401_DATA, |
dba8b4699
|
1286 1287 1288 |
MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK, -1, &chip->rmidi)) < 0) { |
1da177e4c
|
1289 1290 1291 |
snd_card_free(card); return err; } |
215dacc28
|
1292 1293 |
if ((err = snd_opl3_create(card, chip->port + FM801_OPL3_BANK0, chip->port + FM801_OPL3_BANK1, |
1da177e4c
|
1294 1295 1296 1297 1298 1299 1300 1301 |
OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) { snd_card_free(card); return err; } if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { snd_card_free(card); return err; } |
e0a5d82a9
|
1302 |
__fm801_tuner_only: |
1da177e4c
|
1303 1304 1305 1306 1307 1308 1309 1310 |
if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; } pci_set_drvdata(pci, card); dev++; return 0; } |
e23e7a143
|
1311 |
static void snd_card_fm801_remove(struct pci_dev *pci) |
1da177e4c
|
1312 1313 |
{ snd_card_free(pci_get_drvdata(pci)); |
1da177e4c
|
1314 |
} |
c7561cd80
|
1315 |
#ifdef CONFIG_PM_SLEEP |
b1e9ed26a
|
1316 1317 1318 1319 1320 1321 |
static unsigned char saved_regs[] = { FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC, FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2, FM801_CAP_CTRL, FM801_CAP_COUNT, FM801_CAP_BUF1, FM801_CAP_BUF2, FM801_CODEC_CTRL, FM801_I2S_MODE, FM801_VOLUME, FM801_GEN_CTRL, }; |
68cb2b559
|
1322 |
static int snd_fm801_suspend(struct device *dev) |
b1e9ed26a
|
1323 |
{ |
68cb2b559
|
1324 |
struct snd_card *card = dev_get_drvdata(dev); |
b1e9ed26a
|
1325 1326 1327 1328 |
struct fm801 *chip = card->private_data; int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); |
14da04b5f
|
1329 |
|
b1e9ed26a
|
1330 |
for (i = 0; i < ARRAY_SIZE(saved_regs); i++) |
37ba8fca7
|
1331 |
chip->saved_regs[i] = fm801_ioread16(chip, saved_regs[i]); |
14da04b5f
|
1332 1333 1334 |
if (chip->tea575x_tuner & TUNER_ONLY) { /* FIXME: tea575x suspend */ } else { |
14da04b5f
|
1335 1336 1337 |
snd_ac97_suspend(chip->ac97); snd_ac97_suspend(chip->ac97_sec); } |
b1e9ed26a
|
1338 1339 |
return 0; } |
68cb2b559
|
1340 |
static int snd_fm801_resume(struct device *dev) |
b1e9ed26a
|
1341 |
{ |
68cb2b559
|
1342 |
struct snd_card *card = dev_get_drvdata(dev); |
b1e9ed26a
|
1343 1344 |
struct fm801 *chip = card->private_data; int i; |
b56fa687e
|
1345 1346 1347 1348 1349 1350 |
if (chip->tea575x_tuner & TUNER_ONLY) { snd_fm801_chip_init(chip); } else { reset_codec(chip); snd_fm801_chip_multichannel_init(chip); snd_fm801_chip_init(chip); |
14da04b5f
|
1351 1352 |
snd_ac97_resume(chip->ac97); snd_ac97_resume(chip->ac97_sec); |
b56fa687e
|
1353 |
} |
14da04b5f
|
1354 |
|
b1e9ed26a
|
1355 |
for (i = 0; i < ARRAY_SIZE(saved_regs); i++) |
4b5c15f74
|
1356 |
fm801_iowrite16(chip, saved_regs[i], chip->saved_regs[i]); |
b1e9ed26a
|
1357 |
|
cb41f271d
|
1358 1359 1360 1361 |
#ifdef CONFIG_SND_FM801_TEA575X_BOOL if (!(chip->tea575x_tuner & TUNER_DISABLED)) snd_tea575x_set_freq(&chip->tea); #endif |
b1e9ed26a
|
1362 1363 1364 1365 |
snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } |
68cb2b559
|
1366 1367 1368 1369 1370 |
static SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume); #define SND_FM801_PM_OPS &snd_fm801_pm #else #define SND_FM801_PM_OPS NULL |
c7561cd80
|
1371 |
#endif /* CONFIG_PM_SLEEP */ |
b1e9ed26a
|
1372 |
|
e9f66d9b9
|
1373 |
static struct pci_driver fm801_driver = { |
3733e424c
|
1374 |
.name = KBUILD_MODNAME, |
1da177e4c
|
1375 1376 |
.id_table = snd_fm801_ids, .probe = snd_card_fm801_probe, |
e23e7a143
|
1377 |
.remove = snd_card_fm801_remove, |
68cb2b559
|
1378 1379 1380 |
.driver = { .pm = SND_FM801_PM_OPS, }, |
1da177e4c
|
1381 |
}; |
e9f66d9b9
|
1382 |
module_pci_driver(fm801_driver); |