File Index
Files and directories
Sign in
FILE

amd7930.c

File preview with syntax highlighting.

linux-6.19.14/sound/sparc/amd7930.c C
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Driver for AMD7930 sound chips found on Sparcs.
   4 * Copyright (C) 2002, 2008 David S. Miller <davem@davemloft.net>
   5 *
   6 * Based entirely upon drivers/sbus/audio/amd7930.c which is:
   7 * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu)
   8 *
   9 * --- Notes from Thomas's original driver ---
  10 * This is the lowlevel driver for the AMD7930 audio chip found on all
  11 * sun4c machines and some sun4m machines.
  12 *
  13 * The amd7930 is actually an ISDN chip which has a very simple
  14 * integrated audio encoder/decoder. When Sun decided on what chip to
  15 * use for audio, they had the brilliant idea of using the amd7930 and
  16 * only connecting the audio encoder/decoder pins.
  17 *
  18 * Thanks to the AMD engineer who was able to get us the AMD79C30
  19 * databook which has all the programming information and gain tables.
  20 *
  21 * Advanced Micro Devices' Am79C30A is an ISDN/audio chip used in the
  22 * SparcStation 1+.  The chip provides microphone and speaker interfaces
  23 * which provide mono-channel audio at 8K samples per second via either
  24 * 8-bit A-law or 8-bit mu-law encoding.  Also, the chip features an
  25 * ISDN BRI Line Interface Unit (LIU), I.430 S/T physical interface,
  26 * which performs basic D channel LAPD processing and provides raw
  27 * B channel data.  The digital audio channel, the two ISDN B channels,
  28 * and two 64 Kbps channels to the microprocessor are all interconnected
  29 * via a multiplexer.
  30 * --- End of notes from Thoamas's original driver ---
  31 */
  32
  33#include <linux/module.h>
  34#include <linux/kernel.h>
  35#include <linux/slab.h>
  36#include <linux/init.h>
  37#include <linux/interrupt.h>
  38#include <linux/moduleparam.h>
  39#include <linux/of.h>
  40#include <linux/platform_device.h>
  41#include <linux/io.h>
  42#include <linux/string.h>
  43
  44#include <sound/core.h>
  45#include <sound/pcm.h>
  46#include <sound/info.h>
  47#include <sound/control.h>
  48#include <sound/initval.h>
  49
  50#include <asm/irq.h>
  51
  52static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
  53static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
  54static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
  55
  56module_param_array(index, int, NULL, 0444);
  57MODULE_PARM_DESC(index, "Index value for Sun AMD7930 soundcard.");
  58module_param_array(id, charp, NULL, 0444);
  59MODULE_PARM_DESC(id, "ID string for Sun AMD7930 soundcard.");
  60module_param_array(enable, bool, NULL, 0444);
  61MODULE_PARM_DESC(enable, "Enable Sun AMD7930 soundcard.");
  62MODULE_AUTHOR("Thomas K. Dyas and David S. Miller");
  63MODULE_DESCRIPTION("Sun AMD7930");
  64MODULE_LICENSE("GPL");
  65
  66/* Device register layout.  */
  67
  68/* Register interface presented to the CPU by the amd7930. */
  69#define AMD7930_CR	0x00UL		/* Command Register (W) */
  70#define AMD7930_IR	AMD7930_CR	/* Interrupt Register (R) */
  71#define AMD7930_DR	0x01UL		/* Data Register (R/W) */
  72#define AMD7930_DSR1	0x02UL		/* D-channel Status Register 1 (R) */
  73#define AMD7930_DER	0x03UL		/* D-channel Error Register (R) */
  74#define AMD7930_DCTB	0x04UL		/* D-channel Transmit Buffer (W) */
  75#define AMD7930_DCRB	AMD7930_DCTB	/* D-channel Receive Buffer (R) */
  76#define AMD7930_BBTB	0x05UL		/* Bb-channel Transmit Buffer (W) */
  77#define AMD7930_BBRB	AMD7930_BBTB	/* Bb-channel Receive Buffer (R) */
  78#define AMD7930_BCTB	0x06UL		/* Bc-channel Transmit Buffer (W) */
  79#define AMD7930_BCRB	AMD7930_BCTB	/* Bc-channel Receive Buffer (R) */
  80#define AMD7930_DSR2	0x07UL		/* D-channel Status Register 2 (R) */
  81
  82/* Indirect registers in the Main Audio Processor. */
  83struct amd7930_map {
  84	__u16	x[8];
  85	__u16	r[8];
  86	__u16	gx;
  87	__u16	gr;
  88	__u16	ger;
  89	__u16	stgr;
  90	__u16	ftgr;
  91	__u16	atgr;
  92	__u8	mmr1;
  93	__u8	mmr2;
  94};
  95
  96/* After an amd7930 interrupt, reading the Interrupt Register (ir)
  97 * clears the interrupt and returns a bitmask indicating which
  98 * interrupt source(s) require service.
  99 */
 100
 101#define AMR_IR_DTTHRSH			0x01 /* D-channel xmit threshold */
 102#define AMR_IR_DRTHRSH			0x02 /* D-channel recv threshold */
 103#define AMR_IR_DSRI			0x04 /* D-channel packet status */
 104#define AMR_IR_DERI			0x08 /* D-channel error */
 105#define AMR_IR_BBUF			0x10 /* B-channel data xfer */
 106#define AMR_IR_LSRI			0x20 /* LIU status */
 107#define AMR_IR_DSR2I			0x40 /* D-channel buffer status */
 108#define AMR_IR_MLTFRMI			0x80 /* multiframe or PP */
 109
 110/* The amd7930 has "indirect registers" which are accessed by writing
 111 * the register number into the Command Register and then reading or
 112 * writing values from the Data Register as appropriate. We define the
 113 * AMR_* macros to be the indirect register numbers and AM_* macros to
 114 * be bits in whatever register is referred to.
 115 */
 116
 117/* Initialization */
 118#define	AMR_INIT			0x21
 119#define		AM_INIT_ACTIVE			0x01
 120#define		AM_INIT_DATAONLY		0x02
 121#define		AM_INIT_POWERDOWN		0x03
 122#define		AM_INIT_DISABLE_INTS		0x04
 123#define AMR_INIT2			0x20
 124#define		AM_INIT2_ENABLE_POWERDOWN	0x20
 125#define		AM_INIT2_ENABLE_MULTIFRAME	0x10
 126
 127/* Line Interface Unit */
 128#define	AMR_LIU_LSR			0xA1
 129#define		AM_LIU_LSR_STATE		0x07
 130#define		AM_LIU_LSR_F3			0x08
 131#define		AM_LIU_LSR_F7			0x10
 132#define		AM_LIU_LSR_F8			0x20
 133#define		AM_LIU_LSR_HSW			0x40
 134#define		AM_LIU_LSR_HSW_CHG		0x80
 135#define	AMR_LIU_LPR			0xA2
 136#define	AMR_LIU_LMR1			0xA3
 137#define		AM_LIU_LMR1_B1_ENABL		0x01
 138#define		AM_LIU_LMR1_B2_ENABL		0x02
 139#define		AM_LIU_LMR1_F_DISABL		0x04
 140#define		AM_LIU_LMR1_FA_DISABL		0x08
 141#define		AM_LIU_LMR1_REQ_ACTIV		0x10
 142#define		AM_LIU_LMR1_F8_F3		0x20
 143#define		AM_LIU_LMR1_LIU_ENABL		0x40
 144#define	AMR_LIU_LMR2			0xA4
 145#define		AM_LIU_LMR2_DECHO		0x01
 146#define		AM_LIU_LMR2_DLOOP		0x02
 147#define		AM_LIU_LMR2_DBACKOFF		0x04
 148#define		AM_LIU_LMR2_EN_F3_INT		0x08
 149#define		AM_LIU_LMR2_EN_F8_INT		0x10
 150#define		AM_LIU_LMR2_EN_HSW_INT		0x20
 151#define		AM_LIU_LMR2_EN_F7_INT		0x40
 152#define	AMR_LIU_2_4			0xA5
 153#define	AMR_LIU_MF			0xA6
 154#define	AMR_LIU_MFSB			0xA7
 155#define	AMR_LIU_MFQB			0xA8
 156
 157/* Multiplexor */
 158#define	AMR_MUX_MCR1			0x41
 159#define	AMR_MUX_MCR2			0x42
 160#define	AMR_MUX_MCR3			0x43
 161#define		AM_MUX_CHANNEL_B1		0x01
 162#define		AM_MUX_CHANNEL_B2		0x02
 163#define		AM_MUX_CHANNEL_Ba		0x03
 164#define		AM_MUX_CHANNEL_Bb		0x04
 165#define		AM_MUX_CHANNEL_Bc		0x05
 166#define		AM_MUX_CHANNEL_Bd		0x06
 167#define		AM_MUX_CHANNEL_Be		0x07
 168#define		AM_MUX_CHANNEL_Bf		0x08
 169#define	AMR_MUX_MCR4			0x44
 170#define		AM_MUX_MCR4_ENABLE_INTS		0x08
 171#define		AM_MUX_MCR4_REVERSE_Bb		0x10
 172#define		AM_MUX_MCR4_REVERSE_Bc		0x20
 173#define	AMR_MUX_1_4			0x45
 174
 175/* Main Audio Processor */
 176#define	AMR_MAP_X			0x61
 177#define	AMR_MAP_R			0x62
 178#define	AMR_MAP_GX			0x63
 179#define	AMR_MAP_GR			0x64
 180#define	AMR_MAP_GER			0x65
 181#define	AMR_MAP_STGR			0x66
 182#define	AMR_MAP_FTGR_1_2		0x67
 183#define	AMR_MAP_ATGR_1_2		0x68
 184#define	AMR_MAP_MMR1			0x69
 185#define		AM_MAP_MMR1_ALAW		0x01
 186#define		AM_MAP_MMR1_GX			0x02
 187#define		AM_MAP_MMR1_GR			0x04
 188#define		AM_MAP_MMR1_GER			0x08
 189#define		AM_MAP_MMR1_X			0x10
 190#define		AM_MAP_MMR1_R			0x20
 191#define		AM_MAP_MMR1_STG			0x40
 192#define		AM_MAP_MMR1_LOOPBACK		0x80
 193#define	AMR_MAP_MMR2			0x6A
 194#define		AM_MAP_MMR2_AINB		0x01
 195#define		AM_MAP_MMR2_LS			0x02
 196#define		AM_MAP_MMR2_ENABLE_DTMF		0x04
 197#define		AM_MAP_MMR2_ENABLE_TONEGEN	0x08
 198#define		AM_MAP_MMR2_ENABLE_TONERING	0x10
 199#define		AM_MAP_MMR2_DISABLE_HIGHPASS	0x20
 200#define		AM_MAP_MMR2_DISABLE_AUTOZERO	0x40
 201#define	AMR_MAP_1_10			0x6B
 202#define	AMR_MAP_MMR3			0x6C
 203#define	AMR_MAP_STRA			0x6D
 204#define	AMR_MAP_STRF			0x6E
 205#define	AMR_MAP_PEAKX			0x70
 206#define	AMR_MAP_PEAKR			0x71
 207#define	AMR_MAP_15_16			0x72
 208
 209/* Data Link Controller */
 210#define	AMR_DLC_FRAR_1_2_3		0x81
 211#define	AMR_DLC_SRAR_1_2_3		0x82
 212#define	AMR_DLC_TAR			0x83
 213#define	AMR_DLC_DRLR			0x84
 214#define	AMR_DLC_DTCR			0x85
 215#define	AMR_DLC_DMR1			0x86
 216#define		AMR_DLC_DMR1_DTTHRSH_INT	0x01
 217#define		AMR_DLC_DMR1_DRTHRSH_INT	0x02
 218#define		AMR_DLC_DMR1_TAR_ENABL		0x04
 219#define		AMR_DLC_DMR1_EORP_INT		0x08
 220#define		AMR_DLC_DMR1_EN_ADDR1		0x10
 221#define		AMR_DLC_DMR1_EN_ADDR2		0x20
 222#define		AMR_DLC_DMR1_EN_ADDR3		0x40
 223#define		AMR_DLC_DMR1_EN_ADDR4		0x80
 224#define		AMR_DLC_DMR1_EN_ADDRS		0xf0
 225#define	AMR_DLC_DMR2			0x87
 226#define		AMR_DLC_DMR2_RABRT_INT		0x01
 227#define		AMR_DLC_DMR2_RESID_INT		0x02
 228#define		AMR_DLC_DMR2_COLL_INT		0x04
 229#define		AMR_DLC_DMR2_FCS_INT		0x08
 230#define		AMR_DLC_DMR2_OVFL_INT		0x10
 231#define		AMR_DLC_DMR2_UNFL_INT		0x20
 232#define		AMR_DLC_DMR2_OVRN_INT		0x40
 233#define		AMR_DLC_DMR2_UNRN_INT		0x80
 234#define	AMR_DLC_1_7			0x88
 235#define	AMR_DLC_DRCR			0x89
 236#define	AMR_DLC_RNGR1			0x8A
 237#define	AMR_DLC_RNGR2			0x8B
 238#define	AMR_DLC_FRAR4			0x8C
 239#define	AMR_DLC_SRAR4			0x8D
 240#define	AMR_DLC_DMR3			0x8E
 241#define		AMR_DLC_DMR3_VA_INT		0x01
 242#define		AMR_DLC_DMR3_EOTP_INT		0x02
 243#define		AMR_DLC_DMR3_LBRP_INT		0x04
 244#define		AMR_DLC_DMR3_RBA_INT		0x08
 245#define		AMR_DLC_DMR3_LBT_INT		0x10
 246#define		AMR_DLC_DMR3_TBE_INT		0x20
 247#define		AMR_DLC_DMR3_RPLOST_INT		0x40
 248#define		AMR_DLC_DMR3_KEEP_FCS		0x80
 249#define	AMR_DLC_DMR4			0x8F
 250#define		AMR_DLC_DMR4_RCV_1		0x00
 251#define		AMR_DLC_DMR4_RCV_2		0x01
 252#define		AMR_DLC_DMR4_RCV_4		0x02
 253#define		AMR_DLC_DMR4_RCV_8		0x03
 254#define		AMR_DLC_DMR4_RCV_16		0x01
 255#define		AMR_DLC_DMR4_RCV_24		0x02
 256#define		AMR_DLC_DMR4_RCV_30		0x03
 257#define		AMR_DLC_DMR4_XMT_1		0x00
 258#define		AMR_DLC_DMR4_XMT_2		0x04
 259#define		AMR_DLC_DMR4_XMT_4		0x08
 260#define		AMR_DLC_DMR4_XMT_8		0x0c
 261#define		AMR_DLC_DMR4_XMT_10		0x08
 262#define		AMR_DLC_DMR4_XMT_14		0x0c
 263#define		AMR_DLC_DMR4_IDLE_MARK		0x00
 264#define		AMR_DLC_DMR4_IDLE_FLAG		0x10
 265#define		AMR_DLC_DMR4_ADDR_BOTH		0x00
 266#define		AMR_DLC_DMR4_ADDR_1ST		0x20
 267#define		AMR_DLC_DMR4_ADDR_2ND		0xa0
 268#define		AMR_DLC_DMR4_CR_ENABLE		0x40
 269#define	AMR_DLC_12_15			0x90
 270#define	AMR_DLC_ASR			0x91
 271#define	AMR_DLC_EFCR			0x92
 272#define		AMR_DLC_EFCR_EXTEND_FIFO	0x01
 273#define		AMR_DLC_EFCR_SEC_PKT_INT	0x02
 274
 275#define AMR_DSR1_VADDR			0x01
 276#define AMR_DSR1_EORP			0x02
 277#define AMR_DSR1_PKT_IP			0x04
 278#define AMR_DSR1_DECHO_ON		0x08
 279#define AMR_DSR1_DLOOP_ON		0x10
 280#define AMR_DSR1_DBACK_OFF		0x20
 281#define AMR_DSR1_EOTP			0x40
 282#define AMR_DSR1_CXMT_ABRT		0x80
 283
 284#define AMR_DSR2_LBRP			0x01
 285#define AMR_DSR2_RBA			0x02
 286#define AMR_DSR2_RPLOST			0x04
 287#define AMR_DSR2_LAST_BYTE		0x08
 288#define AMR_DSR2_TBE			0x10
 289#define AMR_DSR2_MARK_IDLE		0x20
 290#define AMR_DSR2_FLAG_IDLE		0x40
 291#define AMR_DSR2_SECOND_PKT		0x80
 292
 293#define AMR_DER_RABRT			0x01
 294#define AMR_DER_RFRAME			0x02
 295#define AMR_DER_COLLISION		0x04
 296#define AMR_DER_FCS			0x08
 297#define AMR_DER_OVFL			0x10
 298#define AMR_DER_UNFL			0x20
 299#define AMR_DER_OVRN			0x40
 300#define AMR_DER_UNRN			0x80
 301
 302/* Peripheral Port */
 303#define	AMR_PP_PPCR1			0xC0
 304#define	AMR_PP_PPSR			0xC1
 305#define	AMR_PP_PPIER			0xC2
 306#define	AMR_PP_MTDR			0xC3
 307#define	AMR_PP_MRDR			0xC3
 308#define	AMR_PP_CITDR0			0xC4
 309#define	AMR_PP_CIRDR0			0xC4
 310#define	AMR_PP_CITDR1			0xC5
 311#define	AMR_PP_CIRDR1			0xC5
 312#define	AMR_PP_PPCR2			0xC8
 313#define	AMR_PP_PPCR3			0xC9
 314
 315struct snd_amd7930 {
 316	spinlock_t		lock;
 317	void __iomem		*regs;
 318	u32			flags;
 319#define AMD7930_FLAG_PLAYBACK	0x00000001
 320#define AMD7930_FLAG_CAPTURE	0x00000002
 321
 322	struct amd7930_map	map;
 323
 324	struct snd_card		*card;
 325	struct snd_pcm		*pcm;
 326	struct snd_pcm_substream	*playback_substream;
 327	struct snd_pcm_substream	*capture_substream;
 328
 329	/* Playback/Capture buffer state. */
 330	unsigned char		*p_orig, *p_cur;
 331	int			p_left;
 332	unsigned char		*c_orig, *c_cur;
 333	int			c_left;
 334
 335	int			rgain;
 336	int			pgain;
 337	int			mgain;
 338
 339	struct platform_device	*op;
 340	unsigned int		irq;
 341	struct snd_amd7930	*next;
 342};
 343
 344static struct snd_amd7930 *amd7930_list;
 345
 346/* Idle the AMD7930 chip.  The amd->lock is not held.  */
 347static __inline__ void amd7930_idle(struct snd_amd7930 *amd)
 348{
 349	guard(spinlock_irqsave)(&amd->lock);
 350	sbus_writeb(AMR_INIT, amd->regs + AMD7930_CR);
 351	sbus_writeb(0, amd->regs + AMD7930_DR);
 352}
 353
 354/* Enable chip interrupts.  The amd->lock is not held.  */
 355static __inline__ void amd7930_enable_ints(struct snd_amd7930 *amd)
 356{
 357	guard(spinlock_irqsave)(&amd->lock);
 358	sbus_writeb(AMR_INIT, amd->regs + AMD7930_CR);
 359	sbus_writeb(AM_INIT_ACTIVE, amd->regs + AMD7930_DR);
 360}
 361
 362/* Disable chip interrupts.  The amd->lock is not held.  */
 363static __inline__ void amd7930_disable_ints(struct snd_amd7930 *amd)
 364{
 365	guard(spinlock_irqsave)(&amd->lock);
 366	sbus_writeb(AMR_INIT, amd->regs + AMD7930_CR);
 367	sbus_writeb(AM_INIT_ACTIVE | AM_INIT_DISABLE_INTS, amd->regs + AMD7930_DR);
 368}
 369
 370/* Commit amd7930_map settings to the hardware.
 371 * The amd->lock is held and local interrupts are disabled.
 372 */
 373static void __amd7930_write_map(struct snd_amd7930 *amd)
 374{
 375	struct amd7930_map *map = &amd->map;
 376
 377	sbus_writeb(AMR_MAP_GX, amd->regs + AMD7930_CR);
 378	sbus_writeb(((map->gx >> 0) & 0xff), amd->regs + AMD7930_DR);
 379	sbus_writeb(((map->gx >> 8) & 0xff), amd->regs + AMD7930_DR);
 380
 381	sbus_writeb(AMR_MAP_GR, amd->regs + AMD7930_CR);
 382	sbus_writeb(((map->gr >> 0) & 0xff), amd->regs + AMD7930_DR);
 383	sbus_writeb(((map->gr >> 8) & 0xff), amd->regs + AMD7930_DR);
 384
 385	sbus_writeb(AMR_MAP_STGR, amd->regs + AMD7930_CR);
 386	sbus_writeb(((map->stgr >> 0) & 0xff), amd->regs + AMD7930_DR);
 387	sbus_writeb(((map->stgr >> 8) & 0xff), amd->regs + AMD7930_DR);
 388
 389	sbus_writeb(AMR_MAP_GER, amd->regs + AMD7930_CR);
 390	sbus_writeb(((map->ger >> 0) & 0xff), amd->regs + AMD7930_DR);
 391	sbus_writeb(((map->ger >> 8) & 0xff), amd->regs + AMD7930_DR);
 392
 393	sbus_writeb(AMR_MAP_MMR1, amd->regs + AMD7930_CR);
 394	sbus_writeb(map->mmr1, amd->regs + AMD7930_DR);
 395
 396	sbus_writeb(AMR_MAP_MMR2, amd->regs + AMD7930_CR);
 397	sbus_writeb(map->mmr2, amd->regs + AMD7930_DR);
 398}
 399
 400/* gx, gr & stg gains.  this table must contain 256 elements with
 401 * the 0th being "infinity" (the magic value 9008).  The remaining
 402 * elements match sun's gain curve (but with higher resolution):
 403 * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
 404 */
 405static __const__ __u16 gx_coeff[256] = {
 406	0x9008, 0x8b7c, 0x8b51, 0x8b45, 0x8b42, 0x8b3b, 0x8b36, 0x8b33,
 407	0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
 408	0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
 409	0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
 410	0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
 411	0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
 412	0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
 413	0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
 414	0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
 415	0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
 416	0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
 417	0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
 418	0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
 419	0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
 420	0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
 421	0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
 422	0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
 423	0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
 424	0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
 425	0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
 426	0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
 427	0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
 428	0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
 429	0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
 430	0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
 431	0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
 432	0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
 433	0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
 434	0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
 435	0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
 436	0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
 437	0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
 438};
 439
 440static __const__ __u16 ger_coeff[] = {
 441	0x431f, /* 5. dB */
 442	0x331f, /* 5.5 dB */
 443	0x40dd, /* 6. dB */
 444	0x11dd, /* 6.5 dB */
 445	0x440f, /* 7. dB */
 446	0x411f, /* 7.5 dB */
 447	0x311f, /* 8. dB */
 448	0x5520, /* 8.5 dB */
 449	0x10dd, /* 9. dB */
 450	0x4211, /* 9.5 dB */
 451	0x410f, /* 10. dB */
 452	0x111f, /* 10.5 dB */
 453	0x600b, /* 11. dB */
 454	0x00dd, /* 11.5 dB */
 455	0x4210, /* 12. dB */
 456	0x110f, /* 13. dB */
 457	0x7200, /* 14. dB */
 458	0x2110, /* 15. dB */
 459	0x2200, /* 15.9 dB */
 460	0x000b, /* 16.9 dB */
 461	0x000f  /* 18. dB */
 462};
 463
 464/* Update amd7930_map settings and program them into the hardware.
 465 * The amd->lock is held and local interrupts are disabled.
 466 */
 467static void __amd7930_update_map(struct snd_amd7930 *amd)
 468{
 469	struct amd7930_map *map = &amd->map;
 470	int level;
 471
 472	map->gx = gx_coeff[amd->rgain];
 473	map->stgr = gx_coeff[amd->mgain];
 474	level = (amd->pgain * (256 + ARRAY_SIZE(ger_coeff))) >> 8;
 475	if (level >= 256) {
 476		map->ger = ger_coeff[level - 256];
 477		map->gr = gx_coeff[255];
 478	} else {
 479		map->ger = ger_coeff[0];
 480		map->gr = gx_coeff[level];
 481	}
 482	__amd7930_write_map(amd);
 483}
 484
 485static irqreturn_t snd_amd7930_interrupt(int irq, void *dev_id)
 486{
 487	struct snd_amd7930 *amd = dev_id;
 488	unsigned int elapsed;
 489	u8 ir;
 490
 491	scoped_guard(spinlock, &amd->lock) {
 492		elapsed = 0;
 493
 494		ir = sbus_readb(amd->regs + AMD7930_IR);
 495		if (ir & AMR_IR_BBUF) {
 496			u8 byte;
 497
 498			if (amd->flags & AMD7930_FLAG_PLAYBACK) {
 499				if (amd->p_left > 0) {
 500					byte = *(amd->p_cur++);
 501					amd->p_left--;
 502					sbus_writeb(byte, amd->regs + AMD7930_BBTB);
 503					if (amd->p_left == 0)
 504						elapsed |= AMD7930_FLAG_PLAYBACK;
 505				} else
 506					sbus_writeb(0, amd->regs + AMD7930_BBTB);
 507			} else if (amd->flags & AMD7930_FLAG_CAPTURE) {
 508				byte = sbus_readb(amd->regs + AMD7930_BBRB);
 509				if (amd->c_left > 0) {
 510					*(amd->c_cur++) = byte;
 511					amd->c_left--;
 512					if (amd->c_left == 0)
 513						elapsed |= AMD7930_FLAG_CAPTURE;
 514				}
 515			}
 516		}
 517	}
 518
 519	if (elapsed & AMD7930_FLAG_PLAYBACK)
 520		snd_pcm_period_elapsed(amd->playback_substream);
 521	else
 522		snd_pcm_period_elapsed(amd->capture_substream);
 523
 524	return IRQ_HANDLED;
 525}
 526
 527static int snd_amd7930_trigger(struct snd_amd7930 *amd, unsigned int flag, int cmd)
 528{
 529	int result = 0;
 530
 531	guard(spinlock_irqsave)(&amd->lock);
 532	if (cmd == SNDRV_PCM_TRIGGER_START) {
 533		if (!(amd->flags & flag)) {
 534			amd->flags |= flag;
 535
 536			/* Enable B channel interrupts.  */
 537			sbus_writeb(AMR_MUX_MCR4, amd->regs + AMD7930_CR);
 538			sbus_writeb(AM_MUX_MCR4_ENABLE_INTS, amd->regs + AMD7930_DR);
 539		}
 540	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
 541		if (amd->flags & flag) {
 542			amd->flags &= ~flag;
 543
 544			/* Disable B channel interrupts.  */
 545			sbus_writeb(AMR_MUX_MCR4, amd->regs + AMD7930_CR);
 546			sbus_writeb(0, amd->regs + AMD7930_DR);
 547		}
 548	} else {
 549		result = -EINVAL;
 550	}
 551
 552	return result;
 553}
 554
 555static int snd_amd7930_playback_trigger(struct snd_pcm_substream *substream,
 556					int cmd)
 557{
 558	struct snd_amd7930 *amd = snd_pcm_substream_chip(substream);
 559	return snd_amd7930_trigger(amd, AMD7930_FLAG_PLAYBACK, cmd);
 560}
 561
 562static int snd_amd7930_capture_trigger(struct snd_pcm_substream *substream,
 563				       int cmd)
 564{
 565	struct snd_amd7930 *amd = snd_pcm_substream_chip(substream);
 566	return snd_amd7930_trigger(amd, AMD7930_FLAG_CAPTURE, cmd);
 567}
 568
 569static int snd_amd7930_playback_prepare(struct snd_pcm_substream *substream)
 570{
 571	struct snd_amd7930 *amd = snd_pcm_substream_chip(substream);
 572	struct snd_pcm_runtime *runtime = substream->runtime;
 573	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
 574	u8 new_mmr1;
 575
 576	guard(spinlock_irqsave)(&amd->lock);
 577
 578	amd->flags |= AMD7930_FLAG_PLAYBACK;
 579
 580	/* Setup the pseudo-dma transfer pointers.  */
 581	amd->p_orig = amd->p_cur = runtime->dma_area;
 582	amd->p_left = size;
 583
 584	/* Put the chip into the correct encoding format.  */
 585	new_mmr1 = amd->map.mmr1;
 586	if (runtime->format == SNDRV_PCM_FORMAT_A_LAW)
 587		new_mmr1 |= AM_MAP_MMR1_ALAW;
 588	else
 589		new_mmr1 &= ~AM_MAP_MMR1_ALAW;
 590	if (new_mmr1 != amd->map.mmr1) {
 591		amd->map.mmr1 = new_mmr1;
 592		__amd7930_update_map(amd);
 593	}
 594
 595	return 0;
 596}
 597
 598static int snd_amd7930_capture_prepare(struct snd_pcm_substream *substream)
 599{
 600	struct snd_amd7930 *amd = snd_pcm_substream_chip(substream);
 601	struct snd_pcm_runtime *runtime = substream->runtime;
 602	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
 603	u8 new_mmr1;
 604
 605	guard(spinlock_irqsave)(&amd->lock);
 606
 607	amd->flags |= AMD7930_FLAG_CAPTURE;
 608
 609	/* Setup the pseudo-dma transfer pointers.  */
 610	amd->c_orig = amd->c_cur = runtime->dma_area;
 611	amd->c_left = size;
 612
 613	/* Put the chip into the correct encoding format.  */
 614	new_mmr1 = amd->map.mmr1;
 615	if (runtime->format == SNDRV_PCM_FORMAT_A_LAW)
 616		new_mmr1 |= AM_MAP_MMR1_ALAW;
 617	else
 618		new_mmr1 &= ~AM_MAP_MMR1_ALAW;
 619	if (new_mmr1 != amd->map.mmr1) {
 620		amd->map.mmr1 = new_mmr1;
 621		__amd7930_update_map(amd);
 622	}
 623
 624	return 0;
 625}
 626
 627static snd_pcm_uframes_t snd_amd7930_playback_pointer(struct snd_pcm_substream *substream)
 628{
 629	struct snd_amd7930 *amd = snd_pcm_substream_chip(substream);
 630	size_t ptr;
 631
 632	if (!(amd->flags & AMD7930_FLAG_PLAYBACK))
 633		return 0;
 634	ptr = amd->p_cur - amd->p_orig;
 635	return bytes_to_frames(substream->runtime, ptr);
 636}
 637
 638static snd_pcm_uframes_t snd_amd7930_capture_pointer(struct snd_pcm_substream *substream)
 639{
 640	struct snd_amd7930 *amd = snd_pcm_substream_chip(substream);
 641	size_t ptr;
 642
 643	if (!(amd->flags & AMD7930_FLAG_CAPTURE))
 644		return 0;
 645
 646	ptr = amd->c_cur - amd->c_orig;
 647	return bytes_to_frames(substream->runtime, ptr);
 648}
 649
 650/* Playback and capture have identical properties.  */
 651static const struct snd_pcm_hardware snd_amd7930_pcm_hw =
 652{
 653	.info			= (SNDRV_PCM_INFO_MMAP |
 654				   SNDRV_PCM_INFO_MMAP_VALID |
 655				   SNDRV_PCM_INFO_INTERLEAVED |
 656				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
 657				   SNDRV_PCM_INFO_HALF_DUPLEX),
 658	.formats		= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
 659	.rates			= SNDRV_PCM_RATE_8000,
 660	.rate_min		= 8000,
 661	.rate_max		= 8000,
 662	.channels_min		= 1,
 663	.channels_max		= 1,
 664	.buffer_bytes_max	= (64*1024),
 665	.period_bytes_min	= 1,
 666	.period_bytes_max	= (64*1024),
 667	.periods_min		= 1,
 668	.periods_max		= 1024,
 669};
 670
 671static int snd_amd7930_playback_open(struct snd_pcm_substream *substream)
 672{
 673	struct snd_amd7930 *amd = snd_pcm_substream_chip(substream);
 674	struct snd_pcm_runtime *runtime = substream->runtime;
 675
 676	amd->playback_substream = substream;
 677	runtime->hw = snd_amd7930_pcm_hw;
 678	return 0;
 679}
 680
 681static int snd_amd7930_capture_open(struct snd_pcm_substream *substream)
 682{
 683	struct snd_amd7930 *amd = snd_pcm_substream_chip(substream);
 684	struct snd_pcm_runtime *runtime = substream->runtime;
 685
 686	amd->capture_substream = substream;
 687	runtime->hw = snd_amd7930_pcm_hw;
 688	return 0;
 689}
 690
 691static int snd_amd7930_playback_close(struct snd_pcm_substream *substream)
 692{
 693	struct snd_amd7930 *amd = snd_pcm_substream_chip(substream);
 694
 695	amd->playback_substream = NULL;
 696	return 0;
 697}
 698
 699static int snd_amd7930_capture_close(struct snd_pcm_substream *substream)
 700{
 701	struct snd_amd7930 *amd = snd_pcm_substream_chip(substream);
 702
 703	amd->capture_substream = NULL;
 704	return 0;
 705}
 706
 707static const struct snd_pcm_ops snd_amd7930_playback_ops = {
 708	.open		=	snd_amd7930_playback_open,
 709	.close		=	snd_amd7930_playback_close,
 710	.prepare	=	snd_amd7930_playback_prepare,
 711	.trigger	=	snd_amd7930_playback_trigger,
 712	.pointer	=	snd_amd7930_playback_pointer,
 713};
 714
 715static const struct snd_pcm_ops snd_amd7930_capture_ops = {
 716	.open		=	snd_amd7930_capture_open,
 717	.close		=	snd_amd7930_capture_close,
 718	.prepare	=	snd_amd7930_capture_prepare,
 719	.trigger	=	snd_amd7930_capture_trigger,
 720	.pointer	=	snd_amd7930_capture_pointer,
 721};
 722
 723static int snd_amd7930_pcm(struct snd_amd7930 *amd)
 724{
 725	struct snd_pcm *pcm;
 726	int err;
 727
 728	if ((err = snd_pcm_new(amd->card,
 729			       /* ID */             "sun_amd7930",
 730			       /* device */         0,
 731			       /* playback count */ 1,
 732			       /* capture count */  1, &pcm)) < 0)
 733		return err;
 734
 735	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_amd7930_playback_ops);
 736	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_amd7930_capture_ops);
 737
 738	pcm->private_data = amd;
 739	pcm->info_flags = 0;
 740	strscpy(pcm->name, amd->card->shortname);
 741	amd->pcm = pcm;
 742
 743	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
 744				       NULL, 64*1024, 64*1024);
 745
 746	return 0;
 747}
 748
 749#define VOLUME_MONITOR	0
 750#define VOLUME_CAPTURE	1
 751#define VOLUME_PLAYBACK	2
 752
 753static int snd_amd7930_info_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
 754{
 755	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 756	uinfo->count = 1;
 757	uinfo->value.integer.min = 0;
 758	uinfo->value.integer.max = 255;
 759
 760	return 0;
 761}
 762
 763static int snd_amd7930_get_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
 764{
 765	struct snd_amd7930 *amd = snd_kcontrol_chip(kctl);
 766	int type = kctl->private_value;
 767	int *swval;
 768
 769	switch (type) {
 770	case VOLUME_MONITOR:
 771		swval = &amd->mgain;
 772		break;
 773	case VOLUME_CAPTURE:
 774		swval = &amd->rgain;
 775		break;
 776	case VOLUME_PLAYBACK:
 777	default:
 778		swval = &amd->pgain;
 779		break;
 780	}
 781
 782	ucontrol->value.integer.value[0] = *swval;
 783
 784	return 0;
 785}
 786
 787static int snd_amd7930_put_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
 788{
 789	struct snd_amd7930 *amd = snd_kcontrol_chip(kctl);
 790	int type = kctl->private_value;
 791	int *swval, change;
 792
 793	switch (type) {
 794	case VOLUME_MONITOR:
 795		swval = &amd->mgain;
 796		break;
 797	case VOLUME_CAPTURE:
 798		swval = &amd->rgain;
 799		break;
 800	case VOLUME_PLAYBACK:
 801	default:
 802		swval = &amd->pgain;
 803		break;
 804	}
 805
 806	guard(spinlock_irqsave)(&amd->lock);
 807
 808	if (*swval != ucontrol->value.integer.value[0]) {
 809		*swval = ucontrol->value.integer.value[0] & 0xff;
 810		__amd7930_update_map(amd);
 811		change = 1;
 812	} else
 813		change = 0;
 814
 815	return change;
 816}
 817
 818static const struct snd_kcontrol_new amd7930_controls[] = {
 819	{
 820		.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
 821		.name		=	"Monitor Volume",
 822		.index		=	0,
 823		.info		=	snd_amd7930_info_volume,
 824		.get		=	snd_amd7930_get_volume,
 825		.put		=	snd_amd7930_put_volume,
 826		.private_value	=	VOLUME_MONITOR,
 827	},
 828	{
 829		.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
 830		.name		=	"Capture Volume",
 831		.index		=	0,
 832		.info		=	snd_amd7930_info_volume,
 833		.get		=	snd_amd7930_get_volume,
 834		.put		=	snd_amd7930_put_volume,
 835		.private_value	=	VOLUME_CAPTURE,
 836	},
 837	{
 838		.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
 839		.name		=	"Playback Volume",
 840		.index		=	0,
 841		.info		=	snd_amd7930_info_volume,
 842		.get		=	snd_amd7930_get_volume,
 843		.put		=	snd_amd7930_put_volume,
 844		.private_value	=	VOLUME_PLAYBACK,
 845	},
 846};
 847
 848static int snd_amd7930_mixer(struct snd_amd7930 *amd)
 849{
 850	struct snd_card *card;
 851	int idx, err;
 852
 853	if (snd_BUG_ON(!amd || !amd->card))
 854		return -EINVAL;
 855
 856	card = amd->card;
 857	strscpy(card->mixername, card->shortname);
 858
 859	for (idx = 0; idx < ARRAY_SIZE(amd7930_controls); idx++) {
 860		if ((err = snd_ctl_add(card,
 861				       snd_ctl_new1(&amd7930_controls[idx], amd))) < 0)
 862			return err;
 863	}
 864
 865	return 0;
 866}
 867
 868static int snd_amd7930_free(struct snd_amd7930 *amd)
 869{
 870	struct platform_device *op = amd->op;
 871
 872	amd7930_idle(amd);
 873
 874	if (amd->irq)
 875		free_irq(amd->irq, amd);
 876
 877	if (amd->regs)
 878		of_iounmap(&op->resource[0], amd->regs,
 879			   resource_size(&op->resource[0]));
 880
 881	kfree(amd);
 882
 883	return 0;
 884}
 885
 886static int snd_amd7930_dev_free(struct snd_device *device)
 887{
 888	struct snd_amd7930 *amd = device->device_data;
 889
 890	return snd_amd7930_free(amd);
 891}
 892
 893static const struct snd_device_ops snd_amd7930_dev_ops = {
 894	.dev_free	=	snd_amd7930_dev_free,
 895};
 896
 897static int snd_amd7930_create(struct snd_card *card,
 898			      struct platform_device *op,
 899			      int irq, int dev,
 900			      struct snd_amd7930 **ramd)
 901{
 902	struct snd_amd7930 *amd;
 903	int err;
 904
 905	*ramd = NULL;
 906	amd = kzalloc(sizeof(*amd), GFP_KERNEL);
 907	if (amd == NULL)
 908		return -ENOMEM;
 909
 910	spin_lock_init(&amd->lock);
 911	amd->card = card;
 912	amd->op = op;
 913
 914	amd->regs = of_ioremap(&op->resource[0], 0,
 915			       resource_size(&op->resource[0]), "amd7930");
 916	if (!amd->regs) {
 917		dev_err(card->dev,
 918			"amd7930-%d: Unable to map chip registers.\n", dev);
 919		kfree(amd);
 920		return -EIO;
 921	}
 922
 923	amd7930_idle(amd);
 924
 925	if (request_irq(irq, snd_amd7930_interrupt,
 926			IRQF_SHARED, "amd7930", amd)) {
 927		dev_err(card->dev, "amd7930-%d: Unable to grab IRQ %d\n",
 928			dev, irq);
 929		snd_amd7930_free(amd);
 930		return -EBUSY;
 931	}
 932	amd->irq = irq;
 933
 934	amd7930_enable_ints(amd);
 935
 936	scoped_guard(spinlock_irqsave, &amd->lock) {
 937		amd->rgain = 128;
 938		amd->pgain = 200;
 939		amd->mgain = 0;
 940
 941		memset(&amd->map, 0, sizeof(amd->map));
 942		amd->map.mmr1 = (AM_MAP_MMR1_GX | AM_MAP_MMR1_GER |
 943				 AM_MAP_MMR1_GR | AM_MAP_MMR1_STG);
 944		amd->map.mmr2 = (AM_MAP_MMR2_LS | AM_MAP_MMR2_AINB);
 945
 946		__amd7930_update_map(amd);
 947
 948		/* Always MUX audio (Ba) to channel Bb. */
 949		sbus_writeb(AMR_MUX_MCR1, amd->regs + AMD7930_CR);
 950		sbus_writeb(AM_MUX_CHANNEL_Ba | (AM_MUX_CHANNEL_Bb << 4),
 951			    amd->regs + AMD7930_DR);
 952	}
 953
 954	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
 955			     amd, &snd_amd7930_dev_ops);
 956	if (err < 0) {
 957		snd_amd7930_free(amd);
 958		return err;
 959	}
 960
 961	*ramd = amd;
 962	return 0;
 963}
 964
 965static int amd7930_sbus_probe(struct platform_device *op)
 966{
 967	struct resource *rp = &op->resource[0];
 968	static int dev_num;
 969	struct snd_card *card;
 970	struct snd_amd7930 *amd;
 971	int err, irq;
 972
 973	irq = op->archdata.irqs[0];
 974
 975	if (dev_num >= SNDRV_CARDS)
 976		return -ENODEV;
 977	if (!enable[dev_num]) {
 978		dev_num++;
 979		return -ENOENT;
 980	}
 981
 982	err = snd_card_new(&op->dev, index[dev_num], id[dev_num],
 983			   THIS_MODULE, 0, &card);
 984	if (err < 0)
 985		return err;
 986
 987	strscpy(card->driver, "AMD7930");
 988	strscpy(card->shortname, "Sun AMD7930");
 989	sprintf(card->longname, "%s at 0x%02lx:0x%08Lx, irq %d",
 990		card->shortname,
 991		rp->flags & 0xffL,
 992		(unsigned long long)rp->start,
 993		irq);
 994
 995	if ((err = snd_amd7930_create(card, op,
 996				      irq, dev_num, &amd)) < 0)
 997		goto out_err;
 998
 999	err = snd_amd7930_pcm(amd);
1000	if (err < 0)
1001		goto out_err;
1002
1003	err = snd_amd7930_mixer(amd);
1004	if (err < 0)
1005		goto out_err;
1006
1007	err = snd_card_register(card);
1008	if (err < 0)
1009		goto out_err;
1010
1011	amd->next = amd7930_list;
1012	amd7930_list = amd;
1013
1014	dev_num++;
1015
1016	return 0;
1017
1018out_err:
1019	snd_card_free(card);
1020	return err;
1021}
1022
1023static const struct of_device_id amd7930_match[] = {
1024	{
1025		.name = "audio",
1026	},
1027	{},
1028};
1029MODULE_DEVICE_TABLE(of, amd7930_match);
1030
1031static struct platform_driver amd7930_sbus_driver = {
1032	.driver = {
1033		.name = "audio",
1034		.of_match_table = amd7930_match,
1035	},
1036	.probe		= amd7930_sbus_probe,
1037};
1038
1039static int __init amd7930_init(void)
1040{
1041	return platform_driver_register(&amd7930_sbus_driver);
1042}
1043
1044static void __exit amd7930_exit(void)
1045{
1046	struct snd_amd7930 *p = amd7930_list;
1047
1048	while (p != NULL) {
1049		struct snd_amd7930 *next = p->next;
1050
1051		snd_card_free(p->card);
1052
1053		p = next;
1054	}
1055
1056	amd7930_list = NULL;
1057
1058	platform_driver_unregister(&amd7930_sbus_driver);
1059}
1060
1061module_init(amd7930_init);
1062module_exit(amd7930_exit);
~/ Read-only file preview.